145386Swpaul/* 245386Swpaul * Copyright 2002-2009, Axel D��rfler, axeld@pinc-software.de. 345386Swpaul * Distributed under the terms of the MIT License. 445386Swpaul * 545386Swpaul * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 645386Swpaul * Distributed under the terms of the NewOS License. 745386Swpaul */ 845386Swpaul 945386Swpaul/*! Virtual File System and File System Interface Layer */ 1045386Swpaul 1145386Swpaul#include "vfs.h" 1245386Swpaul 1345386Swpaul#include <new> 1445386Swpaul#include <stdlib.h> 1545386Swpaul#include <string.h> 1645386Swpaul 1745386Swpaul#include "fd.h" 1845386Swpaul#include "fssh_atomic.h" 1945386Swpaul#include "fssh_defs.h" 2045386Swpaul#include "fssh_dirent.h" 2145386Swpaul#include "fssh_errno.h" 2245386Swpaul#include "fssh_fcntl.h" 2345386Swpaul#include "fssh_fs_info.h" 2445386Swpaul#include "fssh_fs_volume.h" 2545386Swpaul#include "fssh_kernel_export.h" 2645386Swpaul#include "fssh_module.h" 2745386Swpaul#include "fssh_stat.h" 2845386Swpaul#include "fssh_stdio.h" 2945386Swpaul#include "fssh_string.h" 3045386Swpaul#include "fssh_uio.h" 3145386Swpaul#include "fssh_unistd.h" 3245386Swpaul#include "hash.h" 3345386Swpaul#include "KPath.h" 3445386Swpaul#include "posix_compatibility.h" 3545386Swpaul#include "syscalls.h" 3645386Swpaul 3745386Swpaul//#define TRACE_VFS 3845386Swpaul#ifdef TRACE_VFS 3945386Swpaul# define TRACE(x) fssh_dprintf x 4045386Swpaul# define FUNCTION(x) fssh_dprintf x 4145386Swpaul#else 4245386Swpaul# define TRACE(x) ; 4345386Swpaul# define FUNCTION(x) ; 4445386Swpaul#endif 4545386Swpaul 4645386Swpaul#define ADD_DEBUGGER_COMMANDS 4745386Swpaul 4845386Swpaul#define ASSERT_LOCKED_MUTEX(x) 4945386Swpaul#define ASSERT(x) 5045386Swpaul 5145386Swpaulnamespace FSShell { 5245386Swpaul 5345386Swpaul 5445386Swpaul#define HAS_FS_CALL(vnode, op) (vnode->ops->op != NULL) 5545386Swpaul#define HAS_FS_MOUNT_CALL(mount, op) (mount->volume->ops->op != NULL) 5645386Swpaul 5745386Swpaul#define FS_CALL(vnode, op, params...) \ 5845386Swpaul vnode->ops->op(vnode->mount->volume, vnode, params) 5945386Swpaul#define FS_CALL_NO_PARAMS(vnode, op) \ 6045386Swpaul vnode->ops->op(vnode->mount->volume, vnode) 6145386Swpaul#define FS_MOUNT_CALL(mount, op, params...) \ 6245386Swpaul mount->volume->ops->op(mount->volume, params) 6345386Swpaul#define FS_MOUNT_CALL_NO_PARAMS(mount, op) \ 6445386Swpaul mount->volume->ops->op(mount->volume) 6545386Swpaul 6645386Swpaul 6745386Swpaulconst static uint32_t kMaxUnusedVnodes = 16; 6845386Swpaul // This is the maximum number of unused vnodes that the system 6945386Swpaul // will keep around (weak limit, if there is enough memory left, 7045386Swpaul // they won't get flushed even when hitting that limit). 7145386Swpaul // It may be chosen with respect to the available memory or enhanced 7245386Swpaul // by some timestamp/frequency heurism. 7345386Swpaul 7445386Swpaulstruct vnode : fssh_fs_vnode { 7545386Swpaul struct vnode *next; 7645386Swpaul vm_cache_ref *cache; 7745386Swpaul fssh_mount_id device; 7845386Swpaul list_link mount_link; 79113038Sobrien list_link unused_link; 80113038Sobrien fssh_vnode_id id; 81113038Sobrien struct fs_mount *mount; 8298849Sken struct vnode *covered_by; 8398849Sken int32_t ref_count; 8445386Swpaul uint32_t type : 29; 8545386Swpaul // TODO: S_INDEX_DIR actually needs another bit. 8645386Swpaul // Better combine this field with the following ones. 8745386Swpaul uint32_t remove : 1; 8845386Swpaul uint32_t busy : 1; 8945386Swpaul uint32_t unpublished : 1; 9045386Swpaul struct file_descriptor *mandatory_locked_by; 9145386Swpaul}; 9298849Sken 9345386Swpaulstruct vnode_hash_key { 9445386Swpaul fssh_mount_id device; 9545386Swpaul fssh_vnode_id vnode; 9645386Swpaul}; 9745386Swpaul 9845386Swpaul/** \brief Structure to manage a mounted file system 9983115Sbrooks 10083115Sbrooks Note: The root_vnode and covers_vnode fields (what others?) are 10145386Swpaul initialized in fs_mount() and not changed afterwards. That is as soon 10245386Swpaul as the mount is mounted and it is made sure it won't be unmounted 10345386Swpaul (e.g. by holding a reference to a vnode of that mount) (read) access 10445386Swpaul to those fields is always safe, even without additional locking. Morever 10545386Swpaul while mounted the mount holds a reference to the covers_vnode, and thus 10645386Swpaul making the access path vnode->mount->covers_vnode->mount->... safe if a 10745386Swpaul reference to vnode is held (note that for the root mount covers_vnode 10845386Swpaul is NULL, though). 10945386Swpaul */ 11045386Swpaulstruct fs_mount { 11145386Swpaul struct fs_mount *next; 11249011Swpaul fssh_file_system_module_info *fs; 11349011Swpaul fssh_mount_id id; 11449011Swpaul fssh_fs_volume *volume; 11545386Swpaul void *cookie; 11698849Sken char *device_name; 11798849Sken char *fs_name; 11898849Sken fssh_recursive_lock rlock; // guards the vnodes list 11998849Sken struct vnode *root_vnode; 12098849Sken struct vnode *covers_vnode; 12198849Sken struct list vnodes; 12298849Sken bool unmounting; 12398849Sken bool owns_file_device; 12498849Sken}; 12598849Sken 12698849Skenstatic fssh_mutex sFileSystemsMutex; 12798849Sken 12898849Sken/** \brief Guards sMountsTable. 12998849Sken * 13098849Sken * The holder is allowed to read/write access the sMountsTable. 13198849Sken * Manipulation of the fs_mount structures themselves 13298849Sken * (and their destruction) requires different locks though. 13398849Sken */ 13498849Skenstatic fssh_mutex sMountMutex; 13598849Sken 13698849Sken/** \brief Guards mount/unmount operations. 13745386Swpaul * 13845386Swpaul * The fs_mount() and fs_unmount() hold the lock during their whole operation. 13945386Swpaul * That is locking the lock ensures that no FS is mounted/unmounted. In 14098849Sken * particular this means that 14145386Swpaul * - sMountsTable will not be modified, 14245386Swpaul * - the fields immutable after initialization of the fs_mount structures in 14345386Swpaul * sMountsTable will not be modified, 14445386Swpaul * - vnode::covered_by of any vnode in sVnodeTable will not be modified. 14558698Sjlemon * 14698849Sken * The thread trying to lock the lock must not hold sVnodeMutex or 14798849Sken * sMountMutex. 14898849Sken */ 14998849Skenstatic fssh_recursive_lock sMountOpLock; 15098849Sken 15198849Sken/** \brief Guards the vnode::covered_by field of any vnode 15298849Sken * 15345386Swpaul * The holder is allowed to read access the vnode::covered_by field of any 15498849Sken * vnode. Additionally holding sMountOpLock allows for write access. 15598849Sken * 15698849Sken * The thread trying to lock the must not hold sVnodeMutex. 15798849Sken */ 15898849Skenstatic fssh_mutex sVnodeCoveredByMutex; 15998849Sken 16098849Sken/** \brief Guards sVnodeTable. 16198849Sken * 16245386Swpaul * The holder is allowed to read/write access sVnodeTable and to 16345386Swpaul * to any unbusy vnode in that table, save 16445386Swpaul * to the immutable fields (device, id, private_node, mount) to which 16545386Swpaul * only read-only access is allowed, and to the field covered_by, which is 16645386Swpaul * guarded by sMountOpLock and sVnodeCoveredByMutex. 16745386Swpaul * 16863702Swpaul * The thread trying to lock the mutex must not hold sMountMutex. 16963699Swpaul * You must not have this mutex held when calling create_sem(), as this 17063702Swpaul * might call vfs_free_unused_vnodes(). 17145386Swpaul */ 17245386Swpaulstatic fssh_mutex sVnodeMutex; 17345386Swpaul 17464139Swpaul#define VNODE_HASH_TABLE_SIZE 1024 17564139Swpaulstatic hash_table *sVnodeTable; 17664139Swpaulstatic list sUnusedVnodeList; 17745386Swpaulstatic uint32_t sUnusedVnodes = 0; 17845386Swpaulstatic struct vnode *sRoot; 17956206Swpaul 18056206Swpaul#define MOUNTS_HASH_TABLE_SIZE 16 18145386Swpaulstatic hash_table *sMountsTable; 18245386Swpaulstatic fssh_mount_id sNextMountID = 1; 18345386Swpaul 18498849Sken#define MAX_TEMP_IO_VECS 8 18598849Sken 18698849Skenfssh_mode_t __fssh_gUmask = 022; 18798849Sken 18898849Sken/* function declarations */ 18998849Sken 19098849Sken// file descriptor operation prototypes 191111815Sphkstatic fssh_status_t file_read(struct file_descriptor *, fssh_off_t pos, 192111815Sphk void *buffer, fssh_size_t *); 193111815Sphkstatic fssh_status_t file_write(struct file_descriptor *, fssh_off_t pos, 194111815Sphk const void *buffer, fssh_size_t *); 195111815Sphkstatic fssh_off_t file_seek(struct file_descriptor *, fssh_off_t pos, 19698849Sken int seek_type); 19798849Skenstatic void file_free_fd(struct file_descriptor *); 19892739Salfredstatic fssh_status_t file_close(struct file_descriptor *); 19992739Salfredstatic fssh_status_t dir_read(struct file_descriptor *, 20092739Salfred struct fssh_dirent *buffer, fssh_size_t bufferSize, 20192739Salfred uint32_t *_count); 20292739Salfredstatic fssh_status_t dir_read(struct vnode *vnode, void *cookie, 20345386Swpaul struct fssh_dirent *buffer, fssh_size_t bufferSize, 20492739Salfred uint32_t *_count); 20592739Salfredstatic fssh_status_t dir_rewind(struct file_descriptor *); 20645386Swpaulstatic void dir_free_fd(struct file_descriptor *); 20792739Salfredstatic fssh_status_t dir_close(struct file_descriptor *); 20892739Salfredstatic fssh_status_t attr_dir_read(struct file_descriptor *, 20992739Salfred struct fssh_dirent *buffer, fssh_size_t bufferSize, 21092739Salfred uint32_t *_count); 21192739Salfredstatic fssh_status_t attr_dir_rewind(struct file_descriptor *); 21292739Salfredstatic void attr_dir_free_fd(struct file_descriptor *); 21392739Salfredstatic fssh_status_t attr_dir_close(struct file_descriptor *); 21492739Salfredstatic fssh_status_t attr_read(struct file_descriptor *, fssh_off_t pos, 21592739Salfred void *buffer, fssh_size_t *); 21692739Salfredstatic fssh_status_t attr_write(struct file_descriptor *, fssh_off_t pos, 21745386Swpaul const void *buffer, fssh_size_t *); 21892739Salfredstatic fssh_off_t attr_seek(struct file_descriptor *, fssh_off_t pos, 21992739Salfred int seek_type); 22092739Salfredstatic void attr_free_fd(struct file_descriptor *); 22145386Swpaulstatic fssh_status_t attr_close(struct file_descriptor *); 22292739Salfredstatic fssh_status_t attr_read_stat(struct file_descriptor *, 22392739Salfred struct fssh_stat *); 22492739Salfredstatic fssh_status_t attr_write_stat(struct file_descriptor *, 22545386Swpaul const struct fssh_stat *, int statMask); 22692739Salfredstatic fssh_status_t index_dir_read(struct file_descriptor *, 22792739Salfred struct fssh_dirent *buffer, fssh_size_t bufferSize, 22898849Sken uint32_t *_count); 22998849Skenstatic fssh_status_t index_dir_rewind(struct file_descriptor *); 23098849Skenstatic void index_dir_free_fd(struct file_descriptor *); 23198849Skenstatic fssh_status_t index_dir_close(struct file_descriptor *); 23298849Skenstatic fssh_status_t query_read(struct file_descriptor *, 23398849Sken struct fssh_dirent *buffer, fssh_size_t bufferSize, 23492739Salfred uint32_t *_count); 23592739Salfredstatic fssh_status_t query_rewind(struct file_descriptor *); 23692739Salfredstatic void query_free_fd(struct file_descriptor *); 23792739Salfredstatic fssh_status_t query_close(struct file_descriptor *); 23892739Salfred 23998849Skenstatic fssh_status_t common_ioctl(struct file_descriptor *, uint32_t, void *buf, 24092739Salfred fssh_size_t len); 24192739Salfredstatic fssh_status_t common_read_stat(struct file_descriptor *, 24299058Salfred struct fssh_stat *); 24398849Skenstatic fssh_status_t common_write_stat(struct file_descriptor *, 24492739Salfred const struct fssh_stat *, int statMask); 24592739Salfred 24692739Salfredstatic fssh_status_t vnode_path_to_vnode(struct vnode *vnode, char *path, 24792739Salfred bool traverseLeafLink, int count, struct vnode **_vnode, 24892739Salfred fssh_vnode_id *_parentID); 24992739Salfredstatic fssh_status_t dir_vnode_to_path(struct vnode *vnode, char *buffer, 25092739Salfred fssh_size_t bufferSize); 25192739Salfredstatic fssh_status_t fd_and_path_to_vnode(int fd, char *path, 25292739Salfred bool traverseLeafLink, struct vnode **_vnode, 25392739Salfred fssh_vnode_id *_parentID, bool kernel); 25492739Salfredstatic void inc_vnode_ref_count(struct vnode *vnode); 25545386Swpaulstatic fssh_status_t dec_vnode_ref_count(struct vnode *vnode, bool reenter); 25692739Salfredstatic inline void put_vnode(struct vnode *vnode); 25792739Salfred 25892739Salfredstatic struct fd_ops sFileOps = { 25945386Swpaul file_read, 26098849Sken file_write, 26199013Speter file_seek, 26299013Speter common_ioctl, 26398849Sken NULL, 26498849Sken NULL, 26549011Swpaul NULL, // read_dir() 26649011Swpaul NULL, // rewind_dir() 26749011Swpaul common_read_stat, 26849011Swpaul common_write_stat, 26949011Swpaul file_close, 27049011Swpaul file_free_fd 27149011Swpaul}; 27249011Swpaul 27349011Swpaulstatic struct fd_ops sDirectoryOps = { 27449011Swpaul NULL, // read() 27551455Swpaul NULL, // write() 27649011Swpaul NULL, // seek() 27749011Swpaul common_ioctl, 27849011Swpaul NULL, // select() 27949011Swpaul NULL, // deselect() 28049011Swpaul dir_read, 28149011Swpaul dir_rewind, 282113506Smdodd common_read_stat, 283113506Smdodd common_write_stat, 284113506Smdodd dir_close, 28549011Swpaul dir_free_fd 28698849Sken}; 28798849Sken 28898849Skenstatic struct fd_ops sAttributeDirectoryOps = { 28998849Sken NULL, // read() 29098849Sken NULL, // write() 29198849Sken NULL, // seek() 29298849Sken common_ioctl, 29398849Sken NULL, // select() 29498849Sken NULL, // deselect() 29598849Sken attr_dir_read, 29698849Sken attr_dir_rewind, 29798849Sken common_read_stat, 29898849Sken common_write_stat, 29998849Sken attr_dir_close, 30045386Swpaul attr_dir_free_fd 30145386Swpaul}; 30245386Swpaul 30345386Swpaulstatic struct fd_ops sAttributeOps = { 30445386Swpaul attr_read, 30545386Swpaul attr_write, 30645386Swpaul attr_seek, 30745386Swpaul common_ioctl, 30845386Swpaul NULL, // select() 30945386Swpaul NULL, // deselect() 31045386Swpaul NULL, // read_dir() 31145386Swpaul NULL, // rewind_dir() 31245386Swpaul attr_read_stat, 31345386Swpaul attr_write_stat, 31445386Swpaul attr_close, 31545386Swpaul attr_free_fd 31645386Swpaul}; 31745386Swpaul 31845386Swpaulstatic struct fd_ops sIndexDirectoryOps = { 31945386Swpaul NULL, // read() 32045386Swpaul NULL, // write() 32145386Swpaul NULL, // seek() 32245386Swpaul NULL, // ioctl() 32345386Swpaul NULL, // select() 32445386Swpaul NULL, // deselect() 32545386Swpaul index_dir_read, 32645386Swpaul index_dir_rewind, 32745386Swpaul NULL, // read_stat() 32845386Swpaul NULL, // write_stat() 32945386Swpaul index_dir_close, 33045386Swpaul index_dir_free_fd 33145386Swpaul}; 33245386Swpaul 33345386Swpaul#if 0 33445386Swpaulstatic struct fd_ops sIndexOps = { 33545386Swpaul NULL, // read() 33645386Swpaul NULL, // write() 33745386Swpaul NULL, // seek() 33845386Swpaul NULL, // ioctl() 33945386Swpaul NULL, // select() 34045386Swpaul NULL, // deselect() 34145386Swpaul NULL, // dir_read() 34245386Swpaul NULL, // dir_rewind() 34345386Swpaul index_read_stat, // read_stat() 34445386Swpaul NULL, // write_stat() 34545386Swpaul NULL, // dir_close() 34645386Swpaul NULL // free_fd() 34745386Swpaul}; 34845386Swpaul#endif 34945386Swpaul 35045386Swpaulstatic struct fd_ops sQueryOps = { 35145386Swpaul NULL, // read() 35245386Swpaul NULL, // write() 35345386Swpaul NULL, // seek() 35445386Swpaul NULL, // ioctl() 35545386Swpaul NULL, // select() 35645386Swpaul NULL, // deselect() 35745386Swpaul query_read, 35845386Swpaul query_rewind, 35945386Swpaul NULL, // read_stat() 36045386Swpaul NULL, // write_stat() 36145386Swpaul query_close, 36245386Swpaul query_free_fd 36345386Swpaul}; 36445386Swpaul 36545386Swpaul 36645386Swpaul// VNodePutter 36745386Swpaulclass VNodePutter { 36845386Swpaulpublic: 36945386Swpaul VNodePutter(struct vnode *vnode = NULL) : fVNode(vnode) {} 37045386Swpaul 37145386Swpaul ~VNodePutter() 37245386Swpaul { 37345386Swpaul Put(); 37445386Swpaul } 37545386Swpaul 37645386Swpaul void SetTo(struct vnode *vnode) 37745386Swpaul { 37845386Swpaul Put(); 37945386Swpaul fVNode = vnode; 38045386Swpaul } 38145386Swpaul 38245386Swpaul void Put() 38345386Swpaul { 38445386Swpaul if (fVNode) { 38545386Swpaul put_vnode(fVNode); 38645386Swpaul fVNode = NULL; 38745386Swpaul } 38845386Swpaul } 38945386Swpaul 39045386Swpaul struct vnode *Detach() 39145386Swpaul { 39245386Swpaul struct vnode *vnode = fVNode; 39345386Swpaul fVNode = NULL; 39445386Swpaul return vnode; 39545386Swpaul } 39645386Swpaul 39745386Swpaulprivate: 39845386Swpaul struct vnode *fVNode; 39945386Swpaul}; 40045386Swpaul 40145386Swpaul 40245386Swpaulstatic int 40345386Swpaulmount_compare(void *_m, const void *_key) 40445386Swpaul{ 40545386Swpaul struct fs_mount *mount = (fs_mount *)_m; 40645386Swpaul const fssh_mount_id *id = (fssh_mount_id *)_key; 40745386Swpaul 40845386Swpaul if (mount->id == *id) 40945386Swpaul return 0; 41045386Swpaul 41145386Swpaul return -1; 41245386Swpaul} 41345386Swpaul 41445386Swpaul 41545386Swpaulstatic uint32_t 41645386Swpaulmount_hash(void *_m, const void *_key, uint32_t range) 41745386Swpaul{ 41845386Swpaul struct fs_mount *mount = (fs_mount *)_m; 41945386Swpaul const fssh_mount_id *id = (fssh_mount_id *)_key; 42045386Swpaul 42145386Swpaul if (mount) 42245386Swpaul return mount->id % range; 423102336Salfred 424102336Salfred return (uint32_t)*id % range; 42545386Swpaul} 42645386Swpaul 42745386Swpaul 42845386Swpaul/** Finds the mounted device (the fs_mount structure) with the given ID. 42945386Swpaul * Note, you must hold the gMountMutex lock when you call this function. 43045386Swpaul */ 43145386Swpaul 43245386Swpaulstatic struct fs_mount * 43345386Swpaulfind_mount(fssh_mount_id id) 43445386Swpaul{ 43545386Swpaul ASSERT_LOCKED_MUTEX(&sMountMutex); 43645386Swpaul 43745386Swpaul return (fs_mount *)hash_lookup(sMountsTable, (void *)&id); 43845386Swpaul} 43945386Swpaul 44045386Swpaul 44145386Swpaulstatic fssh_status_t 44245386Swpaulget_mount(fssh_mount_id id, struct fs_mount **_mount) 44345386Swpaul{ 44445386Swpaul MutexLocker locker(&sMountMutex); 44545386Swpaul 44645386Swpaul struct fs_mount *mount = find_mount(id); 447102336Salfred if (mount == NULL) 448102336Salfred return FSSH_B_BAD_VALUE; 44945386Swpaul 45045386Swpaul if (mount->root_vnode == NULL) { 45145386Swpaul // might have been called during a mount operation in which 45245386Swpaul // case the root node may still be NULL 45345386Swpaul return FSSH_B_BUSY; 45445386Swpaul } 45545386Swpaul 45645386Swpaul inc_vnode_ref_count(mount->root_vnode); 45745386Swpaul *_mount = mount; 45849133Swpaul 45945386Swpaul return FSSH_B_OK; 46045386Swpaul} 46145386Swpaul 46245386Swpaul 46345386Swpaulstatic void 46445386Swpaulput_mount(struct fs_mount *mount) 46545386Swpaul{ 46645386Swpaul if (mount) 46745386Swpaul put_vnode(mount->root_vnode); 46845386Swpaul} 46945386Swpaul 47045386Swpaul 47145386Swpaulstatic fssh_status_t 47245386Swpaulput_file_system(fssh_file_system_module_info *fs) 47345386Swpaul{ 47445386Swpaul return fssh_put_module(fs->info.name); 47545386Swpaul} 47645386Swpaul 47745386Swpaul 47845386Swpaul/** Tries to open the specified file system module. 47945386Swpaul * Accepts a file system name of the form "bfs" or "file_systems/bfs/v1". 48045386Swpaul * Returns a pointer to file system module interface, or NULL if it 48145386Swpaul * could not open the module. 48298849Sken */ 48398849Sken 48498849Skenstatic fssh_file_system_module_info * 48598849Skenget_file_system(const char *fsName) 48698849Sken{ 48798849Sken char name[FSSH_B_FILE_NAME_LENGTH]; 48898849Sken if (fssh_strncmp(fsName, "file_systems/", fssh_strlen("file_systems/"))) { 48998849Sken // construct module name if we didn't get one 49098849Sken // (we currently support only one API) 49198849Sken fssh_snprintf(name, sizeof(name), "file_systems/%s/v1", fsName); 49298849Sken fsName = NULL; 49398849Sken } 49498849Sken 49598849Sken fssh_file_system_module_info *info; 49698849Sken if (fssh_get_module(fsName ? fsName : name, (fssh_module_info **)&info) != FSSH_B_OK) 49798849Sken return NULL; 49898849Sken 49998849Sken return info; 50098849Sken} 50198849Sken 50298849Sken 50398849Sken/** Accepts a file system name of the form "bfs" or "file_systems/bfs/v1" 50498849Sken * and returns a compatible fs_info.fsh_name name ("bfs" in both cases). 50598849Sken * The name is allocated for you, and you have to free() it when you're 50698849Sken * done with it. 50798849Sken * Returns NULL if the required memory is no available. 50898849Sken */ 50998849Sken 51098849Skenstatic char * 51198849Skenget_file_system_name(const char *fsName) 51298849Sken{ 51398849Sken const fssh_size_t length = fssh_strlen("file_systems/"); 51498849Sken 51598849Sken if (fssh_strncmp(fsName, "file_systems/", length)) { 51698849Sken // the name already seems to be the module's file name 51798849Sken return fssh_strdup(fsName); 51898849Sken } 51998849Sken 52098849Sken fsName += length; 52198849Sken const char *end = fssh_strchr(fsName, '/'); 52298849Sken if (end == NULL) { 52398849Sken // this doesn't seem to be a valid name, but well... 52498849Sken return fssh_strdup(fsName); 52598849Sken } 52698849Sken 52798849Sken // cut off the trailing /v1 52898849Sken 52998849Sken char *name = (char *)malloc(end + 1 - fsName); 53098849Sken if (name == NULL) 53198849Sken return NULL; 53298849Sken 53398849Sken fssh_strlcpy(name, fsName, end + 1 - fsName); 53498849Sken return name; 53598849Sken} 53698849Sken 53798849Sken 53898849Skenstatic int 53998849Skenvnode_compare(void *_vnode, const void *_key) 54098849Sken{ 54198849Sken struct vnode *vnode = (struct vnode *)_vnode; 54298849Sken const struct vnode_hash_key *key = (vnode_hash_key *)_key; 54398849Sken 54498849Sken if (vnode->device == key->device && vnode->id == key->vnode) 54598849Sken return 0; 54698849Sken 54798849Sken return -1; 54898849Sken} 54998849Sken 55098849Sken 55198849Skenstatic uint32_t 55298849Skenvnode_hash(void *_vnode, const void *_key, uint32_t range) 55398849Sken{ 55498849Sken struct vnode *vnode = (struct vnode *)_vnode; 55598849Sken const struct vnode_hash_key *key = (vnode_hash_key *)_key; 55698849Sken 55798849Sken#define VHASH(mountid, vnodeid) (((uint32_t)((vnodeid) >> 32) + (uint32_t)(vnodeid)) ^ (uint32_t)(mountid)) 55898849Sken 55998849Sken if (vnode != NULL) 56098849Sken return VHASH(vnode->device, vnode->id) % range; 56198849Sken 56298849Sken return VHASH(key->device, key->vnode) % range; 56398849Sken 56498849Sken#undef VHASH 56598849Sken} 56698849Sken 56798849Sken 56898849Skenstatic void 56998849Skenadd_vnode_to_mount_list(struct vnode *vnode, struct fs_mount *mount) 57098849Sken{ 57198849Sken fssh_recursive_lock_lock(&mount->rlock); 57298849Sken 57398849Sken list_add_link_to_head(&mount->vnodes, &vnode->mount_link); 57498849Sken 57598849Sken fssh_recursive_lock_unlock(&mount->rlock); 57698849Sken} 57798849Sken 57898849Sken 57998849Skenstatic void 58098849Skenremove_vnode_from_mount_list(struct vnode *vnode, struct fs_mount *mount) 58198849Sken{ 58298849Sken fssh_recursive_lock_lock(&mount->rlock); 58398849Sken 58498849Sken list_remove_link(&vnode->mount_link); 58598849Sken vnode->mount_link.next = vnode->mount_link.prev = NULL; 58698849Sken 58798849Sken fssh_recursive_lock_unlock(&mount->rlock); 58898849Sken} 58998849Sken 59098849Sken 59198849Skenstatic fssh_status_t 59298849Skencreate_new_vnode(struct vnode **_vnode, fssh_mount_id mountID, fssh_vnode_id vnodeID) 59398849Sken{ 59498849Sken FUNCTION(("create_new_vnode()\n")); 59598849Sken 59698849Sken struct vnode *vnode = (struct vnode *)malloc(sizeof(struct vnode)); 59798849Sken if (vnode == NULL) 59898849Sken return FSSH_B_NO_MEMORY; 59998849Sken 60098849Sken // initialize basic values 60198849Sken fssh_memset(vnode, 0, sizeof(struct vnode)); 60298849Sken vnode->device = mountID; 60398849Sken vnode->id = vnodeID; 60498849Sken 60598849Sken // add the vnode to the mount structure 60698849Sken fssh_mutex_lock(&sMountMutex); 60798849Sken vnode->mount = find_mount(mountID); 60898849Sken if (!vnode->mount || vnode->mount->unmounting) { 60998849Sken fssh_mutex_unlock(&sMountMutex); 61098849Sken free(vnode); 61198849Sken return FSSH_B_ENTRY_NOT_FOUND; 61298849Sken } 61398849Sken 61498849Sken hash_insert(sVnodeTable, vnode); 61598849Sken add_vnode_to_mount_list(vnode, vnode->mount); 61698849Sken 61798849Sken fssh_mutex_unlock(&sMountMutex); 61898849Sken 61998849Sken vnode->ref_count = 1; 62098849Sken *_vnode = vnode; 62198849Sken 62298849Sken return FSSH_B_OK; 62398849Sken} 62498849Sken 62598849Sken 62698849Sken/** Frees the vnode and all resources it has acquired, and removes 62798849Sken * it from the vnode hash as well as from its mount structure. 62898849Sken * Will also make sure that any cache modifications are written back. 62998849Sken */ 63098849Sken 63198849Skenstatic void 63298849Skenfree_vnode(struct vnode *vnode, bool reenter) 63398849Sken{ 63498849Sken ASSERT(vnode->ref_count == 0 && vnode->busy); 63598849Sken 63698849Sken // write back any changes in this vnode's cache -- but only 63798849Sken // if the vnode won't be deleted, in which case the changes 63898849Sken // will be discarded 63998849Sken 64098849Sken if (!vnode->remove && HAS_FS_CALL(vnode, fsync)) 64198849Sken FS_CALL_NO_PARAMS(vnode, fsync); 64298849Sken 64398849Sken if (!vnode->unpublished) { 64498849Sken if (vnode->remove) 64598849Sken FS_CALL(vnode, remove_vnode, reenter); 64698849Sken else 64798849Sken FS_CALL(vnode, put_vnode, reenter); 64898849Sken } 64998849Sken 65098849Sken // The file system has removed the resources of the vnode now, so we can 65198849Sken // make it available again (and remove the busy vnode from the hash) 65298849Sken fssh_mutex_lock(&sVnodeMutex); 65398849Sken hash_remove(sVnodeTable, vnode); 65498849Sken fssh_mutex_unlock(&sVnodeMutex); 65598849Sken 65698849Sken remove_vnode_from_mount_list(vnode, vnode->mount); 65798849Sken 65898849Sken free(vnode); 65998849Sken} 66098849Sken 66198849Sken 66298849Sken/** \brief Decrements the reference counter of the given vnode and deletes it, 66398849Sken * if the counter dropped to 0. 66498849Sken * 66598849Sken * The caller must, of course, own a reference to the vnode to call this 66698849Sken * function. 66798849Sken * The caller must not hold the sVnodeMutex or the sMountMutex. 66898849Sken * 66998849Sken * \param vnode the vnode. 67098849Sken * \param reenter \c true, if this function is called (indirectly) from within 67198849Sken * a file system. 67298849Sken * \return \c FSSH_B_OK, if everything went fine, an error code otherwise. 67398849Sken */ 67498849Sken 67598849Skenstatic fssh_status_t 67698849Skendec_vnode_ref_count(struct vnode *vnode, bool reenter) 67798849Sken{ 67898849Sken fssh_mutex_lock(&sVnodeMutex); 67998849Sken 68098849Sken int32_t oldRefCount = fssh_atomic_add(&vnode->ref_count, -1); 68198849Sken 68298849Sken TRACE(("dec_vnode_ref_count: vnode %p, ref now %ld\n", vnode, vnode->ref_count)); 68398849Sken 68498849Sken if (oldRefCount == 1) { 68598849Sken if (vnode->busy) 68698849Sken fssh_panic("dec_vnode_ref_count: called on busy vnode %p\n", vnode); 68798849Sken 68898849Sken bool freeNode = false; 68998849Sken 69098849Sken // Just insert the vnode into an unused list if we don't need 69198849Sken // to delete it 69298849Sken if (vnode->remove) { 69398849Sken vnode->busy = true; 69498849Sken freeNode = true; 69598849Sken } else { 69698849Sken list_add_item(&sUnusedVnodeList, vnode); 69798849Sken if (++sUnusedVnodes > kMaxUnusedVnodes) { 69898849Sken // there are too many unused vnodes so we free the oldest one 69998849Sken // ToDo: evaluate this mechanism 70098849Sken vnode = (struct vnode *)list_remove_head_item(&sUnusedVnodeList); 70198849Sken vnode->busy = true; 70298849Sken freeNode = true; 70398849Sken sUnusedVnodes--; 70498849Sken } 70598849Sken } 70698849Sken 70798849Sken fssh_mutex_unlock(&sVnodeMutex); 70898849Sken 70998849Sken if (freeNode) 71098849Sken free_vnode(vnode, reenter); 71198849Sken } else 71298849Sken fssh_mutex_unlock(&sVnodeMutex); 71398849Sken 71498849Sken return FSSH_B_OK; 71598849Sken} 71698849Sken 71798849Sken 71898849Sken/** \brief Increments the reference counter of the given vnode. 71998849Sken * 72098849Sken * The caller must either already have a reference to the vnode or hold 72198849Sken * the sVnodeMutex. 72298849Sken * 72398849Sken * \param vnode the vnode. 72498849Sken */ 72598849Sken 72698849Skenstatic void 72798849Skeninc_vnode_ref_count(struct vnode *vnode) 72898849Sken{ 72998849Sken fssh_atomic_add(&vnode->ref_count, 1); 73098849Sken TRACE(("inc_vnode_ref_count: vnode %p, ref now %ld\n", vnode, vnode->ref_count)); 73198849Sken} 73298849Sken 73398849Sken 73498849Sken/** \brief Looks up a vnode by mount and node ID in the sVnodeTable. 73598849Sken * 73698849Sken * The caller must hold the sVnodeMutex. 73798849Sken * 73898849Sken * \param mountID the mount ID. 73998849Sken * \param vnodeID the node ID. 74098849Sken * 74198849Sken * \return The vnode structure, if it was found in the hash table, \c NULL 74298849Sken * otherwise. 74398849Sken */ 74498849Sken 74598849Skenstatic struct vnode * 74698849Skenlookup_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID) 74798849Sken{ 74898849Sken struct vnode_hash_key key; 74998849Sken 75098849Sken key.device = mountID; 75198849Sken key.vnode = vnodeID; 75298849Sken 75398849Sken return (vnode *)hash_lookup(sVnodeTable, &key); 75498849Sken} 75598849Sken 75698849Sken 75798849Sken/** \brief Retrieves a vnode for a given mount ID, node ID pair. 75898849Sken * 75998849Sken * If the node is not yet in memory, it will be loaded. 76098849Sken * 76198849Sken * The caller must not hold the sVnodeMutex or the sMountMutex. 76298849Sken * 76398849Sken * \param mountID the mount ID. 76498849Sken * \param vnodeID the node ID. 76598849Sken * \param _vnode Pointer to a vnode* variable into which the pointer to the 76698849Sken * retrieved vnode structure shall be written. 76798849Sken * \param reenter \c true, if this function is called (indirectly) from within 76898849Sken * a file system. 76998849Sken * \return \c FSSH_B_OK, if everything when fine, an error code otherwise. 77098849Sken */ 77198849Sken 77298849Skenstatic fssh_status_t 77398849Skenget_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, struct vnode **_vnode, int reenter) 77498849Sken{ 77598849Sken FUNCTION(("get_vnode: mountid %ld vnid 0x%Lx %p\n", mountID, vnodeID, _vnode)); 77698849Sken 77798849Sken fssh_mutex_lock(&sVnodeMutex); 77898849Sken 77998849Sken int32_t tries = 300; 78098849Sken // try for 3 secs 78198849Skenrestart: 78298849Sken struct vnode *vnode = lookup_vnode(mountID, vnodeID); 78398849Sken if (vnode && vnode->busy) { 78498849Sken fssh_mutex_unlock(&sVnodeMutex); 78598849Sken if (--tries < 0) { 78698849Sken // vnode doesn't seem to become unbusy 78798849Sken fssh_panic("vnode %d:%" FSSH_B_PRIdINO " is not becoming unbusy!\n", 78898849Sken (int)mountID, vnodeID); 789106627Sjhb return FSSH_B_BUSY; 79098849Sken } 79198849Sken fssh_snooze(10000); // 10 ms 79298849Sken fssh_mutex_lock(&sVnodeMutex); 79398849Sken goto restart; 79498849Sken } 79598849Sken 79698849Sken TRACE(("get_vnode: tried to lookup vnode, got %p\n", vnode)); 79798849Sken 79898849Sken fssh_status_t status; 79998849Sken 80098849Sken if (vnode) { 80198849Sken if (vnode->ref_count == 0) { 80298849Sken // this vnode has been unused before 80398849Sken list_remove_item(&sUnusedVnodeList, vnode); 80498849Sken sUnusedVnodes--; 80598849Sken } 80698849Sken inc_vnode_ref_count(vnode); 80798849Sken } else { 80898849Sken // we need to create a new vnode and read it in 80998849Sken status = create_new_vnode(&vnode, mountID, vnodeID); 81098849Sken if (status < FSSH_B_OK) 81198849Sken goto err; 81298849Sken 81398849Sken vnode->busy = true; 81445386Swpaul fssh_mutex_unlock(&sVnodeMutex); 81545386Swpaul 81645386Swpaul int type; 81745386Swpaul uint32_t flags; 81845386Swpaul status = FS_MOUNT_CALL(vnode->mount, get_vnode, vnodeID, vnode, &type, 819102336Salfred &flags, reenter); 820102336Salfred if (status == FSSH_B_OK && vnode->private_node == NULL) 82145386Swpaul status = FSSH_B_BAD_VALUE; 82245386Swpaul 82345386Swpaul fssh_mutex_lock(&sVnodeMutex); 82445386Swpaul 82545386Swpaul if (status < FSSH_B_OK) 82645386Swpaul goto err1; 82745386Swpaul 82845386Swpaul vnode->type = type; 82945386Swpaul vnode->busy = false; 83045386Swpaul } 83145386Swpaul 83245386Swpaul fssh_mutex_unlock(&sVnodeMutex); 83345386Swpaul 83445386Swpaul TRACE(("get_vnode: returning %p\n", vnode)); 83545386Swpaul 83645386Swpaul *_vnode = vnode; 83745386Swpaul return FSSH_B_OK; 83845386Swpaul 83945386Swpaulerr1: 84045386Swpaul hash_remove(sVnodeTable, vnode); 84145386Swpaul remove_vnode_from_mount_list(vnode, vnode->mount); 84245386Swpaulerr: 84345386Swpaul fssh_mutex_unlock(&sVnodeMutex); 84445386Swpaul if (vnode) 84545386Swpaul free(vnode); 84645386Swpaul 84745386Swpaul return status; 84845386Swpaul} 84945386Swpaul 85045386Swpaul 85145386Swpaul/** \brief Decrements the reference counter of the given vnode and deletes it, 85245386Swpaul * if the counter dropped to 0. 85345386Swpaul * 85445386Swpaul * The caller must, of course, own a reference to the vnode to call this 85545386Swpaul * function. 85645386Swpaul * The caller must not hold the sVnodeMutex or the sMountMutex. 85745386Swpaul * 85845386Swpaul * \param vnode the vnode. 85945386Swpaul */ 86045386Swpaul 86145386Swpaulstatic inline void 86245386Swpaulput_vnode(struct vnode *vnode) 86345386Swpaul{ 86445386Swpaul dec_vnode_ref_count(vnode, false); 86545386Swpaul} 86645386Swpaul 86745386Swpaul 86845386Swpaul/** Disconnects all file descriptors that are associated with the 86945386Swpaul * \a vnodeToDisconnect, or if this is NULL, all vnodes of the specified 87045386Swpaul * \a mount object. 87145386Swpaul * 87245386Swpaul * Note, after you've called this function, there might still be ongoing 87345386Swpaul * accesses - they won't be interrupted if they already happened before. 87445386Swpaul * However, any subsequent access will fail. 87545386Swpaul * 87645386Swpaul * This is not a cheap function and should be used with care and rarely. 87745386Swpaul * TODO: there is currently no means to stop a blocking read/write! 878102336Salfred */ 879102336Salfred 88045386Swpaulvoid 88145386Swpauldisconnect_mount_or_vnode_fds(struct fs_mount *mount, 88245386Swpaul struct vnode *vnodeToDisconnect) 88345386Swpaul{ 88445386Swpaul} 88545386Swpaul 88645386Swpaul 88745386Swpaul/** \brief Resolves a mount point vnode to the volume root vnode it is covered 88845386Swpaul * by. 88945386Swpaul * 89045386Swpaul * Given an arbitrary vnode, the function checks, whether the node is covered 89145386Swpaul * by the root of a volume. If it is the function obtains a reference to the 89245386Swpaul * volume root node and returns it. 89345386Swpaul * 89445386Swpaul * \param vnode The vnode in question. 89545386Swpaul * \return The volume root vnode the vnode cover is covered by, if it is 89645386Swpaul * indeed a mount point, or \c NULL otherwise. 89745386Swpaul */ 89845386Swpaul 89945386Swpaulstatic struct vnode * 90045386Swpaulresolve_mount_point_to_volume_root(struct vnode *vnode) 901102336Salfred{ 902102336Salfred if (!vnode) 90345386Swpaul return NULL; 90445386Swpaul 90545386Swpaul struct vnode *volumeRoot = NULL; 90645386Swpaul 90745386Swpaul fssh_mutex_lock(&sVnodeCoveredByMutex); 90845386Swpaul if (vnode->covered_by) { 90945386Swpaul volumeRoot = vnode->covered_by; 91045386Swpaul inc_vnode_ref_count(volumeRoot); 91145386Swpaul } 91245386Swpaul fssh_mutex_unlock(&sVnodeCoveredByMutex); 91345386Swpaul 91445386Swpaul return volumeRoot; 91545386Swpaul} 91645386Swpaul 91745386Swpaul 91845386Swpaul/** \brief Resolves a mount point vnode to the volume root vnode it is covered 91945386Swpaul * by. 92045386Swpaul * 92145386Swpaul * Given an arbitrary vnode (identified by mount and node ID), the function 92245386Swpaul * checks, whether the node is covered by the root of a volume. If it is the 92345386Swpaul * function returns the mount and node ID of the volume root node. Otherwise 92445386Swpaul * it simply returns the supplied mount and node ID. 92545386Swpaul * 92645386Swpaul * In case of error (e.g. the supplied node could not be found) the variables 92745386Swpaul * for storing the resolved mount and node ID remain untouched and an error 92845386Swpaul * code is returned. 92945386Swpaul * 93045386Swpaul * \param mountID The mount ID of the vnode in question. 931102336Salfred * \param nodeID The node ID of the vnode in question. 932102336Salfred * \param resolvedMountID Pointer to storage for the resolved mount ID. 93345386Swpaul * \param resolvedNodeID Pointer to storage for the resolved node ID. 93445386Swpaul * \return 93545386Swpaul * - \c FSSH_B_OK, if everything went fine, 93645386Swpaul * - another error code, if something went wrong. 93745386Swpaul */ 93845386Swpaul 93945386Swpaulfssh_status_t 94045386Swpaulresolve_mount_point_to_volume_root(fssh_mount_id mountID, fssh_vnode_id nodeID, 94145386Swpaul fssh_mount_id *resolvedMountID, fssh_vnode_id *resolvedNodeID) 94245386Swpaul{ 94345386Swpaul // get the node 94445386Swpaul struct vnode *node; 94545386Swpaul fssh_status_t error = get_vnode(mountID, nodeID, &node, false); 94645386Swpaul if (error != FSSH_B_OK) 94745386Swpaul return error; 94845386Swpaul 94945386Swpaul // resolve the node 95045386Swpaul struct vnode *resolvedNode = resolve_mount_point_to_volume_root(node); 95145386Swpaul if (resolvedNode) { 95245386Swpaul put_vnode(node); 95345386Swpaul node = resolvedNode; 95445386Swpaul } 95545386Swpaul 95645386Swpaul // set the return values 95745386Swpaul *resolvedMountID = node->device; 95845386Swpaul *resolvedNodeID = node->id; 95945386Swpaul 96045386Swpaul put_vnode(node); 96145386Swpaul 96245386Swpaul return FSSH_B_OK; 96345386Swpaul} 96445386Swpaul 96545386Swpaul 96645386Swpaul/** \brief Resolves a volume root vnode to the underlying mount point vnode. 96745386Swpaul * 96845386Swpaul * Given an arbitrary vnode, the function checks, whether the node is the 96945386Swpaul * root of a volume. If it is (and if it is not "/"), the function obtains 97045386Swpaul * a reference to the underlying mount point node and returns it. 97145386Swpaul * 97245386Swpaul * \param vnode The vnode in question (caller must have a reference). 97345386Swpaul * \return The mount point vnode the vnode covers, if it is indeed a volume 97445386Swpaul * root and not "/", or \c NULL otherwise. 97545386Swpaul */ 97645386Swpaul 97745386Swpaulstatic struct vnode * 97845386Swpaulresolve_volume_root_to_mount_point(struct vnode *vnode) 97945386Swpaul{ 98045386Swpaul if (!vnode) 98145386Swpaul return NULL; 98245386Swpaul 98398849Sken struct vnode *mountPoint = NULL; 98498849Sken 98545386Swpaul struct fs_mount *mount = vnode->mount; 98645386Swpaul if (vnode == mount->root_vnode && mount->covers_vnode) { 98745386Swpaul mountPoint = mount->covers_vnode; 98845386Swpaul inc_vnode_ref_count(mountPoint); 98945386Swpaul } 99045386Swpaul 99145386Swpaul return mountPoint; 99245386Swpaul} 99345386Swpaul 99445386Swpaul 99545386Swpaul/** \brief Gets the directory path and leaf name for a given path. 99645386Swpaul * 99745386Swpaul * The supplied \a path is transformed to refer to the directory part of 99845386Swpaul * the entry identified by the original path, and into the buffer \a filename 99945386Swpaul * the leaf name of the original entry is written. 100045386Swpaul * Neither the returned path nor the leaf name can be expected to be 100145386Swpaul * canonical. 100245386Swpaul * 100345386Swpaul * \param path The path to be analyzed. Must be able to store at least one 100445386Swpaul * additional character. 100545386Swpaul * \param filename The buffer into which the leaf name will be written. 100645386Swpaul * Must be of size FSSH_B_FILE_NAME_LENGTH at least. 100745386Swpaul * \return \c FSSH_B_OK, if everything went fine, \c FSSH_B_NAME_TOO_LONG, if the leaf 1008102336Salfred * name is longer than \c FSSH_B_FILE_NAME_LENGTH. 1009102336Salfred */ 101045386Swpaul 101145386Swpaulstatic fssh_status_t 101245386Swpaulget_dir_path_and_leaf(char *path, char *filename) 101345386Swpaul{ 101445386Swpaul char *p = fssh_strrchr(path, '/'); 101545386Swpaul // '/' are not allowed in file names! 101645386Swpaul 101745386Swpaul FUNCTION(("get_dir_path_and_leaf(path = %s)\n", path)); 101850548Sbde 101945386Swpaul if (!p) { 102045386Swpaul // this path is single segment with no '/' in it 102145386Swpaul // ex. "foo" 102245386Swpaul if (fssh_strlcpy(filename, path, FSSH_B_FILE_NAME_LENGTH) >= FSSH_B_FILE_NAME_LENGTH) 102345386Swpaul return FSSH_B_NAME_TOO_LONG; 102445386Swpaul fssh_strcpy(path, "."); 102545386Swpaul } else { 102645386Swpaul p++; 102745386Swpaul if (*p == '\0') { 102845386Swpaul // special case: the path ends in '/' 102945386Swpaul fssh_strcpy(filename, "."); 103067405Sbmilekic } else { 103145386Swpaul // normal leaf: replace the leaf portion of the path with a '.' 103245386Swpaul if (fssh_strlcpy(filename, p, FSSH_B_FILE_NAME_LENGTH) 103345386Swpaul >= FSSH_B_FILE_NAME_LENGTH) { 103467405Sbmilekic return FSSH_B_NAME_TOO_LONG; 103567405Sbmilekic } 103645386Swpaul } 103745386Swpaul p[0] = '.'; 103845386Swpaul p[1] = '\0'; 103962793Sgallatin } 104062793Sgallatin return FSSH_B_OK; 104145386Swpaul} 104245386Swpaul 104345386Swpaul 104445386Swpaulstatic fssh_status_t 104545386Swpaulentry_ref_to_vnode(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, struct vnode **_vnode) 104645386Swpaul{ 104745386Swpaul char clonedName[FSSH_B_FILE_NAME_LENGTH + 1]; 104845386Swpaul if (fssh_strlcpy(clonedName, name, FSSH_B_FILE_NAME_LENGTH) >= FSSH_B_FILE_NAME_LENGTH) 104945386Swpaul return FSSH_B_NAME_TOO_LONG; 105045386Swpaul 105145386Swpaul // get the directory vnode and let vnode_path_to_vnode() do the rest 105245386Swpaul struct vnode *directory; 105345386Swpaul 105445386Swpaul fssh_status_t status = get_vnode(mountID, directoryID, &directory, false); 105545386Swpaul if (status < 0) 105645386Swpaul return status; 105745386Swpaul 105845386Swpaul return vnode_path_to_vnode(directory, clonedName, false, 0, _vnode, NULL); 105945386Swpaul} 106045386Swpaul 106145386Swpaul 106245386Swpaulstatic fssh_status_t 106345386Swpaullookup_dir_entry(struct vnode* dir, const char* name, struct vnode** _vnode) 106445386Swpaul{ 106545386Swpaul fssh_ino_t id; 106645386Swpaul fssh_status_t status = FS_CALL(dir, lookup, name, &id); 106745386Swpaul if (status < FSSH_B_OK) 106845386Swpaul return status; 106945386Swpaul 107067405Sbmilekic fssh_mutex_lock(&sVnodeMutex); 107145386Swpaul *_vnode = lookup_vnode(dir->device, id); 107245386Swpaul fssh_mutex_unlock(&sVnodeMutex); 107345386Swpaul 107445386Swpaul if (*_vnode == NULL) { 107545386Swpaul fssh_panic("lookup_dir_entry(): could not lookup vnode (mountid %d " 1076102336Salfred "vnid %" FSSH_B_PRIdINO ")\n", (int)dir->device, id); 1077102336Salfred return FSSH_B_ENTRY_NOT_FOUND; 107899058Salfred } 107964837Sdwmalone 108045386Swpaul return FSSH_B_OK; 108145386Swpaul} 108245386Swpaul 108345386Swpaul 108445386Swpaul/*! Returns the vnode for the relative path starting at the specified \a vnode. 108545386Swpaul \a path must not be NULL. 108667405Sbmilekic If it returns successfully, \a path contains the name of the last path 108745386Swpaul component. This function clobbers the buffer pointed to by \a path only 108845386Swpaul if it does contain more than one component. 108967405Sbmilekic Note, this reduces the ref_count of the starting \a vnode, no matter if 109045386Swpaul it is successful or not! 109145386Swpaul*/ 109267405Sbmilekicstatic fssh_status_t 109345386Swpaulvnode_path_to_vnode(struct vnode *vnode, char *path, bool traverseLeafLink, 109445386Swpaul int count, struct vnode **_vnode, fssh_vnode_id *_parentID) 109545386Swpaul{ 109645386Swpaul fssh_status_t status = 0; 109745386Swpaul fssh_vnode_id lastParentID = vnode->id; 109864837Sdwmalone 109964837Sdwmalone FUNCTION(("vnode_path_to_vnode(vnode = %p, path = %s)\n", vnode, path)); 110064837Sdwmalone 110164837Sdwmalone if (path == NULL) { 110264837Sdwmalone put_vnode(vnode); 110364837Sdwmalone return FSSH_B_BAD_VALUE; 110464837Sdwmalone } 110545386Swpaul 110645386Swpaul while (true) { 110745386Swpaul struct vnode *nextVnode; 110898849Sken char *nextPath; 110945386Swpaul 111045386Swpaul TRACE(("vnode_path_to_vnode: top of loop. p = %p, p = '%s'\n", path, path)); 111145386Swpaul 111245386Swpaul // done? 1113102336Salfred if (path[0] == '\0') 1114102336Salfred break; 111545386Swpaul 111645386Swpaul // walk to find the next path component ("path" will point to a single 111745386Swpaul // path component), and filter out multiple slashes 111845386Swpaul for (nextPath = path + 1; *nextPath != '\0' && *nextPath != '/'; nextPath++); 111945386Swpaul 112045386Swpaul if (*nextPath == '/') { 112145386Swpaul *nextPath = '\0'; 112249036Swpaul do 1123111119Simp nextPath++; 112487846Sluigi while (*nextPath == '/'); 112545386Swpaul } 112645386Swpaul 1127111119Simp // See if the '..' is at the root of a mount and move to the covered 112845386Swpaul // vnode so we pass the '..' path to the underlying filesystem 112945386Swpaul if (!fssh_strcmp("..", path) 113045386Swpaul && vnode->mount->root_vnode == vnode 113145386Swpaul && vnode->mount->covers_vnode) { 113249036Swpaul nextVnode = vnode->mount->covers_vnode; 113349036Swpaul inc_vnode_ref_count(nextVnode); 113449036Swpaul put_vnode(vnode); 113549036Swpaul vnode = nextVnode; 113649036Swpaul } 113745386Swpaul 113845386Swpaul // Check if we have the right to search the current directory vnode. 113948597Swpaul // If a file system doesn't have the access() function, we assume that 114045386Swpaul // searching a directory is always allowed 114145386Swpaul if (HAS_FS_CALL(vnode, access)) 114245386Swpaul status = FS_CALL(vnode, access, FSSH_X_OK); 114345386Swpaul 114445386Swpaul // Tell the filesystem to get the vnode of this path component (if we got the 114558698Sjlemon // permission from the call above) 114658698Sjlemon if (status >= FSSH_B_OK) 114749036Swpaul status = lookup_dir_entry(vnode, path, &nextVnode); 114845386Swpaul 114945386Swpaul if (status < FSSH_B_OK) { 115045386Swpaul put_vnode(vnode); 115145386Swpaul return status; 115245386Swpaul } 115345386Swpaul 115445386Swpaul // If the new node is a symbolic link, resolve it (if we've been told to do it) 115545386Swpaul if (FSSH_S_ISLNK(vnode->type) 115645386Swpaul && !(!traverseLeafLink && nextPath[0] == '\0')) { 1157102336Salfred fssh_size_t bufferSize; 1158102336Salfred char *buffer; 115945386Swpaul 116045386Swpaul TRACE(("traverse link\n")); 116145386Swpaul 116245386Swpaul // it's not exactly nice style using goto in this way, but hey, it works :-/ 116345386Swpaul if (count + 1 > FSSH_B_MAX_SYMLINKS) { 116445386Swpaul status = FSSH_B_LINK_LIMIT; 116545386Swpaul goto resolve_link_error; 116649036Swpaul } 1167111119Simp 116845386Swpaul buffer = (char *)malloc(bufferSize = FSSH_B_PATH_NAME_LENGTH); 116945386Swpaul if (buffer == NULL) { 117045386Swpaul status = FSSH_B_NO_MEMORY; 117149036Swpaul goto resolve_link_error; 117249036Swpaul } 117349036Swpaul 117449036Swpaul if (HAS_FS_CALL(nextVnode, read_symlink)) { 117549036Swpaul status = FS_CALL(nextVnode, read_symlink, buffer, 117645386Swpaul &bufferSize); 117749036Swpaul } else 117848597Swpaul status = FSSH_B_BAD_VALUE; 117945386Swpaul 118045386Swpaul if (status < FSSH_B_OK) { 118145386Swpaul free(buffer); 118245386Swpaul 118345386Swpaul resolve_link_error: 118458698Sjlemon put_vnode(vnode); 118558698Sjlemon put_vnode(nextVnode); 118649036Swpaul 118745386Swpaul return status; 118845386Swpaul } 118945386Swpaul put_vnode(nextVnode); 119045386Swpaul 119145386Swpaul // Check if we start from the root directory or the current 119298849Sken // directory ("vnode" still points to that one). 119398849Sken // Cut off all leading slashes if it's the root directory 119445386Swpaul path = buffer; 119545386Swpaul if (path[0] == '/') { 119645386Swpaul // we don't need the old directory anymore 119745386Swpaul put_vnode(vnode); 1198102336Salfred 1199102336Salfred while (*++path == '/') 120045386Swpaul ; 120145386Swpaul vnode = sRoot; 120245386Swpaul inc_vnode_ref_count(vnode); 120345386Swpaul } 120445386Swpaul inc_vnode_ref_count(vnode); 120545386Swpaul // balance the next recursion - we will decrement the ref_count 120645386Swpaul // of the vnode, no matter if we succeeded or not 120749036Swpaul 120845386Swpaul status = vnode_path_to_vnode(vnode, path, traverseLeafLink, count + 1, 120945386Swpaul &nextVnode, &lastParentID); 121045386Swpaul 1211111119Simp free(buffer); 121245386Swpaul 121345386Swpaul if (status < FSSH_B_OK) { 121445386Swpaul put_vnode(vnode); 121545386Swpaul return status; 121645386Swpaul } 121745386Swpaul } else 121845386Swpaul lastParentID = vnode->id; 121945386Swpaul 122045386Swpaul // decrease the ref count on the old dir we just looked up into 122145386Swpaul put_vnode(vnode); 122245386Swpaul 122345386Swpaul path = nextPath; 122445386Swpaul vnode = nextVnode; 122545386Swpaul 122664837Sdwmalone // see if we hit a mount point 122764837Sdwmalone struct vnode *mountPoint = resolve_mount_point_to_volume_root(vnode); 122867405Sbmilekic if (mountPoint) { 122968621Sbmilekic put_vnode(vnode); 123049036Swpaul vnode = mountPoint; 123149036Swpaul } 123249036Swpaul } 123349036Swpaul 123445386Swpaul *_vnode = vnode; 123545386Swpaul if (_parentID) 123649780Swpaul *_parentID = lastParentID; 123745386Swpaul 123845386Swpaul return FSSH_B_OK; 123945386Swpaul} 124045386Swpaul 124145386Swpaul 124245386Swpaulstatic fssh_status_t 124358698Sjlemonpath_to_vnode(char *path, bool traverseLink, struct vnode **_vnode, 124458698Sjlemon fssh_vnode_id *_parentID, bool kernel) 124549036Swpaul{ 124645386Swpaul struct vnode *start = NULL; 124745386Swpaul 124845386Swpaul FUNCTION(("path_to_vnode(path = \"%s\")\n", path)); 124945386Swpaul 125045386Swpaul if (!path) 125198849Sken return FSSH_B_BAD_VALUE; 125298849Sken 125398849Sken // figure out if we need to start at root or at cwd 125498849Sken if (*path == '/') { 125598849Sken if (sRoot == NULL) { 125698849Sken // we're a bit early, aren't we? 125798849Sken return FSSH_B_ERROR; 125898849Sken } 125998849Sken 126098849Sken while (*++path == '/') 126198849Sken ; 126298849Sken start = sRoot; 1263104401Salfred inc_vnode_ref_count(start); 126498849Sken } else { 126598849Sken struct io_context *context = get_current_io_context(kernel); 126698849Sken 126798849Sken fssh_mutex_lock(&context->io_mutex); 126898849Sken start = context->cwd; 126998849Sken if (start != NULL) 127098849Sken inc_vnode_ref_count(start); 127198849Sken fssh_mutex_unlock(&context->io_mutex); 127298849Sken 127398849Sken if (start == NULL) 127498849Sken return FSSH_B_ERROR; 127598849Sken } 127698849Sken 127798849Sken return vnode_path_to_vnode(start, path, traverseLink, 0, _vnode, _parentID); 127898849Sken} 127998849Sken 128098849Sken 128198849Sken/** Returns the vnode in the next to last segment of the path, and returns 128298849Sken * the last portion in filename. 128398849Sken * The path buffer must be able to store at least one additional character. 128498849Sken */ 128598849Sken 128698849Skenstatic fssh_status_t 128798849Skenpath_to_dir_vnode(char *path, struct vnode **_vnode, char *filename, bool kernel) 128898849Sken{ 128998849Sken fssh_status_t status = get_dir_path_and_leaf(path, filename); 129098849Sken if (status != FSSH_B_OK) 129198849Sken return status; 129298849Sken 1293111119Simp return path_to_vnode(path, true, _vnode, NULL, kernel); 129498849Sken} 129598849Sken 129698849Sken 129798849Sken/** \brief Retrieves the directory vnode and the leaf name of an entry referred 129898849Sken * to by a FD + path pair. 1299111119Simp * 130098849Sken * \a path must be given in either case. \a fd might be omitted, in which 130198849Sken * case \a path is either an absolute path or one relative to the current 130298849Sken * directory. If both a supplied and \a path is relative it is reckoned off 130398849Sken * of the directory referred to by \a fd. If \a path is absolute \a fd is 130498849Sken * ignored. 1305111119Simp * 130698849Sken * The caller has the responsibility to call put_vnode() on the returned 130798849Sken * directory vnode. 130898849Sken * 130998849Sken * \param fd The FD. May be < 0. 131098849Sken * \param path The absolute or relative path. Must not be \c NULL. The buffer 131198849Sken * is modified by this function. It must have at least room for a 131298849Sken * string one character longer than the path it contains. 131398849Sken * \param _vnode A pointer to a variable the directory vnode shall be written 1314111119Simp * into. 131598849Sken * \param filename A buffer of size FSSH_B_FILE_NAME_LENGTH or larger into which 131698849Sken * the leaf name of the specified entry will be written. 131798849Sken * \param kernel \c true, if invoked from inside the kernel, \c false if 131898849Sken * invoked from userland. 131998849Sken * \return \c FSSH_B_OK, if everything went fine, another error code otherwise. 132098849Sken */ 132198849Sken 132298849Skenstatic fssh_status_t 132398849Skenfd_and_path_to_dir_vnode(int fd, char *path, struct vnode **_vnode, 132498849Sken char *filename, bool kernel) 132598849Sken{ 132698849Sken if (!path) 132798849Sken return FSSH_B_BAD_VALUE; 132898849Sken if (fd < 0) 132998849Sken return path_to_dir_vnode(path, _vnode, filename, kernel); 133098849Sken 133198849Sken fssh_status_t status = get_dir_path_and_leaf(path, filename); 133298849Sken if (status != FSSH_B_OK) 133398849Sken return status; 133498849Sken 133598849Sken return fd_and_path_to_vnode(fd, path, true, _vnode, NULL, kernel); 133698849Sken} 133798849Sken 133898849Sken 133998849Sken/** Returns a vnode's name in the d_name field of a supplied dirent buffer. 134098849Sken */ 134198849Sken 134298849Skenstatic fssh_status_t 134398849Skenget_vnode_name(struct vnode *vnode, struct vnode *parent, 134498849Sken struct fssh_dirent *buffer, fssh_size_t bufferSize) 134598849Sken{ 134698849Sken if (bufferSize < sizeof(struct fssh_dirent)) 134798849Sken return FSSH_B_BAD_VALUE; 134898849Sken 134998849Sken // See if vnode is the root of a mount and move to the covered 135098849Sken // vnode so we get the underlying file system 135198849Sken VNodePutter vnodePutter; 135298849Sken if (vnode->mount->root_vnode == vnode && vnode->mount->covers_vnode != NULL) { 135398849Sken vnode = vnode->mount->covers_vnode; 135498849Sken inc_vnode_ref_count(vnode); 135598849Sken vnodePutter.SetTo(vnode); 135698849Sken } 135798849Sken 135898849Sken if (HAS_FS_CALL(vnode, get_vnode_name)) { 135998849Sken // The FS supports getting the name of a vnode. 136098849Sken return FS_CALL(vnode, get_vnode_name, buffer->d_name, 136198849Sken (char*)buffer + bufferSize - buffer->d_name); 136298849Sken } 136398849Sken 136498849Sken // The FS doesn't support getting the name of a vnode. So we search the 136598849Sken // parent directory for the vnode, if the caller let us. 136698849Sken 136798849Sken if (parent == NULL) 136898849Sken return FSSH_EOPNOTSUPP; 136998849Sken 137098849Sken void *cookie; 137198849Sken 137298849Sken fssh_status_t status = FS_CALL(parent, open_dir, &cookie); 137398849Sken if (status >= FSSH_B_OK) { 137498849Sken while (true) { 137598849Sken uint32_t num = 1; 137698849Sken status = dir_read(parent, cookie, buffer, bufferSize, &num); 137798849Sken if (status < FSSH_B_OK) 137898849Sken break; 137998849Sken if (num == 0) { 138098849Sken status = FSSH_B_ENTRY_NOT_FOUND; 138198849Sken break; 138298849Sken } 138398849Sken 138498849Sken if (vnode->id == buffer->d_ino) { 138598849Sken // found correct entry! 138698849Sken break; 138798849Sken } 138898849Sken } 138998849Sken 139098849Sken FS_CALL(vnode, close_dir, cookie); 139198849Sken FS_CALL(vnode, free_dir_cookie, cookie); 139298849Sken } 139398849Sken return status; 139498849Sken} 139598849Sken 139698849Sken 139798849Skenstatic fssh_status_t 139845386Swpaulget_vnode_name(struct vnode *vnode, struct vnode *parent, char *name, 139945386Swpaul fssh_size_t nameSize) 140045386Swpaul{ 140145386Swpaul char buffer[sizeof(struct fssh_dirent) + FSSH_B_FILE_NAME_LENGTH]; 140245386Swpaul struct fssh_dirent *dirent = (struct fssh_dirent *)buffer; 140345386Swpaul 1404102336Salfred fssh_status_t status = get_vnode_name(vnode, parent, buffer, sizeof(buffer)); 1405102336Salfred if (status != FSSH_B_OK) 140645386Swpaul return status; 140745386Swpaul 140845386Swpaul if (fssh_strlcpy(name, dirent->d_name, nameSize) >= nameSize) 140945386Swpaul return FSSH_B_BUFFER_OVERFLOW; 141045386Swpaul 141145386Swpaul return FSSH_B_OK; 141245386Swpaul} 141345386Swpaul 141445386Swpaul 141545386Swpaul/** Gets the full path to a given directory vnode. 141645386Swpaul * It uses the fs_get_vnode_name() call to get the name of a vnode; if a 141748597Swpaul * file system doesn't support this call, it will fall back to iterating 141845386Swpaul * through the parent directory to get the name of the child. 141945386Swpaul * 142045386Swpaul * To protect against circular loops, it supports a maximum tree depth 142145386Swpaul * of 256 levels. 1422102336Salfred * 1423102336Salfred * Note that the path may not be correct the time this function returns! 142445386Swpaul * It doesn't use any locking to prevent returning the correct path, as 142545386Swpaul * paths aren't safe anyway: the path to a file can change at any time. 142645386Swpaul * 142745386Swpaul * It might be a good idea, though, to check if the returned path exists 142845386Swpaul * in the calling function (it's not done here because of efficiency) 142945386Swpaul */ 143045386Swpaul 143145386Swpaulstatic fssh_status_t 143245386Swpauldir_vnode_to_path(struct vnode *vnode, char *buffer, fssh_size_t bufferSize) 143345386Swpaul{ 143445386Swpaul FUNCTION(("dir_vnode_to_path(%p, %p, %lu)\n", vnode, buffer, bufferSize)); 143545386Swpaul 143645386Swpaul if (vnode == NULL || buffer == NULL) 143745386Swpaul return FSSH_B_BAD_VALUE; 143845386Swpaul 143945386Swpaul /* this implementation is currently bound to FSSH_B_PATH_NAME_LENGTH */ 1440102336Salfred KPath pathBuffer; 1441102336Salfred if (pathBuffer.InitCheck() != FSSH_B_OK) 144245386Swpaul return FSSH_B_NO_MEMORY; 144345386Swpaul 144445386Swpaul char *path = pathBuffer.LockBuffer(); 144545386Swpaul int32_t insert = pathBuffer.BufferSize(); 144645386Swpaul int32_t maxLevel = 256; 144763699Swpaul int32_t length; 144845386Swpaul fssh_status_t status; 144945386Swpaul 145045386Swpaul // we don't use get_vnode() here because this call is more 145145386Swpaul // efficient and does all we need from get_vnode() 145245386Swpaul inc_vnode_ref_count(vnode); 145348597Swpaul 145445386Swpaul // resolve a volume root to its mount point 145545386Swpaul struct vnode *mountPoint = resolve_volume_root_to_mount_point(vnode); 145645386Swpaul if (mountPoint) { 145745386Swpaul put_vnode(vnode); 1458102336Salfred vnode = mountPoint; 1459102336Salfred } 146045386Swpaul 146145386Swpaul path[--insert] = '\0'; 146245386Swpaul 146345386Swpaul while (true) { 146445386Swpaul // the name buffer is also used for fs_read_dir() 146545386Swpaul char nameBuffer[sizeof(struct fssh_dirent) + FSSH_B_FILE_NAME_LENGTH]; 146645386Swpaul char *name = &((struct fssh_dirent *)nameBuffer)->d_name[0]; 146745386Swpaul struct vnode *parentVnode; 146845386Swpaul fssh_vnode_id parentID; 146945386Swpaul 147045386Swpaul // lookup the parent vnode 147145386Swpaul status = lookup_dir_entry(vnode, "..", &parentVnode); 147245386Swpaul if (status < FSSH_B_OK) 147345386Swpaul goto out; 147445386Swpaul 147545386Swpaul // get the node's name 1476102336Salfred status = get_vnode_name(vnode, parentVnode, 1477102336Salfred (struct fssh_dirent*)nameBuffer, sizeof(nameBuffer)); 147845386Swpaul 147945386Swpaul // resolve a volume root to its mount point 148045386Swpaul mountPoint = resolve_volume_root_to_mount_point(parentVnode); 148145386Swpaul if (mountPoint) { 148245386Swpaul put_vnode(parentVnode); 148345386Swpaul parentVnode = mountPoint; 148445386Swpaul parentID = parentVnode->id; 148545386Swpaul } 148645386Swpaul 148745386Swpaul bool hitRoot = (parentVnode == vnode); 148848597Swpaul 148945386Swpaul // release the current vnode, we only need its parent from now on 149045386Swpaul put_vnode(vnode); 149145386Swpaul vnode = parentVnode; 149245386Swpaul 1493102336Salfred if (status < FSSH_B_OK) 1494102336Salfred goto out; 149545386Swpaul 149645386Swpaul if (hitRoot) { 149745386Swpaul // we have reached "/", which means we have constructed the full 149845386Swpaul // path 149945386Swpaul break; 150045386Swpaul } 150145386Swpaul 150245386Swpaul // ToDo: add an explicit check for loops in about 10 levels to do 150345386Swpaul // real loop detection 150445386Swpaul 150545386Swpaul // don't go deeper as 'maxLevel' to prevent circular loops 150645386Swpaul if (maxLevel-- < 0) { 150745386Swpaul status = FSSH_ELOOP; 150845386Swpaul goto out; 150945386Swpaul } 151045386Swpaul 1511102336Salfred // add the name in front of the current path 1512102336Salfred name[FSSH_B_FILE_NAME_LENGTH - 1] = '\0'; 151345386Swpaul length = fssh_strlen(name); 151445386Swpaul insert -= length; 151545386Swpaul if (insert <= 0) { 151645386Swpaul status = FSSH_ENOBUFS; 151745386Swpaul goto out; 151845386Swpaul } 151945386Swpaul fssh_memcpy(path + insert, name, length); 152045386Swpaul path[--insert] = '/'; 152145386Swpaul } 152245386Swpaul 152345386Swpaul // the root dir will result in an empty path: fix it 152445386Swpaul if (path[insert] == '\0') 152545386Swpaul path[--insert] = '/'; 152645386Swpaul 152745386Swpaul TRACE((" path is: %s\n", path + insert)); 152845386Swpaul 152945386Swpaul // copy the path to the output buffer 153045386Swpaul length = pathBuffer.BufferSize() - insert; 153145386Swpaul if (length <= (int)bufferSize) 1532102336Salfred fssh_memcpy(buffer, path + insert, length); 1533102336Salfred else 153445386Swpaul status = FSSH_ENOBUFS; 153545386Swpaul 153648011Swpaulout: 153745386Swpaul put_vnode(vnode); 153845386Swpaul return status; 153945386Swpaul} 154045386Swpaul 154145386Swpaul 154245386Swpaul/** Checks the length of every path component, and adds a '.' 154345386Swpaul * if the path ends in a slash. 154445386Swpaul * The given path buffer must be able to store at least one 154545386Swpaul * additional character. 154645386Swpaul */ 1547105219Sphk 1548102336Salfredstatic fssh_status_t 154945386Swpaulcheck_path(char *to) 155045386Swpaul{ 155145386Swpaul int32_t length = 0; 155245386Swpaul 155345386Swpaul // check length of every path component 155445386Swpaul 155545386Swpaul while (*to) { 155645386Swpaul char *begin; 155745386Swpaul if (*to == '/') 155845386Swpaul to++, length++; 155945386Swpaul 156045386Swpaul begin = to; 156145386Swpaul while (*to != '/' && *to) 156245386Swpaul to++, length++; 156345386Swpaul 156445386Swpaul if (to - begin > FSSH_B_FILE_NAME_LENGTH) 156545386Swpaul return FSSH_B_NAME_TOO_LONG; 156645386Swpaul } 156745386Swpaul 156845386Swpaul if (length == 0) 156945386Swpaul return FSSH_B_ENTRY_NOT_FOUND; 157045386Swpaul 157145386Swpaul // complete path if there is a slash at the end 157245386Swpaul 157345386Swpaul if (*(to - 1) == '/') { 157445386Swpaul if (length > FSSH_B_PATH_NAME_LENGTH - 2) 157545386Swpaul return FSSH_B_NAME_TOO_LONG; 157645386Swpaul 1577105219Sphk to[0] = '.'; 1578102336Salfred to[1] = '\0'; 157945386Swpaul } 158045386Swpaul 158145386Swpaul return FSSH_B_OK; 158245386Swpaul} 158345386Swpaul 158445386Swpaul 158545386Swpaulstatic struct file_descriptor * 158645386Swpaulget_fd_and_vnode(int fd, struct vnode **_vnode, bool kernel) 158745386Swpaul{ 158845386Swpaul struct file_descriptor *descriptor = get_fd(get_current_io_context(kernel), fd); 158945386Swpaul if (descriptor == NULL) 159045386Swpaul return NULL; 159145386Swpaul 159245386Swpaul if (fd_vnode(descriptor) == NULL) { 159345386Swpaul put_fd(descriptor); 159445386Swpaul return NULL; 159545386Swpaul } 159645386Swpaul 159745386Swpaul // ToDo: when we can close a file descriptor at any point, investigate 159845386Swpaul // if this is still valid to do (accessing the vnode without ref_count 159945386Swpaul // or locking) 160045386Swpaul *_vnode = descriptor->u.vnode; 160145386Swpaul return descriptor; 160245386Swpaul} 160345386Swpaul 160445386Swpaul 160545386Swpaulstatic struct vnode * 160645386Swpaulget_vnode_from_fd(int fd, bool kernel) 160745386Swpaul{ 160845386Swpaul struct file_descriptor *descriptor; 160945386Swpaul struct vnode *vnode; 161045386Swpaul 161145386Swpaul descriptor = get_fd(get_current_io_context(kernel), fd); 161245386Swpaul if (descriptor == NULL) 161345386Swpaul return NULL; 161445386Swpaul 161545386Swpaul vnode = fd_vnode(descriptor); 161645386Swpaul if (vnode != NULL) 161745386Swpaul inc_vnode_ref_count(vnode); 161845386Swpaul 161945386Swpaul put_fd(descriptor); 162045386Swpaul return vnode; 1621102336Salfred} 1622102336Salfred 162345386Swpaul 162445386Swpaul/** Gets the vnode from an FD + path combination. If \a fd is lower than zero, 162545386Swpaul * only the path will be considered. In this case, the \a path must not be 162645386Swpaul * NULL. 162745386Swpaul * If \a fd is a valid file descriptor, \a path may be NULL for directories, 162845386Swpaul * and should be NULL for files. 162945386Swpaul */ 163045386Swpaul 163145386Swpaulstatic fssh_status_t 163245386Swpaulfd_and_path_to_vnode(int fd, char *path, bool traverseLeafLink, 163345386Swpaul struct vnode **_vnode, fssh_vnode_id *_parentID, bool kernel) 163445386Swpaul{ 163545386Swpaul if (fd < 0 && !path) 163645386Swpaul return FSSH_B_BAD_VALUE; 163745386Swpaul 163845386Swpaul if (fd < 0 || (path != NULL && path[0] == '/')) { 163945386Swpaul // no FD or absolute path 164045386Swpaul return path_to_vnode(path, traverseLeafLink, _vnode, _parentID, kernel); 164145386Swpaul } 164245386Swpaul 164345386Swpaul // FD only, or FD + relative path 164445386Swpaul struct vnode *vnode = get_vnode_from_fd(fd, kernel); 164571999Sphk if (!vnode) 164671999Sphk return FSSH_B_FILE_ERROR; 164745386Swpaul 164845386Swpaul if (path != NULL) { 164945386Swpaul return vnode_path_to_vnode(vnode, path, traverseLeafLink, 0, 165045386Swpaul _vnode, _parentID); 165145386Swpaul } 165245386Swpaul 165372084Sphk // there is no relative path to take into account 165445386Swpaul 165545386Swpaul *_vnode = vnode; 165645386Swpaul if (_parentID) 165745386Swpaul *_parentID = -1; 165845386Swpaul 165945386Swpaul return FSSH_B_OK; 166045386Swpaul} 166145386Swpaul 166245386Swpaul 166345386Swpaulstatic int 166445386Swpaulget_new_fd(int type, struct fs_mount *mount, struct vnode *vnode, 166545386Swpaul void *cookie, int openMode, bool kernel) 166645386Swpaul{ 166745386Swpaul struct file_descriptor *descriptor; 166845386Swpaul int fd; 166945386Swpaul 167045386Swpaul // if the vnode is locked, we don't allow creating a new file descriptor for it 167145386Swpaul if (vnode && vnode->mandatory_locked_by != NULL) 167245386Swpaul return FSSH_B_BUSY; 167345386Swpaul 167445386Swpaul descriptor = alloc_fd(); 167545386Swpaul if (!descriptor) 167645386Swpaul return FSSH_B_NO_MEMORY; 167745386Swpaul 167845386Swpaul if (vnode) 167945386Swpaul descriptor->u.vnode = vnode; 168045386Swpaul else 168145386Swpaul descriptor->u.mount = mount; 168245386Swpaul descriptor->cookie = cookie; 168345386Swpaul 168445386Swpaul switch (type) { 168545386Swpaul // vnode types 168645386Swpaul case FDTYPE_FILE: 168745386Swpaul descriptor->ops = &sFileOps; 168845386Swpaul break; 168945386Swpaul case FDTYPE_DIR: 169045386Swpaul descriptor->ops = &sDirectoryOps; 169145386Swpaul break; 169245386Swpaul case FDTYPE_ATTR: 169345386Swpaul descriptor->ops = &sAttributeOps; 169445386Swpaul break; 169545386Swpaul case FDTYPE_ATTR_DIR: 169645386Swpaul descriptor->ops = &sAttributeDirectoryOps; 169745386Swpaul break; 169845386Swpaul 169945386Swpaul // mount types 1700102336Salfred case FDTYPE_INDEX_DIR: 1701102336Salfred descriptor->ops = &sIndexDirectoryOps; 170245386Swpaul break; 170345386Swpaul case FDTYPE_QUERY: 170445386Swpaul descriptor->ops = &sQueryOps; 170545386Swpaul break; 170698849Sken 170745386Swpaul default: 170845386Swpaul fssh_panic("get_new_fd() called with unknown type %d\n", type); 170945386Swpaul break; 171045386Swpaul } 171183630Sjlemon descriptor->type = type; 171283630Sjlemon descriptor->open_mode = openMode; 171383630Sjlemon 171483630Sjlemon fd = new_fd(get_current_io_context(kernel), descriptor); 171558698Sjlemon if (fd < 0) { 171645386Swpaul free(descriptor); 171745386Swpaul return FSSH_B_NO_MORE_FDS; 171845386Swpaul } 171945386Swpaul 172045386Swpaul return fd; 172145386Swpaul} 172245386Swpaul 172345386Swpaul 172445386Swpaul/*! Does the dirty work of combining the file_io_vecs with the iovecs 172545386Swpaul and calls the file system hooks to read/write the request to disk. 172645386Swpaul*/ 172745386Swpaulstatic fssh_status_t 172845386Swpaulcommon_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs, 172945386Swpaul fssh_size_t fileVecCount, const fssh_iovec *vecs, fssh_size_t vecCount, 173045386Swpaul uint32_t *_vecIndex, fssh_size_t *_vecOffset, fssh_size_t *_numBytes, 173145386Swpaul bool doWrite) 173245386Swpaul{ 173345386Swpaul if (fileVecCount == 0) { 173445386Swpaul // There are no file vecs at this offset, so we're obviously trying 173545386Swpaul // to access the file outside of its bounds 173645386Swpaul return FSSH_B_BAD_VALUE; 173745386Swpaul } 173845386Swpaul 173945386Swpaul fssh_size_t numBytes = *_numBytes; 174045386Swpaul uint32_t fileVecIndex; 174145386Swpaul fssh_size_t vecOffset = *_vecOffset; 174245386Swpaul uint32_t vecIndex = *_vecIndex; 174345386Swpaul fssh_status_t status; 174445386Swpaul fssh_size_t size; 174545386Swpaul 174645386Swpaul if (!doWrite && vecOffset == 0) { 174745386Swpaul // now directly read the data from the device 174845386Swpaul // the first file_io_vec can be read directly 174945386Swpaul 175076033Swpaul size = fileVecs[0].length; 175145386Swpaul if (size > numBytes) 175245386Swpaul size = numBytes; 175345386Swpaul 175498849Sken status = fssh_read_pages(fd, fileVecs[0].offset, &vecs[vecIndex], 175598849Sken vecCount - vecIndex, &size); 175698849Sken if (status < FSSH_B_OK) 175798849Sken return status; 175898849Sken 175998849Sken // TODO: this is a work-around for buggy device drivers! 176098849Sken // When our own drivers honour the length, we can: 176198849Sken // a) also use this direct I/O for writes (otherwise, it would 176298849Sken // overwrite precious data) 176398849Sken // b) panic if the term below is true (at least for writes) 176498849Sken if ((uint64_t)size > (uint64_t)fileVecs[0].length) { 176598849Sken //dprintf("warning: device driver %p doesn't respect total length in read_pages() call!\n", ref->device); 176645386Swpaul size = fileVecs[0].length; 176745386Swpaul } 176845386Swpaul 176945386Swpaul ASSERT(size <= fileVecs[0].length); 177045386Swpaul 177145386Swpaul // If the file portion was contiguous, we're already done now 177245386Swpaul if (size == numBytes) 177345386Swpaul return FSSH_B_OK; 177445386Swpaul 177545386Swpaul // if we reached the end of the file, we can return as well 177645386Swpaul if ((uint64_t)size != (uint64_t)fileVecs[0].length) { 177745386Swpaul *_numBytes = size; 177845386Swpaul return FSSH_B_OK; 177945386Swpaul } 178045386Swpaul 178145386Swpaul fileVecIndex = 1; 178245386Swpaul 178345386Swpaul // first, find out where we have to continue in our iovecs 178445386Swpaul for (; vecIndex < vecCount; vecIndex++) { 178545386Swpaul if (size < vecs[vecIndex].iov_len) 178645386Swpaul break; 178745386Swpaul 178845386Swpaul size -= vecs[vecIndex].iov_len; 178945386Swpaul } 179045386Swpaul 179145386Swpaul vecOffset = size; 179245386Swpaul } else { 179345386Swpaul fileVecIndex = 0; 179445386Swpaul size = 0; 179545386Swpaul } 179645386Swpaul 179745386Swpaul // Too bad, let's process the rest of the file_io_vecs 179845386Swpaul 179945386Swpaul fssh_size_t totalSize = size; 180045386Swpaul fssh_size_t bytesLeft = numBytes - size; 180145386Swpaul 180245386Swpaul for (; fileVecIndex < fileVecCount; fileVecIndex++) { 180345386Swpaul const fssh_file_io_vec &fileVec = fileVecs[fileVecIndex]; 180445386Swpaul fssh_off_t fileOffset = fileVec.offset; 180545386Swpaul fssh_off_t fileLeft = fssh_min_c((uint64_t)fileVec.length, (uint64_t)bytesLeft); 180645386Swpaul 180745386Swpaul TRACE(("FILE VEC [%lu] length %Ld\n", fileVecIndex, fileLeft)); 180845386Swpaul 180945386Swpaul // process the complete fileVec 181045386Swpaul while (fileLeft > 0) { 181145386Swpaul fssh_iovec tempVecs[MAX_TEMP_IO_VECS]; 181245386Swpaul uint32_t tempCount = 0; 181345386Swpaul 181445386Swpaul // size tracks how much of what is left of the current fileVec 181545386Swpaul // (fileLeft) has been assigned to tempVecs 181645386Swpaul size = 0; 181745386Swpaul 181845386Swpaul // assign what is left of the current fileVec to the tempVecs 181945386Swpaul for (size = 0; (uint64_t)size < (uint64_t)fileLeft && vecIndex < vecCount 182045386Swpaul && tempCount < MAX_TEMP_IO_VECS;) { 182145386Swpaul // try to satisfy one iovec per iteration (or as much as 182298849Sken // possible) 182398849Sken 182498849Sken // bytes left of the current iovec 182598849Sken fssh_size_t vecLeft = vecs[vecIndex].iov_len - vecOffset; 182698849Sken if (vecLeft == 0) { 182745386Swpaul vecOffset = 0; 182845386Swpaul vecIndex++; 182945386Swpaul continue; 183045386Swpaul } 183145386Swpaul 183298849Sken TRACE(("fill vec %ld, offset = %lu, size = %lu\n", 183398849Sken vecIndex, vecOffset, size)); 183445386Swpaul 183545386Swpaul // actually available bytes 183698849Sken fssh_size_t tempVecSize = fssh_min_c(vecLeft, fileLeft - size); 183798849Sken 183845386Swpaul tempVecs[tempCount].iov_base 183945386Swpaul = (void *)((fssh_addr_t)vecs[vecIndex].iov_base + vecOffset); 184045386Swpaul tempVecs[tempCount].iov_len = tempVecSize; 184145386Swpaul tempCount++; 184245386Swpaul 184358698Sjlemon size += tempVecSize; 184445386Swpaul vecOffset += tempVecSize; 184558698Sjlemon } 184658698Sjlemon 184745386Swpaul fssh_size_t bytes = size; 184845386Swpaul if (doWrite) { 184945386Swpaul status = fssh_write_pages(fd, fileOffset, tempVecs, 185045386Swpaul tempCount, &bytes); 185145386Swpaul } else { 185245386Swpaul status = fssh_read_pages(fd, fileOffset, tempVecs, 185345386Swpaul tempCount, &bytes); 185445386Swpaul } 185545386Swpaul if (status < FSSH_B_OK) 185645386Swpaul return status; 185745386Swpaul 185845386Swpaul totalSize += bytes; 185945386Swpaul bytesLeft -= size; 186045386Swpaul fileOffset += size; 186145386Swpaul fileLeft -= size; 186245386Swpaul 186345386Swpaul if (size != bytes || vecIndex >= vecCount) { 186445386Swpaul // there are no more bytes or iovecs, let's bail out 1865102336Salfred *_numBytes = totalSize; 1866102336Salfred return FSSH_B_OK; 186745386Swpaul } 186845386Swpaul } 186945386Swpaul } 187045386Swpaul 187145386Swpaul *_vecIndex = vecIndex; 187245386Swpaul *_vecOffset = vecOffset; 187345386Swpaul *_numBytes = totalSize; 187445386Swpaul return FSSH_B_OK; 187545386Swpaul} 187645386Swpaul 187745386Swpaul 187845386Swpaul// #pragma mark - public VFS API 187945386Swpaul 188045386Swpaul 188145386Swpaulextern "C" fssh_status_t 188245386Swpaulfssh_new_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID, 188345386Swpaul void *privateNode, fssh_fs_vnode_ops *ops) 188445386Swpaul{ 188545386Swpaul FUNCTION(("new_vnode(volume = %p (%ld), vnodeID = %Ld, node = %p)\n", 188645386Swpaul volume, volume->id, vnodeID, privateNode)); 188745386Swpaul 188845386Swpaul if (privateNode == NULL) 188945386Swpaul return FSSH_B_BAD_VALUE; 189045386Swpaul 189145386Swpaul fssh_mutex_lock(&sVnodeMutex); 189245386Swpaul 189345386Swpaul // file system integrity check: 189445386Swpaul // test if the vnode already exists and bail out if this is the case! 189545386Swpaul 189645386Swpaul // ToDo: the R5 implementation obviously checks for a different cookie 189745386Swpaul // and doesn't panic if they are equal 189845386Swpaul 189945386Swpaul struct vnode *vnode = lookup_vnode(volume->id, vnodeID); 190045386Swpaul if (vnode != NULL) { 190145386Swpaul fssh_panic("vnode %d:%" FSSH_B_PRIdINO " already exists (node = %p, " 190249133Swpaul "vnode->node = %p)!", (int)volume->id, vnodeID, privateNode, 190345386Swpaul vnode->private_node); 190445386Swpaul } 190545386Swpaul 190645386Swpaul fssh_status_t status = create_new_vnode(&vnode, volume->id, vnodeID); 190745386Swpaul if (status == FSSH_B_OK) { 190845386Swpaul vnode->private_node = privateNode; 190945386Swpaul vnode->ops = ops; 191045386Swpaul vnode->busy = true; 191145386Swpaul vnode->unpublished = true; 191245386Swpaul } 191345386Swpaul 191445386Swpaul TRACE(("returns: %s\n", strerror(status))); 191545386Swpaul 191645386Swpaul fssh_mutex_unlock(&sVnodeMutex); 191745386Swpaul return status; 191845386Swpaul} 191945386Swpaul 192045386Swpaul 192145386Swpaulextern "C" fssh_status_t 192245386Swpaulfssh_publish_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID, 192345386Swpaul void *privateNode, fssh_fs_vnode_ops *ops, int type, uint32_t flags) 192445386Swpaul{ 192545386Swpaul FUNCTION(("publish_vnode()\n")); 192658698Sjlemon 192758698Sjlemon MutexLocker locker(sVnodeMutex); 192858698Sjlemon 192945386Swpaul struct vnode *vnode = lookup_vnode(volume->id, vnodeID); 193045386Swpaul fssh_status_t status = FSSH_B_OK; 193145386Swpaul 193245386Swpaul if (vnode != NULL && vnode->busy && vnode->unpublished 193345386Swpaul && vnode->private_node == privateNode) { 193445386Swpaul // already known, but not published 193598849Sken } else if (vnode == NULL && privateNode != NULL) { 193698849Sken status = create_new_vnode(&vnode, volume->id, vnodeID); 193749036Swpaul if (status == FSSH_B_OK) { 193845386Swpaul vnode->private_node = privateNode; 193998849Sken vnode->ops = ops; 194098849Sken vnode->busy = true; 194198849Sken vnode->unpublished = true; 194298849Sken } 194358698Sjlemon } else 194458698Sjlemon status = FSSH_B_BAD_VALUE; 194558698Sjlemon 194645386Swpaul // create sub vnodes, if necessary 194745386Swpaul if (status == FSSH_B_OK && volume->sub_volume != NULL) { 194845386Swpaul locker.Unlock(); 194945386Swpaul 195045386Swpaul fssh_fs_volume *subVolume = volume; 195145386Swpaul while (status == FSSH_B_OK && subVolume->sub_volume != NULL) { 195245386Swpaul subVolume = subVolume->sub_volume; 195345386Swpaul status = subVolume->ops->create_sub_vnode(subVolume, vnodeID, 195445386Swpaul vnode); 195545386Swpaul } 195651352Swpaul 195745386Swpaul if (status != FSSH_B_OK) { 195845386Swpaul // error -- clean up the created sub vnodes 195945386Swpaul while (subVolume->super_volume != volume) { 196045386Swpaul subVolume = subVolume->super_volume; 196158698Sjlemon subVolume->ops->delete_sub_vnode(subVolume, vnode); 196258698Sjlemon } 196358698Sjlemon } 196445386Swpaul 196545386Swpaul locker.Lock(); 196645386Swpaul } 196745386Swpaul 196845386Swpaul if (status == FSSH_B_OK) { 196945386Swpaul vnode->type = type; 197045386Swpaul vnode->busy = false; 197145386Swpaul vnode->unpublished = false; 197245386Swpaul } 197345386Swpaul 197445386Swpaul TRACE(("returns: %s\n", strerror(status))); 197545386Swpaul 197645386Swpaul return status; 197745386Swpaul} 197845386Swpaul 197945386Swpaul 198045386Swpaulextern "C" fssh_status_t 198145386Swpaulfssh_get_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID, 198245386Swpaul void **privateNode) 198345386Swpaul{ 198445386Swpaul struct vnode *vnode; 198545386Swpaul 198645386Swpaul if (volume == NULL) 198745386Swpaul return FSSH_B_BAD_VALUE; 198845386Swpaul 198949133Swpaul fssh_status_t status = get_vnode(volume->id, vnodeID, &vnode, true); 199045386Swpaul if (status < FSSH_B_OK) 199145386Swpaul return status; 199245386Swpaul 199345386Swpaul // If this is a layered FS, we need to get the node cookie for the requested 199445386Swpaul // layer. 199545386Swpaul if (HAS_FS_CALL(vnode, get_super_vnode)) { 199645386Swpaul fssh_fs_vnode resolvedNode; 199745386Swpaul fssh_status_t status = FS_CALL(vnode, get_super_vnode, volume, 199845386Swpaul &resolvedNode); 199958698Sjlemon if (status != FSSH_B_OK) { 200058698Sjlemon fssh_panic("get_vnode(): Failed to get super node for vnode %p, " 200158698Sjlemon "volume: %p", vnode, volume); 200245386Swpaul put_vnode(vnode); 200345386Swpaul return status; 200445386Swpaul } 200545386Swpaul 200645386Swpaul if (privateNode != NULL) 200745386Swpaul *privateNode = resolvedNode.private_node; 200845386Swpaul } else if (privateNode != NULL) 200945386Swpaul *privateNode = vnode->private_node; 201045386Swpaul 201145386Swpaul return FSSH_B_OK; 201298849Sken} 201345386Swpaul 201445386Swpaul 201545386Swpaulextern "C" fssh_status_t 201645386Swpaulfssh_acquire_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID) 201798849Sken{ 201845386Swpaul struct vnode *vnode; 201945386Swpaul 202045386Swpaul fssh_mutex_lock(&sVnodeMutex); 202145386Swpaul vnode = lookup_vnode(volume->id, vnodeID); 202245386Swpaul fssh_mutex_unlock(&sVnodeMutex); 202345386Swpaul 202445386Swpaul if (vnode == NULL) 202545386Swpaul return FSSH_B_BAD_VALUE; 202645386Swpaul 202745386Swpaul inc_vnode_ref_count(vnode); 202845386Swpaul return FSSH_B_OK; 202945386Swpaul} 203045386Swpaul 203145386Swpaul 203245386Swpaulextern "C" fssh_status_t 203345386Swpaulfssh_put_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID) 203445386Swpaul{ 203545386Swpaul struct vnode *vnode; 203645386Swpaul 203745386Swpaul fssh_mutex_lock(&sVnodeMutex); 203845386Swpaul vnode = lookup_vnode(volume->id, vnodeID); 2039102336Salfred fssh_mutex_unlock(&sVnodeMutex); 2040102336Salfred 204149011Swpaul if (vnode == NULL) 204245386Swpaul return FSSH_B_BAD_VALUE; 204345386Swpaul 204445386Swpaul dec_vnode_ref_count(vnode, true); 204545386Swpaul return FSSH_B_OK; 204645386Swpaul} 204745386Swpaul 204849011Swpaul 204949011Swpaulextern "C" fssh_status_t 205049011Swpaulfssh_remove_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID) 205149011Swpaul{ 205249011Swpaul struct vnode *vnode; 205345386Swpaul bool remove = false; 205445386Swpaul 205545386Swpaul MutexLocker locker(sVnodeMutex); 205649011Swpaul 205745386Swpaul vnode = lookup_vnode(volume->id, vnodeID); 205845386Swpaul if (vnode == NULL) 205998849Sken return FSSH_B_ENTRY_NOT_FOUND; 206098849Sken 206198849Sken if (vnode->covered_by != NULL) { 206298849Sken // this vnode is in use 206398849Sken fssh_mutex_unlock(&sVnodeMutex); 206498849Sken return FSSH_B_BUSY; 206598849Sken } 206698849Sken 206798849Sken vnode->remove = true; 206898849Sken if (vnode->unpublished) { 206998849Sken // prepare the vnode for deletion 207098849Sken vnode->busy = true; 207198849Sken remove = true; 207298849Sken } 207398849Sken 207498849Sken locker.Unlock(); 207598849Sken 207698849Sken if (remove) { 207798849Sken // if the vnode hasn't been published yet, we delete it here 207898849Sken fssh_atomic_add(&vnode->ref_count, -1); 207998849Sken free_vnode(vnode, true); 208098849Sken } 208198849Sken 208298849Sken return FSSH_B_OK; 208398849Sken} 208498849Sken 208598849Sken 208698849Skenextern "C" fssh_status_t 208798849Skenfssh_unremove_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID) 208898849Sken{ 208998849Sken struct vnode *vnode; 209098849Sken 2091102336Salfred fssh_mutex_lock(&sVnodeMutex); 2092102336Salfred 209349011Swpaul vnode = lookup_vnode(volume->id, vnodeID); 209445386Swpaul if (vnode) 209545386Swpaul vnode->remove = false; 209645386Swpaul 209749011Swpaul fssh_mutex_unlock(&sVnodeMutex); 209845386Swpaul return FSSH_B_OK; 209998849Sken} 210098849Sken 210198849Sken 210298849Skenextern "C" fssh_status_t 210398849Skenfssh_get_vnode_removed(fssh_fs_volume *volume, fssh_vnode_id vnodeID, bool* removed) 210498849Sken{ 210598849Sken fssh_mutex_lock(&sVnodeMutex); 210698849Sken 210798849Sken fssh_status_t result; 210898849Sken 210998849Sken if (struct vnode* vnode = lookup_vnode(volume->id, vnodeID)) { 211098849Sken if (removed) 211198849Sken *removed = vnode->remove; 211298849Sken result = FSSH_B_OK; 211349011Swpaul } else 211449011Swpaul result = FSSH_B_BAD_VALUE; 211545386Swpaul 211693818Sjhb fssh_mutex_unlock(&sVnodeMutex); 211793818Sjhb return result; 2118113609Snjl} 2119106936Ssam 212083630Sjlemon 212169583Swpaulextern "C" fssh_fs_volume* 212245386Swpaulfssh_volume_for_vnode(fssh_fs_vnode *_vnode) 212345386Swpaul{ 212445386Swpaul if (_vnode == NULL) 212572813Swpaul return NULL; 212645386Swpaul 212749011Swpaul struct vnode* vnode = static_cast<struct vnode*>(_vnode); 212849011Swpaul return vnode->mount->volume; 212965176Sdfr} 213049011Swpaul 213149011Swpaul 213245386Swpaul//! Works directly on the host's file system 213349011Swpaulextern "C" fssh_status_t 213445386Swpaulfssh_read_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs, 213545386Swpaul fssh_size_t count, fssh_size_t *_numBytes) 213645386Swpaul{ 213749035Swpaul // check how much the iovecs allow us to read 213849035Swpaul fssh_size_t toRead = 0; 213949133Swpaul for (fssh_size_t i = 0; i < count; i++) 214049035Swpaul toRead += vecs[i].iov_len; 214149011Swpaul 214249011Swpaul fssh_iovec* newVecs = NULL; 214349133Swpaul if (*_numBytes < toRead) { 214449011Swpaul // We're supposed to read less than specified by the vecs. Since 214549011Swpaul // readv_pos() doesn't support this, we need to clone the vecs. 214645386Swpaul newVecs = new(std::nothrow) fssh_iovec[count]; 214749011Swpaul if (!newVecs) 214849011Swpaul return FSSH_B_NO_MEMORY; 214949011Swpaul 215045386Swpaul fssh_size_t newCount = 0; 215145386Swpaul for (fssh_size_t i = 0; i < count && toRead > 0; i++) { 215245386Swpaul fssh_size_t vecLen = fssh_min_c(vecs[i].iov_len, toRead); 215345386Swpaul newVecs[i].iov_base = vecs[i].iov_base; 215445386Swpaul newVecs[i].iov_len = vecLen; 215545386Swpaul toRead -= vecLen; 215645386Swpaul newCount++; 215749011Swpaul } 215845386Swpaul 215945386Swpaul vecs = newVecs; 216045386Swpaul count = newCount; 216145386Swpaul } 216245386Swpaul 216345386Swpaul fssh_ssize_t bytesRead = fssh_readv_pos(fd, pos, vecs, count); 216445386Swpaul delete[] newVecs; 216545386Swpaul if (bytesRead < 0) 216645386Swpaul return fssh_get_errno(); 216749011Swpaul 216845386Swpaul *_numBytes = bytesRead; 216945386Swpaul return FSSH_B_OK; 217045386Swpaul} 217145386Swpaul 217245386Swpaul 217345386Swpaul//! Works directly on the host's file system 217445386Swpaulextern "C" fssh_status_t 217572645Sasmodaifssh_write_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs, 217645386Swpaul fssh_size_t count, fssh_size_t *_numBytes) 217745386Swpaul{ 217845386Swpaul // check how much the iovecs allow us to write 217945386Swpaul fssh_size_t toWrite = 0; 218045386Swpaul for (fssh_size_t i = 0; i < count; i++) 218149011Swpaul toWrite += vecs[i].iov_len; 218245386Swpaul 218345386Swpaul fssh_iovec* newVecs = NULL; 218445386Swpaul if (*_numBytes < toWrite) { 218545386Swpaul // We're supposed to write less than specified by the vecs. Since 218645386Swpaul // writev_pos() doesn't support this, we need to clone the vecs. 218745386Swpaul newVecs = new(std::nothrow) fssh_iovec[count]; 218845386Swpaul if (!newVecs) 218945386Swpaul return FSSH_B_NO_MEMORY; 219045386Swpaul 219145386Swpaul fssh_size_t newCount = 0; 219249011Swpaul for (fssh_size_t i = 0; i < count && toWrite > 0; i++) { 219350548Sbde fssh_size_t vecLen = fssh_min_c(vecs[i].iov_len, toWrite); 219445386Swpaul newVecs[i].iov_base = vecs[i].iov_base; 219549011Swpaul newVecs[i].iov_len = vecLen; 2196112872Snjl toWrite -= vecLen; 219749011Swpaul newCount++; 219845386Swpaul } 219945386Swpaul 220045386Swpaul vecs = newVecs; 220145386Swpaul count = newCount; 220245386Swpaul } 220345386Swpaul 220498849Sken fssh_ssize_t bytesWritten = fssh_writev_pos(fd, pos, vecs, count); 220545386Swpaul delete[] newVecs; 220645386Swpaul if (bytesWritten < 0) 220749011Swpaul return fssh_get_errno(); 220845386Swpaul 220945386Swpaul *_numBytes = bytesWritten; 221098849Sken return FSSH_B_OK; 221198849Sken} 221298849Sken 221398849Sken 221498849Sken//! Works directly on the host's file system 221598849Skenextern "C" fssh_status_t 221698849Skenfssh_read_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs, 221745386Swpaul fssh_size_t fileVecCount, const fssh_iovec *vecs, fssh_size_t vecCount, 221863699Swpaul uint32_t *_vecIndex, fssh_size_t *_vecOffset, fssh_size_t *_bytes) 221963699Swpaul{ 222063699Swpaul return common_file_io_vec_pages(fd, fileVecs, fileVecCount, 222163699Swpaul vecs, vecCount, _vecIndex, _vecOffset, _bytes, false); 222263699Swpaul} 222363699Swpaul 222463699Swpaul 222563699Swpaul//! Works directly on the host's file system 222663699Swpaulextern "C" fssh_status_t 222763699Swpaulfssh_write_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs, 222864139Swpaul fssh_size_t fileVecCount, const fssh_iovec *vecs, fssh_size_t vecCount, 222964139Swpaul uint32_t *_vecIndex, fssh_size_t *_vecOffset, fssh_size_t *_bytes) 223064139Swpaul{ 223164139Swpaul return common_file_io_vec_pages(fd, fileVecs, fileVecCount, 223263699Swpaul vecs, vecCount, _vecIndex, _vecOffset, _bytes, true); 223345386Swpaul} 223445386Swpaul 223598849Sken 223645386Swpaulextern "C" fssh_status_t 223798849Skenfssh_entry_cache_add(fssh_dev_t mountID, fssh_ino_t dirID, const char* name, 223898849Sken fssh_ino_t nodeID) 223945386Swpaul{ 224045386Swpaul // We don't implement an entry cache in the FS shell. 224198849Sken return FSSH_B_OK; 224245386Swpaul} 224398849Sken 224498849Sken 224545386Swpaulextern "C" fssh_status_t 224645386Swpaulfssh_entry_cache_remove(fssh_dev_t mountID, fssh_ino_t dirID, const char* name) 224745386Swpaul{ 224845386Swpaul // We don't implement an entry cache in the FS shell. 224945386Swpaul return FSSH_B_ENTRY_NOT_FOUND; 225045386Swpaul} 225145386Swpaul 225245386Swpaul 225398849Sken// #pragma mark - private VFS API 225445386Swpaul// Functions the VFS exports for other parts of the kernel 225545386Swpaul 225645386Swpaul 225745386Swpaul/** Acquires another reference to the vnode that has to be released 225845386Swpaul * by calling vfs_put_vnode(). 225945386Swpaul */ 226045386Swpaul 226145386Swpaulvoid 226245386Swpaulvfs_acquire_vnode(void *_vnode) 226363699Swpaul{ 226463699Swpaul inc_vnode_ref_count((struct vnode *)_vnode); 226563699Swpaul} 226663699Swpaul 226763699Swpaul 226863699Swpaul/** This is currently called from file_cache_create() only. 226963699Swpaul * It's probably a temporary solution as long as devfs requires that 227063699Swpaul * fs_read_pages()/fs_write_pages() are called with the standard 227163699Swpaul * open cookie and not with a device cookie. 227263699Swpaul * If that's done differently, remove this call; it has no other 227363699Swpaul * purpose. 227463699Swpaul */ 227563699Swpaul 227663699Swpaulfssh_status_t 227763699Swpaulvfs_get_cookie_from_fd(int fd, void **_cookie) 227895673Sphk{ 227963699Swpaul struct file_descriptor *descriptor; 228095673Sphk 228163699Swpaul descriptor = get_fd(get_current_io_context(true), fd); 228263699Swpaul if (descriptor == NULL) 228363699Swpaul return FSSH_B_FILE_ERROR; 228463699Swpaul 228563699Swpaul *_cookie = descriptor->cookie; 228663699Swpaul return FSSH_B_OK; 228745386Swpaul} 228845386Swpaul 228945386Swpaul 229045386Swpaulint 229198849Skenvfs_get_vnode_from_fd(int fd, bool kernel, void **vnode) 229298849Sken{ 229398849Sken *vnode = get_vnode_from_fd(fd, kernel); 229498849Sken 229598849Sken if (*vnode == NULL) 229698849Sken return FSSH_B_FILE_ERROR; 229798849Sken 229898849Sken return FSSH_B_NO_ERROR; 229998849Sken} 230098849Sken 230198849Sken 230298849Skenfssh_status_t 230398849Skenvfs_get_vnode_from_path(const char *path, bool kernel, void **_vnode) 230498849Sken{ 230598849Sken TRACE(("vfs_get_vnode_from_path: entry. path = '%s', kernel %d\n", path, kernel)); 230698849Sken 230798849Sken KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); 230898849Sken if (pathBuffer.InitCheck() != FSSH_B_OK) 230963090Sarchie return FSSH_B_NO_MEMORY; 231045386Swpaul 2311106936Ssam char *buffer = pathBuffer.LockBuffer(); 231245386Swpaul fssh_strlcpy(buffer, path, pathBuffer.BufferSize()); 2313113609Snjl 2314112872Snjl struct vnode *vnode; 2315112872Snjl fssh_status_t status = path_to_vnode(buffer, true, &vnode, NULL, kernel); 2316112872Snjl if (status < FSSH_B_OK) 2317112872Snjl return status; 2318112872Snjl 2319113609Snjl *_vnode = vnode; 2320112872Snjl return FSSH_B_OK; 2321112872Snjl} 2322112872Snjl 232345386Swpaul 2324112872Snjlfssh_status_t 2325112872Snjlvfs_get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, void **_vnode) 2326112872Snjl{ 232749011Swpaul struct vnode *vnode; 232845386Swpaul 232945386Swpaul fssh_status_t status = get_vnode(mountID, vnodeID, &vnode, false); 233098849Sken if (status < FSSH_B_OK) 233198849Sken return status; 233298849Sken 233398849Sken *_vnode = vnode; 233498849Sken return FSSH_B_OK; 233598849Sken} 233698849Sken 233798849Sken 233898849Skenfssh_status_t 233998849Skenvfs_read_pages(void *_vnode, void *cookie, fssh_off_t pos, 234098849Sken const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes) 234198849Sken{ 234298849Sken struct vnode *vnode = (struct vnode *)_vnode; 234398849Sken 234498849Sken return FS_CALL(vnode, read_pages, 234598849Sken cookie, pos, vecs, count, _numBytes); 234698849Sken} 234798849Sken 234898849Sken 234998849Skenfssh_status_t 235098849Skenvfs_write_pages(void *_vnode, void *cookie, fssh_off_t pos, 235198849Sken const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes) 235298849Sken{ 235398849Sken struct vnode *vnode = (struct vnode *)_vnode; 235498849Sken 235598849Sken return FS_CALL(vnode, write_pages, 235698849Sken cookie, pos, vecs, count, _numBytes); 2357113609Snjl} 2358113609Snjl 2359113609Snjl 2360113609Snjlfssh_status_t 2361113609Snjlvfs_entry_ref_to_vnode(fssh_mount_id mountID, fssh_vnode_id directoryID, 2362113609Snjl const char *name, void **_vnode) 2363113609Snjl{ 2364102336Salfred return entry_ref_to_vnode(mountID, directoryID, name, 2365102336Salfred (struct vnode **)_vnode); 236649011Swpaul} 236749011Swpaul 236849011Swpaul 236949011Swpaulvoid 237049011Swpaulvfs_fs_vnode_to_node_ref(void *_vnode, fssh_mount_id *_mountID, 237198849Sken fssh_vnode_id *_vnodeID) 237298849Sken{ 237349011Swpaul struct vnode *vnode = (struct vnode *)_vnode; 237449011Swpaul 2375112930Sphk *_mountID = vnode->device; 237667087Swpaul *_vnodeID = vnode->id; 237749011Swpaul} 237849011Swpaul 2379113609Snjl 2380113812Simp/** Looks up a vnode with the given mount and vnode ID. 2381113609Snjl * Must only be used with "in-use" vnodes as it doesn't grab a reference 2382112872Snjl * to the node. 2383112872Snjl * It's currently only be used by file_cache_create(). 2384112872Snjl */ 2385113609Snjl 238649011Swpaulfssh_status_t 2387112872Snjlvfs_lookup_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, 2388112872Snjl struct vnode **_vnode) 2389112872Snjl{ 2390112872Snjl fssh_mutex_lock(&sVnodeMutex); 2391112872Snjl struct vnode *vnode = lookup_vnode(mountID, vnodeID); 2392112872Snjl fssh_mutex_unlock(&sVnodeMutex); 2393112872Snjl 2394112872Snjl if (vnode == NULL) 239549011Swpaul return FSSH_B_ERROR; 239698849Sken 2397112872Snjl *_vnode = vnode; 2398112872Snjl return FSSH_B_OK; 239998849Sken} 2400112872Snjl 2401112872Snjl 240249011Swpaulfssh_status_t 240367087Swpaulvfs_get_fs_node_from_path(fssh_fs_volume *volume, const char *path, 240467087Swpaul bool kernel, void **_node) 240549011Swpaul{ 240649011Swpaul TRACE(("vfs_get_fs_node_from_path(volume = %p (%ld), path = \"%s\", " 240749011Swpaul "kernel %d)\n", volume, volume->id, path, kernel)); 240849011Swpaul 240998849Sken KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); 241045386Swpaul if (pathBuffer.InitCheck() != FSSH_B_OK) 241198849Sken return FSSH_B_NO_MEMORY; 241298849Sken 241398849Sken fs_mount *mount; 241498849Sken fssh_status_t status = get_mount(volume->id, &mount); 241598849Sken if (status < FSSH_B_OK) 241698849Sken return status; 241798849Sken 241898849Sken char *buffer = pathBuffer.LockBuffer(); 241998849Sken fssh_strlcpy(buffer, path, pathBuffer.BufferSize()); 242098849Sken 242198849Sken struct vnode *vnode = mount->root_vnode; 242298849Sken 242398849Sken if (buffer[0] == '/') 242498849Sken status = path_to_vnode(buffer, true, &vnode, NULL, true); 242598849Sken else { 242698849Sken inc_vnode_ref_count(vnode); 242798849Sken // vnode_path_to_vnode() releases a reference to the starting vnode 242898849Sken status = vnode_path_to_vnode(vnode, buffer, true, 0, &vnode, NULL); 242998849Sken } 243098849Sken 243198849Sken put_mount(mount); 243298849Sken 243398849Sken if (status < FSSH_B_OK) 243498849Sken return status; 243598849Sken 243698849Sken if (vnode->device != volume->id) { 243798849Sken // wrong mount ID - must not gain access on foreign file system nodes 243898849Sken put_vnode(vnode); 243998849Sken return FSSH_B_BAD_VALUE; 244098849Sken } 244198849Sken 244298849Sken // Use get_vnode() to resolve the cookie for the right layer. 244398849Sken status = ::fssh_get_vnode(volume, vnode->id, _node); 244498849Sken put_vnode(vnode); 244598849Sken 244698849Sken return FSSH_B_OK; 244798849Sken} 244898849Sken 244998849Sken 245098849Sken/** Finds the full path to the file that contains the module \a moduleName, 245198849Sken * puts it into \a pathBuffer, and returns FSSH_B_OK for success. 245298849Sken * If \a pathBuffer was too small, it returns \c FSSH_B_BUFFER_OVERFLOW, 245398849Sken * \c FSSH_B_ENTRY_NOT_FOUNT if no file could be found. 245498849Sken * \a pathBuffer is clobbered in any case and must not be relied on if this 245598849Sken * functions returns unsuccessfully. 245698849Sken */ 245798849Sken 245898849Skenfssh_status_t 245998849Skenvfs_get_module_path(const char *basePath, const char *moduleName, char *pathBuffer, 246098849Sken fssh_size_t bufferSize) 246198849Sken{ 246298849Sken struct vnode *dir, *file; 246398849Sken fssh_status_t status; 246498849Sken fssh_size_t length; 246598849Sken char *path; 246698849Sken 246745386Swpaul if (bufferSize == 0 || fssh_strlcpy(pathBuffer, basePath, bufferSize) >= bufferSize) 246845386Swpaul return FSSH_B_BUFFER_OVERFLOW; 246945386Swpaul 247045386Swpaul status = path_to_vnode(pathBuffer, true, &dir, NULL, true); 247145386Swpaul if (status < FSSH_B_OK) 247245386Swpaul return status; 247345386Swpaul 247445386Swpaul // the path buffer had been clobbered by the above call 247545386Swpaul length = fssh_strlcpy(pathBuffer, basePath, bufferSize); 247645386Swpaul if (pathBuffer[length - 1] != '/') 2477102336Salfred pathBuffer[length++] = '/'; 2478102336Salfred 247945386Swpaul path = pathBuffer + length; 248045386Swpaul bufferSize -= length; 248145386Swpaul 248248597Swpaul while (moduleName) { 248345386Swpaul char *nextPath = fssh_strchr(moduleName, '/'); 248445386Swpaul if (nextPath == NULL) 248545386Swpaul length = fssh_strlen(moduleName); 248645386Swpaul else { 248745386Swpaul length = nextPath - moduleName; 248845386Swpaul nextPath++; 248945386Swpaul } 249045386Swpaul 249145386Swpaul if (length + 1 >= bufferSize) { 249245386Swpaul status = FSSH_B_BUFFER_OVERFLOW; 249345386Swpaul goto err; 249445386Swpaul } 249545386Swpaul 249645386Swpaul fssh_memcpy(path, moduleName, length); 249745386Swpaul path[length] = '\0'; 249845386Swpaul moduleName = nextPath; 249945386Swpaul 250045386Swpaul status = vnode_path_to_vnode(dir, path, true, 0, &file, NULL); 250177058Sphk if (status < FSSH_B_OK) { 250245386Swpaul // vnode_path_to_vnode() has already released the reference to dir 250345386Swpaul return status; 250445386Swpaul } 250598849Sken 250645386Swpaul if (FSSH_S_ISDIR(file->type)) { 250745386Swpaul // goto the next directory 250845386Swpaul path[length] = '/'; 250945386Swpaul path[length + 1] = '\0'; 251045386Swpaul path += length + 1; 251145386Swpaul bufferSize -= length + 1; 251245386Swpaul 251345386Swpaul dir = file; 251448597Swpaul } else if (FSSH_S_ISREG(file->type)) { 251548597Swpaul // it's a file so it should be what we've searched for 251648597Swpaul put_vnode(file); 251748597Swpaul 251848597Swpaul return FSSH_B_OK; 251998849Sken } else { 252098849Sken TRACE(("vfs_get_module_path(): something is strange here: %d...\n", file->type)); 252198849Sken status = FSSH_B_ERROR; 252298849Sken dir = file; 252398849Sken goto err; 252498849Sken } 252598849Sken } 252698849Sken 252798849Sken // if we got here, the moduleName just pointed to a directory, not to 252898849Sken // a real module - what should we do in this case? 252998849Sken status = FSSH_B_ENTRY_NOT_FOUND; 253045386Swpaul 253145386Swpaulerr: 253245386Swpaul put_vnode(dir); 253345386Swpaul return status; 253445386Swpaul} 253545386Swpaul 253645386Swpaul 253745386Swpaul/** \brief Normalizes a given path. 253845386Swpaul * 253948597Swpaul * The path must refer to an existing or non-existing entry in an existing 254048597Swpaul * directory, that is chopping off the leaf component the remaining path must 254148597Swpaul * refer to an existing directory. 254248597Swpaul * 254348597Swpaul * The returned will be canonical in that it will be absolute, will not 254498849Sken * contain any "." or ".." components or duplicate occurrences of '/'s, 254545386Swpaul * and none of the directory components will by symbolic links. 254645386Swpaul * 254745386Swpaul * Any two paths referring to the same entry, will result in the same 254845386Swpaul * normalized path (well, that is pretty much the definition of `normalized', 254945386Swpaul * isn't it :-). 255045386Swpaul * 255145386Swpaul * \param path The path to be normalized. 255245386Swpaul * \param buffer The buffer into which the normalized path will be written. 255345386Swpaul * \param bufferSize The size of \a buffer. 255448597Swpaul * \param kernel \c true, if the IO context of the kernel shall be used, 255548597Swpaul * otherwise that of the team this thread belongs to. Only relevant, 255648597Swpaul * if the path is relative (to get the CWD). 255748597Swpaul * \return \c FSSH_B_OK if everything went fine, another error code otherwise. 255848597Swpaul */ 255998849Sken 256045386Swpaulfssh_status_t 256145386Swpaulvfs_normalize_path(const char *path, char *buffer, fssh_size_t bufferSize, 256298849Sken bool kernel) 256345386Swpaul{ 256445386Swpaul if (!path || !buffer || bufferSize < 1) 256545386Swpaul return FSSH_B_BAD_VALUE; 256645386Swpaul 256758698Sjlemon TRACE(("vfs_normalize_path(`%s')\n", path)); 256858698Sjlemon 256958698Sjlemon // copy the supplied path to the stack, so it can be modified 257058698Sjlemon KPath mutablePathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); 257158698Sjlemon if (mutablePathBuffer.InitCheck() != FSSH_B_OK) 257258698Sjlemon return FSSH_B_NO_MEMORY; 257358698Sjlemon 257445386Swpaul char *mutablePath = mutablePathBuffer.LockBuffer(); 257545386Swpaul if (fssh_strlcpy(mutablePath, path, FSSH_B_PATH_NAME_LENGTH) >= FSSH_B_PATH_NAME_LENGTH) 2576106936Ssam return FSSH_B_NAME_TOO_LONG; 2577106936Ssam 257845386Swpaul // get the dir vnode and the leaf name 2579106936Ssam struct vnode *dirNode; 2580106936Ssam char leaf[FSSH_B_FILE_NAME_LENGTH]; 2581106936Ssam fssh_status_t error = path_to_dir_vnode(mutablePath, &dirNode, leaf, kernel); 258245386Swpaul if (error != FSSH_B_OK) { 258345386Swpaul TRACE(("vfs_normalize_path(): failed to get dir vnode: %s\n", strerror(error))); 258445386Swpaul return error; 258545386Swpaul } 258645386Swpaul 258745386Swpaul // if the leaf is "." or "..", we directly get the correct directory 258845386Swpaul // vnode and ignore the leaf later 258948597Swpaul bool isDir = (fssh_strcmp(leaf, ".") == 0 || fssh_strcmp(leaf, "..") == 0); 259048597Swpaul if (isDir) 259148597Swpaul error = vnode_path_to_vnode(dirNode, leaf, false, 0, &dirNode, NULL); 259245386Swpaul if (error != FSSH_B_OK) { 259345386Swpaul TRACE(("vfs_normalize_path(): failed to get dir vnode for \".\" or \"..\": %s\n", 259445386Swpaul strerror(error))); 259545386Swpaul return error; 2596102336Salfred } 2597102336Salfred 259845386Swpaul // get the directory path 259945386Swpaul error = dir_vnode_to_path(dirNode, buffer, bufferSize); 260045386Swpaul put_vnode(dirNode); 260145386Swpaul if (error < FSSH_B_OK) { 260245386Swpaul TRACE(("vfs_normalize_path(): failed to get dir path: %s\n", strerror(error))); 260345386Swpaul return error; 260445386Swpaul } 260545386Swpaul 260645386Swpaul // append the leaf name 260745386Swpaul if (!isDir) { 260845386Swpaul // insert a directory separator only if this is not the file system root 260945386Swpaul if ((fssh_strcmp(buffer, "/") != 0 261045386Swpaul && fssh_strlcat(buffer, "/", bufferSize) >= bufferSize) 261145386Swpaul || fssh_strlcat(buffer, leaf, bufferSize) >= bufferSize) { 261245386Swpaul return FSSH_B_NAME_TOO_LONG; 261345386Swpaul } 261445386Swpaul } 261545386Swpaul 261645386Swpaul TRACE(("vfs_normalize_path() -> `%s'\n", buffer)); 261745386Swpaul return FSSH_B_OK; 261845386Swpaul} 261945386Swpaul 262045386Swpaul 262145386Swpaulvoid 262245386Swpaulvfs_put_vnode(void *_vnode) 262345386Swpaul{ 262445386Swpaul put_vnode((struct vnode *)_vnode); 262545386Swpaul} 262645386Swpaul 262745386Swpaul 262845386Swpaulfssh_status_t 262945386Swpaulvfs_get_cwd(fssh_mount_id *_mountID, fssh_vnode_id *_vnodeID) 263045386Swpaul{ 263145386Swpaul // Get current working directory from io context 263245386Swpaul struct io_context *context = get_current_io_context(false); 263345386Swpaul fssh_status_t status = FSSH_B_OK; 263445386Swpaul 263548011Swpaul fssh_mutex_lock(&context->io_mutex); 263645386Swpaul 263745386Swpaul if (context->cwd != NULL) { 263845386Swpaul *_mountID = context->cwd->device; 263945386Swpaul *_vnodeID = context->cwd->id; 264045386Swpaul } else 264145386Swpaul status = FSSH_B_ERROR; 264245386Swpaul 264345386Swpaul fssh_mutex_unlock(&context->io_mutex); 264445386Swpaul return status; 264545386Swpaul} 2646102336Salfred 2647102336Salfred 264845386Swpaulfssh_status_t 264945386Swpaulvfs_get_file_map(void *_vnode, fssh_off_t offset, fssh_size_t size, 265045386Swpaul fssh_file_io_vec *vecs, fssh_size_t *_count) 265145386Swpaul{ 265245386Swpaul struct vnode *vnode = (struct vnode *)_vnode; 265345386Swpaul 265467087Swpaul FUNCTION(("vfs_get_file_map: vnode %p, vecs %p, offset %lld, size = %u\n", vnode, vecs, offset, (unsigned)size)); 265545386Swpaul 265645386Swpaul return FS_CALL(vnode, get_file_map, offset, size, vecs, _count); 265798849Sken} 265845386Swpaul 265945386Swpaul 266067087Swpaulfssh_status_t 266167087Swpaulvfs_stat_vnode(void *_vnode, struct fssh_stat *stat) 266245386Swpaul{ 266367087Swpaul struct vnode *vnode = (struct vnode *)_vnode; 266498849Sken 266545386Swpaul fssh_status_t status = FS_CALL(vnode, read_stat, stat); 266645386Swpaul 266745386Swpaul // fill in the st_dev and st_ino fields 266845386Swpaul if (status == FSSH_B_OK) { 266945386Swpaul stat->fssh_st_dev = vnode->device; 267045386Swpaul stat->fssh_st_ino = vnode->id; 267145386Swpaul } 267245386Swpaul 267345386Swpaul return status; 267445386Swpaul} 267545386Swpaul 267645386Swpaul 267745386Swpaulfssh_status_t 267845386Swpaulvfs_get_vnode_name(void *_vnode, char *name, fssh_size_t nameSize) 267945386Swpaul{ 268045386Swpaul return get_vnode_name((struct vnode *)_vnode, NULL, name, nameSize); 268145386Swpaul} 268245386Swpaul 268345386Swpaul 268445386Swpaulfssh_status_t 268567087Swpaulvfs_entry_ref_to_path(fssh_dev_t device, fssh_ino_t inode, const char *leaf, 268667087Swpaul char *path, fssh_size_t pathLength) 268745386Swpaul{ 268845386Swpaul struct vnode *vnode; 268945386Swpaul fssh_status_t status; 2690102336Salfred 2691102336Salfred // filter invalid leaf names 269245386Swpaul if (leaf != NULL && (leaf[0] == '\0' || fssh_strchr(leaf, '/'))) 269345386Swpaul return FSSH_B_BAD_VALUE; 269445386Swpaul 269545386Swpaul // get the vnode matching the dir's node_ref 269645386Swpaul if (leaf && (fssh_strcmp(leaf, ".") == 0 || fssh_strcmp(leaf, "..") == 0)) { 269745386Swpaul // special cases "." and "..": we can directly get the vnode of the 269845386Swpaul // referenced directory 269945386Swpaul status = entry_ref_to_vnode(device, inode, leaf, &vnode); 270045386Swpaul leaf = NULL; 270145386Swpaul } else 270245386Swpaul status = get_vnode(device, inode, &vnode, false); 270345386Swpaul if (status < FSSH_B_OK) 270445386Swpaul return status; 270545386Swpaul 270645386Swpaul // get the directory path 270745386Swpaul status = dir_vnode_to_path(vnode, path, pathLength); 270845386Swpaul put_vnode(vnode); 270945386Swpaul // we don't need the vnode anymore 271045386Swpaul if (status < FSSH_B_OK) 271145386Swpaul return status; 2712102336Salfred 2713102336Salfred // append the leaf name 271445386Swpaul if (leaf) { 271545386Swpaul // insert a directory separator if this is not the file system root 271645386Swpaul if ((fssh_strcmp(path, "/") && fssh_strlcat(path, "/", pathLength) 271745386Swpaul >= pathLength) 271845386Swpaul || fssh_strlcat(path, leaf, pathLength) >= pathLength) { 271945386Swpaul return FSSH_B_NAME_TOO_LONG; 272048011Swpaul } 272158698Sjlemon } 2722106936Ssam 272345386Swpaul return FSSH_B_OK; 272445386Swpaul} 272545386Swpaul 272645386Swpaul 272758698Sjlemon/** If the given descriptor locked its vnode, that lock will be released. 272858698Sjlemon */ 272958698Sjlemon 273058698Sjlemonvoid 273158698Sjlemonvfs_unlock_vnode_if_locked(struct file_descriptor *descriptor) 273258698Sjlemon{ 273358698Sjlemon struct vnode *vnode = fd_vnode(descriptor); 273458698Sjlemon 273558698Sjlemon if (vnode != NULL && vnode->mandatory_locked_by == descriptor) 273658698Sjlemon vnode->mandatory_locked_by = NULL; 2737106936Ssam} 2738106936Ssam 2739106936Ssam 274045386Swpaul/** Closes all file descriptors of the specified I/O context that 274145386Swpaul * don't have the FSSH_O_CLOEXEC flag set. 274245386Swpaul */ 274345386Swpaul 274445386Swpaulvoid 274545386Swpaulvfs_exec_io_context(void *_context) 274645386Swpaul{ 274745386Swpaul struct io_context *context = (struct io_context *)_context; 274845386Swpaul uint32_t i; 274945386Swpaul 275045386Swpaul for (i = 0; i < context->table_size; i++) { 275145386Swpaul fssh_mutex_lock(&context->io_mutex); 275245386Swpaul 275345386Swpaul struct file_descriptor *descriptor = context->fds[i]; 275445386Swpaul bool remove = false; 275545386Swpaul 275645386Swpaul if (descriptor != NULL && fd_close_on_exec(context, i)) { 275745386Swpaul context->fds[i] = NULL; 275845386Swpaul context->num_used_fds--; 275945386Swpaul 276045386Swpaul remove = true; 276145386Swpaul } 276245386Swpaul 276345386Swpaul fssh_mutex_unlock(&context->io_mutex); 276445386Swpaul 276545386Swpaul if (remove) { 276645386Swpaul close_fd(descriptor); 276758698Sjlemon put_fd(descriptor); 276883115Sbrooks } 2769106936Ssam } 277045386Swpaul} 2771106936Ssam 277245386Swpaul 277345386Swpaul/** Sets up a new io_control structure, and inherits the properties 277445386Swpaul * of the parent io_control if it is given. 277583115Sbrooks */ 277648011Swpaul 277748011Swpaulvoid * 277848011Swpaulvfs_new_io_context(void *_parentContext) 277948011Swpaul{ 278048011Swpaul fssh_size_t tableSize; 278148011Swpaul struct io_context *context; 278245386Swpaul struct io_context *parentContext; 278345386Swpaul 278448011Swpaul context = (io_context *)malloc(sizeof(struct io_context)); 278545386Swpaul if (context == NULL) 278645386Swpaul return NULL; 278745386Swpaul 278845386Swpaul fssh_memset(context, 0, sizeof(struct io_context)); 278945386Swpaul 279045386Swpaul parentContext = (struct io_context *)_parentContext; 279146177Swpaul if (parentContext) 279246177Swpaul tableSize = parentContext->table_size; 279346177Swpaul else 279445386Swpaul tableSize = DEFAULT_FD_TABLE_SIZE; 279545386Swpaul 279698849Sken // allocate space for FDs and their close-on-exec flag 279745386Swpaul context->fds = (file_descriptor **)malloc(sizeof(struct file_descriptor *) * tableSize 279845386Swpaul + (tableSize + 7) / 8); 279947458Swpaul if (context->fds == NULL) { 280048011Swpaul free(context); 280145386Swpaul return NULL; 280245386Swpaul } 280345386Swpaul 280445386Swpaul fssh_memset(context->fds, 0, sizeof(struct file_descriptor *) * tableSize 280545386Swpaul + (tableSize + 7) / 8); 280645386Swpaul context->fds_close_on_exec = (uint8_t *)(context->fds + tableSize); 280745386Swpaul 280845386Swpaul fssh_mutex_init(&context->io_mutex, "I/O context"); 280945386Swpaul 281045386Swpaul // Copy all parent files which don't have the FSSH_O_CLOEXEC flag set 2811102336Salfred 2812102336Salfred if (parentContext) { 281345386Swpaul fssh_size_t i; 281445386Swpaul 281545386Swpaul fssh_mutex_lock(&parentContext->io_mutex); 281645386Swpaul 281745386Swpaul context->cwd = parentContext->cwd; 281845386Swpaul if (context->cwd) 281945386Swpaul inc_vnode_ref_count(context->cwd); 282067087Swpaul 282145386Swpaul for (i = 0; i < tableSize; i++) { 282245386Swpaul struct file_descriptor *descriptor = parentContext->fds[i]; 282345386Swpaul 282445386Swpaul if (descriptor != NULL && !fd_close_on_exec(parentContext, i)) { 282545386Swpaul context->fds[i] = descriptor; 282645386Swpaul context->num_used_fds++; 282745386Swpaul fssh_atomic_add(&descriptor->ref_count, 1); 282845386Swpaul fssh_atomic_add(&descriptor->open_count, 1); 282945386Swpaul } 283058698Sjlemon } 283158698Sjlemon 283258698Sjlemon fssh_mutex_unlock(&parentContext->io_mutex); 283358698Sjlemon } else { 283458698Sjlemon context->cwd = sRoot; 283558698Sjlemon 283658698Sjlemon if (context->cwd) 283758698Sjlemon inc_vnode_ref_count(context->cwd); 283858698Sjlemon } 283958698Sjlemon 284058698Sjlemon context->table_size = tableSize; 284158698Sjlemon 284258698Sjlemon return context; 284358698Sjlemon} 284458698Sjlemon 284558698Sjlemon 284658698Sjlemonfssh_status_t 284758698Sjlemonvfs_free_io_context(void *_ioContext) 284845386Swpaul{ 284945386Swpaul struct io_context *context = (struct io_context *)_ioContext; 285045386Swpaul uint32_t i; 285145386Swpaul 285245386Swpaul if (context->cwd) 285345386Swpaul dec_vnode_ref_count(context->cwd, false); 285445386Swpaul 285545386Swpaul fssh_mutex_lock(&context->io_mutex); 285645386Swpaul 285745386Swpaul for (i = 0; i < context->table_size; i++) { 285845386Swpaul if (struct file_descriptor *descriptor = context->fds[i]) { 285945386Swpaul close_fd(descriptor); 286045386Swpaul put_fd(descriptor); 286145386Swpaul } 2862106936Ssam } 286345386Swpaul 286445386Swpaul fssh_mutex_destroy(&context->io_mutex); 286545386Swpaul 286645386Swpaul free(context->fds); 286745386Swpaul free(context); 286845386Swpaul 286945386Swpaul return FSSH_B_OK; 287045386Swpaul} 287145386Swpaul 287267087Swpaul 287345386Swpaulfssh_status_t 287445386Swpaulvfs_init(kernel_args *args) 287545386Swpaul{ 287645386Swpaul sVnodeTable = hash_init(VNODE_HASH_TABLE_SIZE, fssh_offsetof(struct vnode, next), 2877102336Salfred &vnode_compare, &vnode_hash); 2878102336Salfred if (sVnodeTable == NULL) 287945386Swpaul fssh_panic("vfs_init: error creating vnode hash table\n"); 288045386Swpaul 288145386Swpaul list_init_etc(&sUnusedVnodeList, fssh_offsetof(struct vnode, unused_link)); 288245386Swpaul 288345386Swpaul sMountsTable = hash_init(MOUNTS_HASH_TABLE_SIZE, fssh_offsetof(struct fs_mount, next), 288445386Swpaul &mount_compare, &mount_hash); 288545386Swpaul if (sMountsTable == NULL) 288667087Swpaul fssh_panic("vfs_init: error creating mounts hash table\n"); 288745386Swpaul 288845386Swpaul sRoot = NULL; 288945386Swpaul 289067087Swpaul fssh_mutex_init(&sFileSystemsMutex, "vfs_lock"); 289145386Swpaul fssh_recursive_lock_init(&sMountOpLock, "vfs_mount_op_lock"); 289245386Swpaul fssh_mutex_init(&sMountMutex, "vfs_mount_lock"); 289345386Swpaul fssh_mutex_init(&sVnodeCoveredByMutex, "vfs_vnode_covered_by_lock"); 289467087Swpaul fssh_mutex_init(&sVnodeMutex, "vfs_vnode_lock"); 289545386Swpaul 289645386Swpaul if (block_cache_init() != FSSH_B_OK) 289745386Swpaul return FSSH_B_ERROR; 289845386Swpaul 289945386Swpaul return file_cache_init(); 290045386Swpaul} 290145386Swpaul 290245386Swpaul 290345386Swpaul// #pragma mark - 290445386Swpaul// The filetype-dependent implementations (fd_ops + open/create/rename/remove, ...) 290545386Swpaul 290645386Swpaul 290745386Swpaul/** Calls fs_open() on the given vnode and returns a new 290845386Swpaul * file descriptor for it 290945386Swpaul */ 291045386Swpaul 291145386Swpaulstatic int 291245386Swpaulcreate_vnode(struct vnode *directory, const char *name, int openMode, int perms, bool kernel) 291345386Swpaul{ 291445386Swpaul struct vnode *vnode; 291545386Swpaul void *cookie; 291645386Swpaul fssh_vnode_id newID; 291745386Swpaul int status; 291845386Swpaul 291945386Swpaul if (!HAS_FS_CALL(directory, create)) 292045386Swpaul return FSSH_EROFS; 292145386Swpaul 292245386Swpaul status = FS_CALL(directory, create, name, openMode, perms, &cookie, &newID); 292345386Swpaul if (status < FSSH_B_OK) 292445386Swpaul return status; 292545386Swpaul 292645386Swpaul fssh_mutex_lock(&sVnodeMutex); 292745386Swpaul vnode = lookup_vnode(directory->device, newID); 292845386Swpaul fssh_mutex_unlock(&sVnodeMutex); 292945386Swpaul 293045386Swpaul if (vnode == NULL) { 293145386Swpaul fssh_dprintf("vfs: fs_create() returned success but there is no vnode!"); 293245386Swpaul return FSSH_EINVAL; 293345386Swpaul } 293445386Swpaul 293545386Swpaul if ((status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel)) >= 0) 293645386Swpaul return status; 293745386Swpaul 293845386Swpaul // something went wrong, clean up 293945386Swpaul 294045386Swpaul FS_CALL(vnode, close, cookie); 294145386Swpaul FS_CALL(vnode, free_cookie, cookie); 294245386Swpaul put_vnode(vnode); 294345386Swpaul 294445386Swpaul FS_CALL(directory, unlink, name); 294545386Swpaul 294645386Swpaul return status; 294745386Swpaul} 294845386Swpaul 294945386Swpaul 295045386Swpaul/** Calls fs_open() on the given vnode and returns a new 295145386Swpaul * file descriptor for it 295245386Swpaul */ 295345386Swpaul 295445386Swpaulstatic int 295545386Swpaulopen_vnode(struct vnode *vnode, int openMode, bool kernel) 295645386Swpaul{ 295745386Swpaul void *cookie; 295845386Swpaul int status; 295945386Swpaul 296045386Swpaul status = FS_CALL(vnode, open, openMode, &cookie); 296145386Swpaul if (status < 0) 296245386Swpaul return status; 296345386Swpaul 296445386Swpaul status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel); 296545386Swpaul if (status < 0) { 296645386Swpaul FS_CALL(vnode, close, cookie); 296745386Swpaul FS_CALL(vnode, free_cookie, cookie); 296845386Swpaul } 296945386Swpaul return status; 297045386Swpaul} 297145386Swpaul 297245386Swpaul 297345386Swpaul/** Calls fs open_dir() on the given vnode and returns a new 297445386Swpaul * file descriptor for it 297545386Swpaul */ 297645386Swpaul 297745386Swpaulstatic int 297845386Swpaulopen_dir_vnode(struct vnode *vnode, bool kernel) 297945386Swpaul{ 298045386Swpaul void *cookie; 298145386Swpaul int status; 298245386Swpaul 298345386Swpaul status = FS_CALL(vnode, open_dir, &cookie); 298445386Swpaul if (status < FSSH_B_OK) 298545386Swpaul return status; 298645386Swpaul 2987102336Salfred // file is opened, create a fd 2988102336Salfred status = get_new_fd(FDTYPE_DIR, NULL, vnode, cookie, 0, kernel); 298945386Swpaul if (status >= 0) 299045386Swpaul return status; 299145386Swpaul 299245386Swpaul FS_CALL(vnode, close_dir, cookie); 299345386Swpaul FS_CALL(vnode, free_dir_cookie, cookie); 299498849Sken 299545386Swpaul return status; 299645386Swpaul} 299745386Swpaul 299845386Swpaul 299945386Swpaul/** Calls fs open_attr_dir() on the given vnode and returns a new 300045386Swpaul * file descriptor for it. 300145386Swpaul * Used by attr_dir_open(), and attr_dir_open_fd(). 300298849Sken */ 300398849Sken 300445386Swpaulstatic int 300545386Swpaulopen_attr_dir_vnode(struct vnode *vnode, bool kernel) 300698849Sken{ 300798849Sken void *cookie; 300898849Sken int status; 300998849Sken 301098849Sken if (!HAS_FS_CALL(vnode, open_attr_dir)) 301198849Sken return FSSH_EOPNOTSUPP; 301298849Sken 301398849Sken status = FS_CALL(vnode, open_attr_dir, &cookie); 301498849Sken if (status < 0) 301598849Sken return status; 301698849Sken 301798849Sken // file is opened, create a fd 301898849Sken status = get_new_fd(FDTYPE_ATTR_DIR, NULL, vnode, cookie, 0, kernel); 301998849Sken if (status >= 0) 302098849Sken return status; 302198849Sken 302245386Swpaul FS_CALL(vnode, close_attr_dir, cookie); 302398849Sken FS_CALL(vnode, free_attr_dir_cookie, cookie); 302445386Swpaul 302598849Sken return status; 302698849Sken} 302798849Sken 302898849Sken 302998849Skenstatic int 303098849Skenfile_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, int perms, bool kernel) 303198849Sken{ 303245386Swpaul struct vnode *directory; 303398849Sken int status; 303445386Swpaul 303545386Swpaul FUNCTION(("file_create_entry_ref: name = '%s', omode %x, perms %d, kernel %d\n", name, openMode, perms, kernel)); 303645386Swpaul 303745386Swpaul // get directory to put the new file in 303845386Swpaul status = get_vnode(mountID, directoryID, &directory, false); 303995673Sphk if (status < FSSH_B_OK) 304098849Sken return status; 304198849Sken 304298849Sken status = create_vnode(directory, name, openMode, perms, kernel); 304398849Sken put_vnode(directory); 304498849Sken 304598849Sken return status; 304645386Swpaul} 304798849Sken 304845386Swpaul 304963699Swpaulstatic int 305063699Swpaulfile_create(int fd, char *path, int openMode, int perms, bool kernel) 305163699Swpaul{ 305245386Swpaul char name[FSSH_B_FILE_NAME_LENGTH]; 305345386Swpaul struct vnode *directory; 305445386Swpaul int status; 305545386Swpaul 305645386Swpaul FUNCTION(("file_create: path '%s', omode %x, perms %d, kernel %d\n", path, openMode, perms, kernel)); 305763699Swpaul 305863699Swpaul // get directory to put the new file in 305998849Sken status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel); 306098849Sken if (status < 0) 306198849Sken return status; 306298849Sken 306398849Sken status = create_vnode(directory, name, openMode, perms, kernel); 306498849Sken 306545386Swpaul put_vnode(directory); 306698849Sken return status; 306763699Swpaul} 306863699Swpaul 306945386Swpaul 307045386Swpaulstatic int 307145386Swpaulfile_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, bool kernel) 307245386Swpaul{ 307345386Swpaul struct vnode *vnode; 307445386Swpaul int status; 307545386Swpaul 307645386Swpaul if (name == NULL || *name == '\0') 307745386Swpaul return FSSH_B_BAD_VALUE; 307845386Swpaul 307945386Swpaul FUNCTION(("file_open_entry_ref(ref = (%ld, %Ld, %s), openMode = %d)\n", 308045386Swpaul mountID, directoryID, name, openMode)); 308145386Swpaul 308245386Swpaul // get the vnode matching the entry_ref 308345386Swpaul status = entry_ref_to_vnode(mountID, directoryID, name, &vnode); 308445386Swpaul if (status < FSSH_B_OK) 308545386Swpaul return status; 308645386Swpaul 308745386Swpaul status = open_vnode(vnode, openMode, kernel); 308845386Swpaul if (status < FSSH_B_OK) 3089102336Salfred put_vnode(vnode); 3090102336Salfred 309145386Swpaul return status; 309245386Swpaul} 309345386Swpaul 309445386Swpaul 309563699Swpaulstatic int 309645386Swpaulfile_open(int fd, char *path, int openMode, bool kernel) 309745386Swpaul{ 309845386Swpaul int status = FSSH_B_OK; 309945386Swpaul bool traverse = ((openMode & FSSH_O_NOTRAVERSE) == 0); 310045386Swpaul 310145386Swpaul FUNCTION(("file_open: fd: %d, entry path = '%s', omode %d, kernel %d\n", 310245386Swpaul fd, path, openMode, kernel)); 310345386Swpaul 310445386Swpaul // get the vnode matching the vnode + path combination 310545386Swpaul struct vnode *vnode = NULL; 310645386Swpaul fssh_vnode_id parentID; 310763699Swpaul status = fd_and_path_to_vnode(fd, path, traverse, &vnode, &parentID, kernel); 310863699Swpaul if (status != FSSH_B_OK) 310963699Swpaul return status; 311095673Sphk 311163699Swpaul // open the vnode 311263699Swpaul status = open_vnode(vnode, openMode, kernel); 311363699Swpaul // put only on error -- otherwise our reference was transferred to the FD 311463699Swpaul if (status < FSSH_B_OK) 311563699Swpaul put_vnode(vnode); 311663699Swpaul 311763699Swpaul return status; 311845386Swpaul} 311963699Swpaul 312063699Swpaul 312163699Swpaulstatic fssh_status_t 312263699Swpaulfile_close(struct file_descriptor *descriptor) 312363699Swpaul{ 312463699Swpaul struct vnode *vnode = descriptor->u.vnode; 312563699Swpaul fssh_status_t status = FSSH_B_OK; 312663699Swpaul 312763699Swpaul FUNCTION(("file_close(descriptor = %p)\n", descriptor)); 312863699Swpaul 312963699Swpaul if (HAS_FS_CALL(vnode, close)) 313045386Swpaul status = FS_CALL(vnode, close, descriptor->cookie); 313145386Swpaul 313245386Swpaul return status; 313345386Swpaul} 313445386Swpaul 313545386Swpaul 313645386Swpaulstatic void 313745386Swpaulfile_free_fd(struct file_descriptor *descriptor) 313845386Swpaul{ 3139102336Salfred struct vnode *vnode = descriptor->u.vnode; 3140102336Salfred 314145386Swpaul if (vnode != NULL) { 314245386Swpaul FS_CALL(vnode, free_cookie, descriptor->cookie); 314345386Swpaul put_vnode(vnode); 314445386Swpaul } 314545386Swpaul} 314645386Swpaul 314783630Sjlemon 314845386Swpaulstatic fssh_status_t 314945386Swpaulfile_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length) 315067087Swpaul{ 315145386Swpaul struct vnode *vnode = descriptor->u.vnode; 315245386Swpaul 315345386Swpaul FUNCTION(("file_read: buf %p, pos %Ld, len %p = %ld\n", buffer, pos, length, *length)); 315445386Swpaul return FS_CALL(vnode, read, descriptor->cookie, pos, buffer, length); 315545386Swpaul} 315645386Swpaul 315745386Swpaul 315845386Swpaulstatic fssh_status_t 315945386Swpaulfile_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length) 316045386Swpaul{ 316145386Swpaul struct vnode *vnode = descriptor->u.vnode; 316245386Swpaul 316345386Swpaul FUNCTION(("file_write: buf %p, pos %Ld, len %p\n", buffer, pos, length)); 316445386Swpaul return FS_CALL(vnode, write, descriptor->cookie, pos, buffer, length); 316545386Swpaul} 316645386Swpaul 316745386Swpaul 316845386Swpaulstatic fssh_off_t 316945386Swpaulfile_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType) 317045386Swpaul{ 317145386Swpaul fssh_off_t offset; 317245386Swpaul 317345386Swpaul FUNCTION(("file_seek(pos = %Ld, seekType = %d)\n", pos, seekType)); 317445386Swpaul // ToDo: seek should fail for pipes and FIFOs... 317545386Swpaul 317645386Swpaul switch (seekType) { 317745386Swpaul case FSSH_SEEK_SET: 317845386Swpaul offset = 0; 317945386Swpaul break; 318045386Swpaul case FSSH_SEEK_CUR: 318145386Swpaul offset = descriptor->pos; 318245386Swpaul break; 318345386Swpaul case FSSH_SEEK_END: 318445386Swpaul { 318545386Swpaul struct vnode *vnode = descriptor->u.vnode; 318645386Swpaul struct fssh_stat stat; 318745386Swpaul fssh_status_t status; 318845386Swpaul 318945386Swpaul if (!HAS_FS_CALL(vnode, read_stat)) 319045386Swpaul return FSSH_EOPNOTSUPP; 319145386Swpaul 319245386Swpaul status = FS_CALL(vnode, read_stat, &stat); 319345386Swpaul if (status < FSSH_B_OK) 319445386Swpaul return status; 319545386Swpaul 319645386Swpaul offset = stat.fssh_st_size; 319745386Swpaul break; 319845386Swpaul } 319945386Swpaul default: 320045386Swpaul return FSSH_B_BAD_VALUE; 320145386Swpaul } 320283630Sjlemon 320383630Sjlemon // assumes fssh_off_t is 64 bits wide 320483630Sjlemon if (offset > 0 && LLONG_MAX - offset < pos) 320583630Sjlemon return FSSH_EOVERFLOW; 320683630Sjlemon 320783630Sjlemon pos += offset; 320883630Sjlemon if (pos < 0) 320983630Sjlemon return FSSH_B_BAD_VALUE; 321083630Sjlemon 321183630Sjlemon return descriptor->pos = pos; 321283630Sjlemon} 321383630Sjlemon 321445386Swpaul 3215106936Ssamstatic fssh_status_t 321645386Swpauldir_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, int perms, bool kernel) 321745386Swpaul{ 321845386Swpaul struct vnode *vnode; 321967087Swpaul fssh_status_t status; 322045386Swpaul 322145386Swpaul if (name == NULL || *name == '\0') 322245386Swpaul return FSSH_B_BAD_VALUE; 322345386Swpaul 322498849Sken FUNCTION(("dir_create_entry_ref(dev = %ld, ino = %Ld, name = '%s', perms = %d)\n", mountID, parentID, name, perms)); 322598849Sken 322698849Sken status = get_vnode(mountID, parentID, &vnode, kernel); 322798849Sken if (status < FSSH_B_OK) 322898849Sken return status; 322998849Sken 323098849Sken if (HAS_FS_CALL(vnode, create_dir)) 323198849Sken status = FS_CALL(vnode, create_dir, name, perms); 323298849Sken else 323398849Sken status = FSSH_EROFS; 323498849Sken 323598849Sken put_vnode(vnode); 323698849Sken return status; 323798849Sken} 323898849Sken 323998849Sken 324098849Skenstatic fssh_status_t 324198849Skendir_create(int fd, char *path, int perms, bool kernel) 324298849Sken{ 324398849Sken char filename[FSSH_B_FILE_NAME_LENGTH]; 324498849Sken struct vnode *vnode; 324598849Sken fssh_status_t status; 324698849Sken 324798849Sken FUNCTION(("dir_create: path '%s', perms %d, kernel %d\n", path, perms, kernel)); 324898849Sken 324998849Sken status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel); 325098849Sken if (status < 0) 325198849Sken return status; 325298849Sken 325398849Sken if (HAS_FS_CALL(vnode, create_dir)) 325498849Sken status = FS_CALL(vnode, create_dir, filename, perms); 325598849Sken else 325698849Sken status = FSSH_EROFS; 325798849Sken 325898849Sken put_vnode(vnode); 325998849Sken return status; 326098849Sken} 326198849Sken 326298849Sken 326398849Skenstatic int 326498849Skendir_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, bool kernel) 326598849Sken{ 326698849Sken struct vnode *vnode; 326798849Sken int status; 326898849Sken 326998849Sken FUNCTION(("dir_open_entry_ref()\n")); 327098849Sken 327198849Sken if (name && *name == '\0') 327298849Sken return FSSH_B_BAD_VALUE; 327398849Sken 327498849Sken // get the vnode matching the entry_ref/node_ref 327598849Sken if (name) 327698849Sken status = entry_ref_to_vnode(mountID, parentID, name, &vnode); 327798849Sken else 327898849Sken status = get_vnode(mountID, parentID, &vnode, false); 327998849Sken if (status < FSSH_B_OK) 328098849Sken return status; 328198849Sken 328298849Sken status = open_dir_vnode(vnode, kernel); 328398849Sken if (status < FSSH_B_OK) 328498849Sken put_vnode(vnode); 328598849Sken 328698849Sken return status; 328798849Sken} 328898849Sken 328998849Sken 329098849Skenstatic int 329198849Skendir_open(int fd, char *path, bool kernel) 329298849Sken{ 329398849Sken int status = FSSH_B_OK; 329498849Sken 329598849Sken FUNCTION(("dir_open: fd: %d, entry path = '%s', kernel %d\n", fd, path, kernel)); 329698849Sken 329798849Sken // get the vnode matching the vnode + path combination 329898849Sken struct vnode *vnode = NULL; 329998849Sken fssh_vnode_id parentID; 330098849Sken status = fd_and_path_to_vnode(fd, path, true, &vnode, &parentID, kernel); 330198849Sken if (status != FSSH_B_OK) 330298849Sken return status; 330398849Sken 330498849Sken // open the dir 330598849Sken status = open_dir_vnode(vnode, kernel); 330698849Sken if (status < FSSH_B_OK) 330798849Sken put_vnode(vnode); 330898849Sken 330998849Sken return status; 331098849Sken} 331198849Sken 331298849Sken 331398849Skenstatic fssh_status_t 331498849Skendir_close(struct file_descriptor *descriptor) 331598849Sken{ 331698849Sken struct vnode *vnode = descriptor->u.vnode; 331798849Sken 331898849Sken FUNCTION(("dir_close(descriptor = %p)\n", descriptor)); 331998849Sken 332098849Sken if (HAS_FS_CALL(vnode, close_dir)) 332198849Sken return FS_CALL(vnode, close_dir, descriptor->cookie); 332298849Sken 332398849Sken return FSSH_B_OK; 332498849Sken} 332598849Sken 332698849Sken 332798849Skenstatic void 332898849Skendir_free_fd(struct file_descriptor *descriptor) 332998849Sken{ 333098849Sken struct vnode *vnode = descriptor->u.vnode; 333198849Sken 333298849Sken if (vnode != NULL) { 333398849Sken FS_CALL(vnode, free_dir_cookie, descriptor->cookie); 333498849Sken put_vnode(vnode); 333598849Sken } 333698849Sken} 333798849Sken 333898849Sken 333998849Skenstatic fssh_status_t 334098849Skendir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer, 334198849Sken fssh_size_t bufferSize, uint32_t *_count) 334298849Sken{ 334398849Sken return dir_read(descriptor->u.vnode, descriptor->cookie, buffer, bufferSize, _count); 334498849Sken} 334598849Sken 334698849Sken 334798849Skenstatic void 334898849Skenfix_dirent(struct vnode *parent, struct fssh_dirent *entry) 334998849Sken{ 335098849Sken // set d_pdev and d_pino 335198849Sken entry->d_pdev = parent->device; 335298849Sken entry->d_pino = parent->id; 335398849Sken 335498849Sken // If this is the ".." entry and the directory is the root of a FS, 335598849Sken // we need to replace d_dev and d_ino with the actual values. 335698849Sken if (fssh_strcmp(entry->d_name, "..") == 0 335798849Sken && parent->mount->root_vnode == parent 335898849Sken && parent->mount->covers_vnode) { 335998849Sken inc_vnode_ref_count(parent); 336098849Sken // vnode_path_to_vnode() puts the node 336198849Sken 336298849Sken // ".." is guaranteed to to be clobbered by this call 336398849Sken struct vnode *vnode; 336498849Sken fssh_status_t status = vnode_path_to_vnode(parent, (char*)"..", false, 336598849Sken 0, &vnode, NULL); 336698849Sken 336798849Sken if (status == FSSH_B_OK) { 336898849Sken entry->d_dev = vnode->device; 336998849Sken entry->d_ino = vnode->id; 337098849Sken } 337198849Sken } else { 337298849Sken // resolve mount points 337398849Sken struct vnode *vnode = NULL; 337498849Sken fssh_status_t status = get_vnode(entry->d_dev, entry->d_ino, &vnode, false); 337598849Sken if (status != FSSH_B_OK) 337698849Sken return; 337798849Sken 337898849Sken fssh_mutex_lock(&sVnodeCoveredByMutex); 337998849Sken if (vnode->covered_by) { 338098849Sken entry->d_dev = vnode->covered_by->device; 338198849Sken entry->d_ino = vnode->covered_by->id; 338298849Sken } 338398849Sken fssh_mutex_unlock(&sVnodeCoveredByMutex); 338498849Sken 338598849Sken put_vnode(vnode); 338698849Sken } 338798849Sken} 338898849Sken 338998849Sken 339098849Skenstatic fssh_status_t 339198849Skendir_read(struct vnode *vnode, void *cookie, struct fssh_dirent *buffer, 339298849Sken fssh_size_t bufferSize, uint32_t *_count) 339398849Sken{ 339498849Sken if (!HAS_FS_CALL(vnode, read_dir)) 339598849Sken return FSSH_EOPNOTSUPP; 339698849Sken 339798849Sken fssh_status_t error = FS_CALL(vnode, read_dir,cookie,buffer,bufferSize,_count); 339898849Sken if (error != FSSH_B_OK) 339998849Sken return error; 340098849Sken 340198849Sken // we need to adjust the read dirents 340298849Sken if (*_count > 0) { 340398849Sken // XXX: Currently reading only one dirent is supported. Make this a loop! 340498849Sken fix_dirent(vnode, buffer); 340598849Sken } 340698849Sken 340798849Sken return error; 340898849Sken} 340998849Sken 341098849Sken 341198849Skenstatic fssh_status_t 341298849Skendir_rewind(struct file_descriptor *descriptor) 341398849Sken{ 341498849Sken struct vnode *vnode = descriptor->u.vnode; 341598849Sken 341698849Sken if (HAS_FS_CALL(vnode, rewind_dir)) 341798849Sken return FS_CALL(vnode, rewind_dir,descriptor->cookie); 341898849Sken 341998849Sken return FSSH_EOPNOTSUPP; 342098849Sken} 342198849Sken 342298849Sken 342398849Skenstatic fssh_status_t 342498849Skendir_remove(int fd, char *path, bool kernel) 342598849Sken{ 342698849Sken char name[FSSH_B_FILE_NAME_LENGTH]; 342798849Sken struct vnode *directory; 342898849Sken fssh_status_t status; 342998849Sken 343098849Sken if (path != NULL) { 343198849Sken // we need to make sure our path name doesn't stop with "/", ".", or ".." 343298849Sken char *lastSlash = fssh_strrchr(path, '/'); 343398849Sken if (lastSlash != NULL) { 343498849Sken char *leaf = lastSlash + 1; 343598849Sken if (!fssh_strcmp(leaf, "..")) 343698849Sken return FSSH_B_NOT_ALLOWED; 343798849Sken 343898849Sken // omit multiple slashes 343998849Sken while (lastSlash > path && lastSlash[-1] == '/') { 344098849Sken lastSlash--; 344198849Sken } 344298849Sken 344398849Sken if (!leaf[0] 344498849Sken || !fssh_strcmp(leaf, ".")) { 344598849Sken // "name/" -> "name", or "name/." -> "name" 344698849Sken lastSlash[0] = '\0'; 344798849Sken } 344898849Sken } else if (!fssh_strcmp(path, "..")) 344998849Sken return FSSH_B_NOT_ALLOWED; 345098849Sken } 345198849Sken 345298849Sken status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel); 345398849Sken if (status < FSSH_B_OK) 345498849Sken return status; 345598849Sken 345698849Sken if (HAS_FS_CALL(directory, remove_dir)) { 345798849Sken status = FS_CALL(directory, remove_dir, name); 345898849Sken } else 345998849Sken status = FSSH_EROFS; 346098849Sken 346198849Sken put_vnode(directory); 346298849Sken return status; 346398849Sken} 346498849Sken 346598849Sken 346698849Skenstatic fssh_status_t 346798849Skencommon_ioctl(struct file_descriptor *descriptor, uint32_t op, void *buffer, 346898849Sken fssh_size_t length) 346998849Sken{ 347098849Sken struct vnode *vnode = descriptor->u.vnode; 347198849Sken 347298849Sken if (HAS_FS_CALL(vnode, ioctl)) { 347398849Sken return FS_CALL(vnode, ioctl, 347498849Sken descriptor->cookie, op, buffer, length); 347598849Sken } 347698849Sken 347798849Sken return FSSH_EOPNOTSUPP; 347898849Sken} 347998849Sken 348098849Sken 348198849Skenstatic fssh_status_t 348298849Skencommon_fcntl(int fd, int op, uint32_t argument, bool kernel) 348398849Sken{ 348498849Sken struct file_descriptor *descriptor; 348598849Sken struct vnode *vnode; 348698849Sken fssh_status_t status; 348798849Sken 348898849Sken FUNCTION(("common_fcntl(fd = %d, op = %d, argument = %lx, %s)\n", 348998849Sken fd, op, argument, kernel ? "kernel" : "user")); 349098849Sken 349198849Sken descriptor = get_fd_and_vnode(fd, &vnode, kernel); 349298849Sken if (descriptor == NULL) 349398849Sken return FSSH_B_FILE_ERROR; 349498849Sken 349598849Sken switch (op) { 349698849Sken case FSSH_F_SETFD: 349798849Sken { 349898849Sken struct io_context *context = get_current_io_context(kernel); 349998849Sken // Set file descriptor flags 350098849Sken 350198849Sken // FSSH_O_CLOEXEC is the only flag available at this time 350298849Sken fssh_mutex_lock(&context->io_mutex); 350398849Sken fd_set_close_on_exec(context, fd, argument == FSSH_FD_CLOEXEC); 350498849Sken fssh_mutex_unlock(&context->io_mutex); 350598849Sken 350698849Sken status = FSSH_B_OK; 350798849Sken break; 350898849Sken } 350998849Sken 351098849Sken case FSSH_F_GETFD: 351198849Sken { 351298849Sken struct io_context *context = get_current_io_context(kernel); 351398849Sken 351498849Sken // Get file descriptor flags 351598849Sken fssh_mutex_lock(&context->io_mutex); 351698849Sken status = fd_close_on_exec(context, fd) ? FSSH_FD_CLOEXEC : 0; 351798849Sken fssh_mutex_unlock(&context->io_mutex); 351898849Sken break; 351998849Sken } 352098849Sken 352198849Sken case FSSH_F_SETFL: 352298849Sken // Set file descriptor open mode 352398849Sken if (HAS_FS_CALL(vnode, set_flags)) { 352498849Sken // we only accept changes to FSSH_O_APPEND and FSSH_O_NONBLOCK 352598849Sken argument &= FSSH_O_APPEND | FSSH_O_NONBLOCK; 352698849Sken 352798849Sken status = FS_CALL(vnode, set_flags, descriptor->cookie, (int)argument); 352898849Sken if (status == FSSH_B_OK) { 352998849Sken // update this descriptor's open_mode field 353098849Sken descriptor->open_mode = (descriptor->open_mode & ~(FSSH_O_APPEND | FSSH_O_NONBLOCK)) 3531102336Salfred | argument; 3532102336Salfred } 353345386Swpaul } else 353445386Swpaul status = FSSH_EOPNOTSUPP; 353545386Swpaul break; 353645386Swpaul 353745386Swpaul case FSSH_F_GETFL: 353867087Swpaul // Get file descriptor open mode 353945386Swpaul status = descriptor->open_mode; 354098849Sken break; 354198849Sken 354298849Sken case FSSH_F_DUPFD: 354398849Sken { 354498849Sken struct io_context *context = get_current_io_context(kernel); 354598849Sken 354698849Sken status = new_fd_etc(context, descriptor, (int)argument); 354798849Sken if (status >= 0) { 354898849Sken fssh_mutex_lock(&context->io_mutex); 354998849Sken fd_set_close_on_exec(context, fd, false); 355045386Swpaul fssh_mutex_unlock(&context->io_mutex); 355145386Swpaul 355245386Swpaul fssh_atomic_add(&descriptor->ref_count, 1); 355345386Swpaul } 355445386Swpaul break; 355567087Swpaul } 355645386Swpaul 355745386Swpaul case FSSH_F_GETLK: 355845386Swpaul case FSSH_F_SETLK: 355945386Swpaul case FSSH_F_SETLKW: 356045386Swpaul status = FSSH_B_BAD_VALUE; 356145386Swpaul break; 356245386Swpaul 356345386Swpaul // ToDo: add support for more ops? 3564102336Salfred 3565102336Salfred default: 356645386Swpaul status = FSSH_B_BAD_VALUE; 356745386Swpaul } 356845386Swpaul 356945386Swpaul put_fd(descriptor); 357045386Swpaul return status; 357167087Swpaul} 357267087Swpaul 357345386Swpaul 357445386Swpaulstatic fssh_status_t 357545386Swpaulcommon_sync(int fd, bool kernel) 357645386Swpaul{ 357745386Swpaul struct file_descriptor *descriptor; 357845386Swpaul struct vnode *vnode; 357945386Swpaul fssh_status_t status; 358045386Swpaul 358145386Swpaul FUNCTION(("common_fsync: entry. fd %d kernel %d\n", fd, kernel)); 358245386Swpaul 358345386Swpaul descriptor = get_fd_and_vnode(fd, &vnode, kernel); 358445386Swpaul if (descriptor == NULL) 358545386Swpaul return FSSH_B_FILE_ERROR; 358645386Swpaul 358745386Swpaul if (HAS_FS_CALL(vnode, fsync)) 358845386Swpaul status = FS_CALL_NO_PARAMS(vnode, fsync); 358945386Swpaul else 359045386Swpaul status = FSSH_EOPNOTSUPP; 359145386Swpaul 359245386Swpaul put_fd(descriptor); 359345386Swpaul return status; 359445386Swpaul} 359545386Swpaul 359645386Swpaul 359745386Swpaulstatic fssh_status_t 359845386Swpaulcommon_lock_node(int fd, bool kernel) 359945386Swpaul{ 360045386Swpaul struct file_descriptor *descriptor; 360145386Swpaul struct vnode *vnode; 360245386Swpaul 360345386Swpaul descriptor = get_fd_and_vnode(fd, &vnode, kernel); 360445386Swpaul if (descriptor == NULL) 360567087Swpaul return FSSH_B_FILE_ERROR; 360645386Swpaul 360745386Swpaul fssh_status_t status = FSSH_B_OK; 360845386Swpaul 360945386Swpaul // We need to set the locking atomically - someone 361045386Swpaul // else might set one at the same time 361145386Swpaul#ifdef __x86_64__ 361245386Swpaul if (fssh_atomic_test_and_set64((vint64_t *)&vnode->mandatory_locked_by, 361345386Swpaul (fssh_addr_t)descriptor, 0) != 0) 3614102336Salfred#else 3615102336Salfred if (fssh_atomic_test_and_set((vint32_t *)&vnode->mandatory_locked_by, 361649011Swpaul (fssh_addr_t)descriptor, 0) != 0) 361745386Swpaul#endif 361845386Swpaul status = FSSH_B_BUSY; 361945386Swpaul 362049011Swpaul put_fd(descriptor); 362167087Swpaul return status; 362245386Swpaul} 362367087Swpaul 362445386Swpaul 362545386Swpaulstatic fssh_status_t 362645386Swpaulcommon_unlock_node(int fd, bool kernel) 3627{ 3628 struct file_descriptor *descriptor; 3629 struct vnode *vnode; 3630 3631 descriptor = get_fd_and_vnode(fd, &vnode, kernel); 3632 if (descriptor == NULL) 3633 return FSSH_B_FILE_ERROR; 3634 3635 fssh_status_t status = FSSH_B_OK; 3636 3637 // We need to set the locking atomically - someone 3638 // else might set one at the same time 3639#ifdef __x86_64__ 3640 if (fssh_atomic_test_and_set64((vint64_t *)&vnode->mandatory_locked_by, 3641 0, (fssh_addr_t)descriptor) != (int64_t)descriptor) 3642#else 3643 if (fssh_atomic_test_and_set((vint32_t *)&vnode->mandatory_locked_by, 3644 0, (fssh_addr_t)descriptor) != (int32_t)descriptor) 3645#endif 3646 status = FSSH_B_BAD_VALUE; 3647 3648 put_fd(descriptor); 3649 return status; 3650} 3651 3652 3653static fssh_status_t 3654common_read_link(int fd, char *path, char *buffer, fssh_size_t *_bufferSize, 3655 bool kernel) 3656{ 3657 struct vnode *vnode; 3658 fssh_status_t status; 3659 3660 status = fd_and_path_to_vnode(fd, path, false, &vnode, NULL, kernel); 3661 if (status < FSSH_B_OK) 3662 return status; 3663 3664 if (HAS_FS_CALL(vnode, read_symlink)) { 3665 status = FS_CALL(vnode, read_symlink, buffer, _bufferSize); 3666 } else 3667 status = FSSH_B_BAD_VALUE; 3668 3669 put_vnode(vnode); 3670 return status; 3671} 3672 3673 3674static fssh_status_t 3675common_create_symlink(int fd, char *path, const char *toPath, int mode, 3676 bool kernel) 3677{ 3678 // path validity checks have to be in the calling function! 3679 char name[FSSH_B_FILE_NAME_LENGTH]; 3680 struct vnode *vnode; 3681 fssh_status_t status; 3682 3683 FUNCTION(("common_create_symlink(fd = %d, path = %s, toPath = %s, mode = %d, kernel = %d)\n", fd, path, toPath, mode, kernel)); 3684 3685 status = fd_and_path_to_dir_vnode(fd, path, &vnode, name, kernel); 3686 if (status < FSSH_B_OK) 3687 return status; 3688 3689 if (HAS_FS_CALL(vnode, create_symlink)) 3690 status = FS_CALL(vnode, create_symlink, name, toPath, mode); 3691 else 3692 status = FSSH_EROFS; 3693 3694 put_vnode(vnode); 3695 3696 return status; 3697} 3698 3699 3700static fssh_status_t 3701common_create_link(char *path, char *toPath, bool kernel) 3702{ 3703 // path validity checks have to be in the calling function! 3704 char name[FSSH_B_FILE_NAME_LENGTH]; 3705 struct vnode *directory, *vnode; 3706 fssh_status_t status; 3707 3708 FUNCTION(("common_create_link(path = %s, toPath = %s, kernel = %d)\n", path, toPath, kernel)); 3709 3710 status = path_to_dir_vnode(path, &directory, name, kernel); 3711 if (status < FSSH_B_OK) 3712 return status; 3713 3714 status = path_to_vnode(toPath, true, &vnode, NULL, kernel); 3715 if (status < FSSH_B_OK) 3716 goto err; 3717 3718 if (directory->mount != vnode->mount) { 3719 status = FSSH_B_CROSS_DEVICE_LINK; 3720 goto err1; 3721 } 3722 3723 if (HAS_FS_CALL(directory, link)) 3724 status = FS_CALL(directory, link, name, vnode); 3725 else 3726 status = FSSH_EROFS; 3727 3728err1: 3729 put_vnode(vnode); 3730err: 3731 put_vnode(directory); 3732 3733 return status; 3734} 3735 3736 3737static fssh_status_t 3738common_unlink(int fd, char *path, bool kernel) 3739{ 3740 char filename[FSSH_B_FILE_NAME_LENGTH]; 3741 struct vnode *vnode; 3742 fssh_status_t status; 3743 3744 FUNCTION(("common_unlink: fd: %d, path '%s', kernel %d\n", fd, path, kernel)); 3745 3746 status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel); 3747 if (status < 0) 3748 return status; 3749 3750 if (HAS_FS_CALL(vnode, unlink)) 3751 status = FS_CALL(vnode, unlink, filename); 3752 else 3753 status = FSSH_EROFS; 3754 3755 put_vnode(vnode); 3756 3757 return status; 3758} 3759 3760 3761static fssh_status_t 3762common_access(char *path, int mode, bool kernel) 3763{ 3764 struct vnode *vnode; 3765 fssh_status_t status; 3766 3767 status = path_to_vnode(path, true, &vnode, NULL, kernel); 3768 if (status < FSSH_B_OK) 3769 return status; 3770 3771 if (HAS_FS_CALL(vnode, access)) 3772 status = FS_CALL(vnode, access, mode); 3773 else 3774 status = FSSH_B_OK; 3775 3776 put_vnode(vnode); 3777 3778 return status; 3779} 3780 3781 3782static fssh_status_t 3783common_rename(int fd, char *path, int newFD, char *newPath, bool kernel) 3784{ 3785 struct vnode *fromVnode, *toVnode; 3786 char fromName[FSSH_B_FILE_NAME_LENGTH]; 3787 char toName[FSSH_B_FILE_NAME_LENGTH]; 3788 fssh_status_t status; 3789 3790 FUNCTION(("common_rename(fd = %d, path = %s, newFD = %d, newPath = %s, kernel = %d)\n", fd, path, newFD, newPath, kernel)); 3791 3792 status = fd_and_path_to_dir_vnode(fd, path, &fromVnode, fromName, kernel); 3793 if (status < 0) 3794 return status; 3795 3796 status = fd_and_path_to_dir_vnode(newFD, newPath, &toVnode, toName, kernel); 3797 if (status < 0) 3798 goto err; 3799 3800 if (fromVnode->device != toVnode->device) { 3801 status = FSSH_B_CROSS_DEVICE_LINK; 3802 goto err1; 3803 } 3804 3805 if (HAS_FS_CALL(fromVnode, rename)) 3806 status = FS_CALL(fromVnode, rename, fromName, toVnode, toName); 3807 else 3808 status = FSSH_EROFS; 3809 3810err1: 3811 put_vnode(toVnode); 3812err: 3813 put_vnode(fromVnode); 3814 3815 return status; 3816} 3817 3818 3819static fssh_status_t 3820common_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat) 3821{ 3822 struct vnode *vnode = descriptor->u.vnode; 3823 3824 FUNCTION(("common_read_stat: stat %p\n", stat)); 3825 3826 stat->fssh_st_atim.tv_nsec = 0; 3827 stat->fssh_st_mtim.tv_nsec = 0; 3828 stat->fssh_st_ctim.tv_nsec = 0; 3829 stat->fssh_st_crtim.tv_nsec = 0; 3830 3831 fssh_status_t status = FS_CALL(vnode, read_stat, stat); 3832 3833 // fill in the st_dev and st_ino fields 3834 if (status == FSSH_B_OK) { 3835 stat->fssh_st_dev = vnode->device; 3836 stat->fssh_st_ino = vnode->id; 3837 } 3838 3839 return status; 3840} 3841 3842 3843static fssh_status_t 3844common_write_stat(struct file_descriptor *descriptor, 3845 const struct fssh_stat *stat, int statMask) 3846{ 3847 struct vnode *vnode = descriptor->u.vnode; 3848 3849 FUNCTION(("common_write_stat(vnode = %p, stat = %p, statMask = %d)\n", vnode, stat, statMask)); 3850 if (!HAS_FS_CALL(vnode, write_stat)) 3851 return FSSH_EROFS; 3852 3853 return FS_CALL(vnode, write_stat, stat, statMask); 3854} 3855 3856 3857static fssh_status_t 3858common_path_read_stat(int fd, char *path, bool traverseLeafLink, 3859 struct fssh_stat *stat, bool kernel) 3860{ 3861 struct vnode *vnode; 3862 fssh_status_t status; 3863 3864 FUNCTION(("common_path_read_stat: fd: %d, path '%s', stat %p,\n", fd, path, stat)); 3865 3866 status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel); 3867 if (status < 0) 3868 return status; 3869 3870 status = FS_CALL(vnode, read_stat, stat); 3871 3872 // fill in the st_dev and st_ino fields 3873 if (status == FSSH_B_OK) { 3874 stat->fssh_st_dev = vnode->device; 3875 stat->fssh_st_ino = vnode->id; 3876 } 3877 3878 put_vnode(vnode); 3879 return status; 3880} 3881 3882 3883static fssh_status_t 3884common_path_write_stat(int fd, char *path, bool traverseLeafLink, 3885 const struct fssh_stat *stat, int statMask, bool kernel) 3886{ 3887 struct vnode *vnode; 3888 fssh_status_t status; 3889 3890 FUNCTION(("common_write_stat: fd: %d, path '%s', stat %p, stat_mask %d, kernel %d\n", fd, path, stat, statMask, kernel)); 3891 3892 status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel); 3893 if (status < 0) 3894 return status; 3895 3896 if (HAS_FS_CALL(vnode, write_stat)) 3897 status = FS_CALL(vnode, write_stat, stat, statMask); 3898 else 3899 status = FSSH_EROFS; 3900 3901 put_vnode(vnode); 3902 3903 return status; 3904} 3905 3906 3907static int 3908attr_dir_open(int fd, char *path, bool kernel) 3909{ 3910 struct vnode *vnode; 3911 int status; 3912 3913 FUNCTION(("attr_dir_open(fd = %d, path = '%s', kernel = %d)\n", fd, path, kernel)); 3914 3915 status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel); 3916 if (status < FSSH_B_OK) 3917 return status; 3918 3919 status = open_attr_dir_vnode(vnode, kernel); 3920 if (status < 0) 3921 put_vnode(vnode); 3922 3923 return status; 3924} 3925 3926 3927static fssh_status_t 3928attr_dir_close(struct file_descriptor *descriptor) 3929{ 3930 struct vnode *vnode = descriptor->u.vnode; 3931 3932 FUNCTION(("attr_dir_close(descriptor = %p)\n", descriptor)); 3933 3934 if (HAS_FS_CALL(vnode, close_attr_dir)) 3935 return FS_CALL(vnode, close_attr_dir, descriptor->cookie); 3936 3937 return FSSH_B_OK; 3938} 3939 3940 3941static void 3942attr_dir_free_fd(struct file_descriptor *descriptor) 3943{ 3944 struct vnode *vnode = descriptor->u.vnode; 3945 3946 if (vnode != NULL) { 3947 FS_CALL(vnode, free_attr_dir_cookie, descriptor->cookie); 3948 put_vnode(vnode); 3949 } 3950} 3951 3952 3953static fssh_status_t 3954attr_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer, 3955 fssh_size_t bufferSize, uint32_t *_count) 3956{ 3957 struct vnode *vnode = descriptor->u.vnode; 3958 3959 FUNCTION(("attr_dir_read(descriptor = %p)\n", descriptor)); 3960 3961 if (HAS_FS_CALL(vnode, read_attr_dir)) 3962 return FS_CALL(vnode, read_attr_dir, descriptor->cookie, buffer, bufferSize, _count); 3963 3964 return FSSH_EOPNOTSUPP; 3965} 3966 3967 3968static fssh_status_t 3969attr_dir_rewind(struct file_descriptor *descriptor) 3970{ 3971 struct vnode *vnode = descriptor->u.vnode; 3972 3973 FUNCTION(("attr_dir_rewind(descriptor = %p)\n", descriptor)); 3974 3975 if (HAS_FS_CALL(vnode, rewind_attr_dir)) 3976 return FS_CALL(vnode, rewind_attr_dir, descriptor->cookie); 3977 3978 return FSSH_EOPNOTSUPP; 3979} 3980 3981 3982static int 3983attr_create(int fd, const char *name, uint32_t type, int openMode, bool kernel) 3984{ 3985 struct vnode *vnode; 3986 void *cookie; 3987 int status; 3988 3989 if (name == NULL || *name == '\0') 3990 return FSSH_B_BAD_VALUE; 3991 3992 vnode = get_vnode_from_fd(fd, kernel); 3993 if (vnode == NULL) 3994 return FSSH_B_FILE_ERROR; 3995 3996 if (!HAS_FS_CALL(vnode, create_attr)) { 3997 status = FSSH_EROFS; 3998 goto err; 3999 } 4000 4001 status = FS_CALL(vnode, create_attr, name, type, openMode, &cookie); 4002 if (status < FSSH_B_OK) 4003 goto err; 4004 4005 if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0) 4006 return status; 4007 4008 FS_CALL(vnode, close_attr, cookie); 4009 FS_CALL(vnode, free_attr_cookie, cookie); 4010 4011 FS_CALL(vnode, remove_attr, name); 4012 4013err: 4014 put_vnode(vnode); 4015 4016 return status; 4017} 4018 4019 4020static int 4021attr_open(int fd, const char *name, int openMode, bool kernel) 4022{ 4023 struct vnode *vnode; 4024 void *cookie; 4025 int status; 4026 4027 if (name == NULL || *name == '\0') 4028 return FSSH_B_BAD_VALUE; 4029 4030 vnode = get_vnode_from_fd(fd, kernel); 4031 if (vnode == NULL) 4032 return FSSH_B_FILE_ERROR; 4033 4034 if (!HAS_FS_CALL(vnode, open_attr)) { 4035 status = FSSH_EOPNOTSUPP; 4036 goto err; 4037 } 4038 4039 status = FS_CALL(vnode, open_attr, name, openMode, &cookie); 4040 if (status < FSSH_B_OK) 4041 goto err; 4042 4043 // now we only need a file descriptor for this attribute and we're done 4044 if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0) 4045 return status; 4046 4047 FS_CALL(vnode, close_attr, cookie); 4048 FS_CALL(vnode, free_attr_cookie, cookie); 4049 4050err: 4051 put_vnode(vnode); 4052 4053 return status; 4054} 4055 4056 4057static fssh_status_t 4058attr_close(struct file_descriptor *descriptor) 4059{ 4060 struct vnode *vnode = descriptor->u.vnode; 4061 4062 FUNCTION(("attr_close(descriptor = %p)\n", descriptor)); 4063 4064 if (HAS_FS_CALL(vnode, close_attr)) 4065 return FS_CALL(vnode, close_attr, descriptor->cookie); 4066 4067 return FSSH_B_OK; 4068} 4069 4070 4071static void 4072attr_free_fd(struct file_descriptor *descriptor) 4073{ 4074 struct vnode *vnode = descriptor->u.vnode; 4075 4076 if (vnode != NULL) { 4077 FS_CALL(vnode, free_attr_cookie, descriptor->cookie); 4078 put_vnode(vnode); 4079 } 4080} 4081 4082 4083static fssh_status_t 4084attr_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length) 4085{ 4086 struct vnode *vnode = descriptor->u.vnode; 4087 4088 FUNCTION(("attr_read: buf %p, pos %Ld, len %p = %ld\n", buffer, pos, length, *length)); 4089 if (!HAS_FS_CALL(vnode, read_attr)) 4090 return FSSH_EOPNOTSUPP; 4091 4092 return FS_CALL(vnode, read_attr, descriptor->cookie, pos, buffer, length); 4093} 4094 4095 4096static fssh_status_t 4097attr_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length) 4098{ 4099 struct vnode *vnode = descriptor->u.vnode; 4100 4101 FUNCTION(("attr_write: buf %p, pos %Ld, len %p\n", buffer, pos, length)); 4102 if (!HAS_FS_CALL(vnode, write_attr)) 4103 return FSSH_EOPNOTSUPP; 4104 4105 return FS_CALL(vnode, write_attr, descriptor->cookie, pos, buffer, length); 4106} 4107 4108 4109static fssh_off_t 4110attr_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType) 4111{ 4112 fssh_off_t offset; 4113 4114 switch (seekType) { 4115 case FSSH_SEEK_SET: 4116 offset = 0; 4117 break; 4118 case FSSH_SEEK_CUR: 4119 offset = descriptor->pos; 4120 break; 4121 case FSSH_SEEK_END: 4122 { 4123 struct vnode *vnode = descriptor->u.vnode; 4124 struct fssh_stat stat; 4125 fssh_status_t status; 4126 4127 if (!HAS_FS_CALL(vnode, read_stat)) 4128 return FSSH_EOPNOTSUPP; 4129 4130 status = FS_CALL(vnode, read_attr_stat, descriptor->cookie, &stat); 4131 if (status < FSSH_B_OK) 4132 return status; 4133 4134 offset = stat.fssh_st_size; 4135 break; 4136 } 4137 default: 4138 return FSSH_B_BAD_VALUE; 4139 } 4140 4141 // assumes fssh_off_t is 64 bits wide 4142 if (offset > 0 && LLONG_MAX - offset < pos) 4143 return FSSH_EOVERFLOW; 4144 4145 pos += offset; 4146 if (pos < 0) 4147 return FSSH_B_BAD_VALUE; 4148 4149 return descriptor->pos = pos; 4150} 4151 4152 4153static fssh_status_t 4154attr_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat) 4155{ 4156 struct vnode *vnode = descriptor->u.vnode; 4157 4158 FUNCTION(("attr_read_stat: stat 0x%p\n", stat)); 4159 4160 if (!HAS_FS_CALL(vnode, read_attr_stat)) 4161 return FSSH_EOPNOTSUPP; 4162 4163 return FS_CALL(vnode, read_attr_stat, descriptor->cookie, stat); 4164} 4165 4166 4167static fssh_status_t 4168attr_write_stat(struct file_descriptor *descriptor, 4169 const struct fssh_stat *stat, int statMask) 4170{ 4171 struct vnode *vnode = descriptor->u.vnode; 4172 4173 FUNCTION(("attr_write_stat: stat = %p, statMask %d\n", stat, statMask)); 4174 4175 if (!HAS_FS_CALL(vnode, write_attr_stat)) 4176 return FSSH_EROFS; 4177 4178 return FS_CALL(vnode, write_attr_stat, descriptor->cookie, stat, statMask); 4179} 4180 4181 4182static fssh_status_t 4183attr_remove(int fd, const char *name, bool kernel) 4184{ 4185 struct file_descriptor *descriptor; 4186 struct vnode *vnode; 4187 fssh_status_t status; 4188 4189 if (name == NULL || *name == '\0') 4190 return FSSH_B_BAD_VALUE; 4191 4192 FUNCTION(("attr_remove: fd = %d, name = \"%s\", kernel %d\n", fd, name, kernel)); 4193 4194 descriptor = get_fd_and_vnode(fd, &vnode, kernel); 4195 if (descriptor == NULL) 4196 return FSSH_B_FILE_ERROR; 4197 4198 if (HAS_FS_CALL(vnode, remove_attr)) 4199 status = FS_CALL(vnode, remove_attr, name); 4200 else 4201 status = FSSH_EROFS; 4202 4203 put_fd(descriptor); 4204 4205 return status; 4206} 4207 4208 4209static fssh_status_t 4210attr_rename(int fromfd, const char *fromName, int tofd, const char *toName, bool kernel) 4211{ 4212 struct file_descriptor *fromDescriptor, *toDescriptor; 4213 struct vnode *fromVnode, *toVnode; 4214 fssh_status_t status; 4215 4216 if (fromName == NULL || *fromName == '\0' || toName == NULL || *toName == '\0') 4217 return FSSH_B_BAD_VALUE; 4218 4219 FUNCTION(("attr_rename: from fd = %d, from name = \"%s\", to fd = %d, to name = \"%s\", kernel %d\n", fromfd, fromName, tofd, toName, kernel)); 4220 4221 fromDescriptor = get_fd_and_vnode(fromfd, &fromVnode, kernel); 4222 if (fromDescriptor == NULL) 4223 return FSSH_B_FILE_ERROR; 4224 4225 toDescriptor = get_fd_and_vnode(tofd, &toVnode, kernel); 4226 if (toDescriptor == NULL) { 4227 status = FSSH_B_FILE_ERROR; 4228 goto err; 4229 } 4230 4231 // are the files on the same volume? 4232 if (fromVnode->device != toVnode->device) { 4233 status = FSSH_B_CROSS_DEVICE_LINK; 4234 goto err1; 4235 } 4236 4237 if (HAS_FS_CALL(fromVnode, rename_attr)) 4238 status = FS_CALL(fromVnode, rename_attr, fromName, toVnode, toName); 4239 else 4240 status = FSSH_EROFS; 4241 4242err1: 4243 put_fd(toDescriptor); 4244err: 4245 put_fd(fromDescriptor); 4246 4247 return status; 4248} 4249 4250 4251static fssh_status_t 4252index_dir_open(fssh_mount_id mountID, bool kernel) 4253{ 4254 struct fs_mount *mount; 4255 void *cookie; 4256 4257 FUNCTION(("index_dir_open(mountID = %ld, kernel = %d)\n", mountID, kernel)); 4258 4259 fssh_status_t status = get_mount(mountID, &mount); 4260 if (status < FSSH_B_OK) 4261 return status; 4262 4263 if (!HAS_FS_MOUNT_CALL(mount, open_index_dir)) { 4264 status = FSSH_EOPNOTSUPP; 4265 goto out; 4266 } 4267 4268 status = FS_MOUNT_CALL(mount, open_index_dir, &cookie); 4269 if (status < FSSH_B_OK) 4270 goto out; 4271 4272 // get fd for the index directory 4273 status = get_new_fd(FDTYPE_INDEX_DIR, mount, NULL, cookie, 0, kernel); 4274 if (status >= 0) 4275 goto out; 4276 4277 // something went wrong 4278 FS_MOUNT_CALL(mount, close_index_dir, cookie); 4279 FS_MOUNT_CALL(mount, free_index_dir_cookie, cookie); 4280 4281out: 4282 put_mount(mount); 4283 return status; 4284} 4285 4286 4287static fssh_status_t 4288index_dir_close(struct file_descriptor *descriptor) 4289{ 4290 struct fs_mount *mount = descriptor->u.mount; 4291 4292 FUNCTION(("index_dir_close(descriptor = %p)\n", descriptor)); 4293 4294 if (HAS_FS_MOUNT_CALL(mount, close_index_dir)) 4295 return FS_MOUNT_CALL(mount, close_index_dir, descriptor->cookie); 4296 4297 return FSSH_B_OK; 4298} 4299 4300 4301static void 4302index_dir_free_fd(struct file_descriptor *descriptor) 4303{ 4304 struct fs_mount *mount = descriptor->u.mount; 4305 4306 if (mount != NULL) { 4307 FS_MOUNT_CALL(mount, free_index_dir_cookie, descriptor->cookie); 4308 // ToDo: find a replacement ref_count object - perhaps the root dir? 4309 //put_vnode(vnode); 4310 } 4311} 4312 4313 4314static fssh_status_t 4315index_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer, 4316 fssh_size_t bufferSize, uint32_t *_count) 4317{ 4318 struct fs_mount *mount = descriptor->u.mount; 4319 4320 if (HAS_FS_MOUNT_CALL(mount, read_index_dir)) 4321 return FS_MOUNT_CALL(mount, read_index_dir, descriptor->cookie, buffer, bufferSize, _count); 4322 4323 return FSSH_EOPNOTSUPP; 4324} 4325 4326 4327static fssh_status_t 4328index_dir_rewind(struct file_descriptor *descriptor) 4329{ 4330 struct fs_mount *mount = descriptor->u.mount; 4331 4332 if (HAS_FS_MOUNT_CALL(mount, rewind_index_dir)) 4333 return FS_MOUNT_CALL(mount, rewind_index_dir, descriptor->cookie); 4334 4335 return FSSH_EOPNOTSUPP; 4336} 4337 4338 4339static fssh_status_t 4340index_create(fssh_mount_id mountID, const char *name, uint32_t type, uint32_t flags, bool kernel) 4341{ 4342 FUNCTION(("index_create(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel)); 4343 4344 struct fs_mount *mount; 4345 fssh_status_t status = get_mount(mountID, &mount); 4346 if (status < FSSH_B_OK) 4347 return status; 4348 4349 if (!HAS_FS_MOUNT_CALL(mount, create_index)) { 4350 status = FSSH_EROFS; 4351 goto out; 4352 } 4353 4354 status = FS_MOUNT_CALL(mount, create_index, name, type, flags); 4355 4356out: 4357 put_mount(mount); 4358 return status; 4359} 4360 4361 4362static fssh_status_t 4363index_name_read_stat(fssh_mount_id mountID, const char *name, 4364 struct fssh_stat *stat, bool kernel) 4365{ 4366 FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel)); 4367 4368 struct fs_mount *mount; 4369 fssh_status_t status = get_mount(mountID, &mount); 4370 if (status < FSSH_B_OK) 4371 return status; 4372 4373 if (!HAS_FS_MOUNT_CALL(mount, read_index_stat)) { 4374 status = FSSH_EOPNOTSUPP; 4375 goto out; 4376 } 4377 4378 status = FS_MOUNT_CALL(mount, read_index_stat, name, stat); 4379 4380out: 4381 put_mount(mount); 4382 return status; 4383} 4384 4385 4386static fssh_status_t 4387index_remove(fssh_mount_id mountID, const char *name, bool kernel) 4388{ 4389 FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel)); 4390 4391 struct fs_mount *mount; 4392 fssh_status_t status = get_mount(mountID, &mount); 4393 if (status < FSSH_B_OK) 4394 return status; 4395 4396 if (!HAS_FS_MOUNT_CALL(mount, remove_index)) { 4397 status = FSSH_EROFS; 4398 goto out; 4399 } 4400 4401 status = FS_MOUNT_CALL(mount, remove_index, name); 4402 4403out: 4404 put_mount(mount); 4405 return status; 4406} 4407 4408 4409/*! ToDo: the query FS API is still the pretty much the same as in R5. 4410 It would be nice if the FS would find some more kernel support 4411 for them. 4412 For example, query parsing should be moved into the kernel. 4413*/ 4414static int 4415query_open(fssh_dev_t device, const char *query, uint32_t flags, 4416 fssh_port_id port, int32_t token, bool kernel) 4417{ 4418 struct fs_mount *mount; 4419 void *cookie; 4420 4421 FUNCTION(("query_open(device = %ld, query = \"%s\", kernel = %d)\n", device, query, kernel)); 4422 4423 fssh_status_t status = get_mount(device, &mount); 4424 if (status < FSSH_B_OK) 4425 return status; 4426 4427 if (!HAS_FS_MOUNT_CALL(mount, open_query)) { 4428 status = FSSH_EOPNOTSUPP; 4429 goto out; 4430 } 4431 4432 status = FS_MOUNT_CALL(mount, open_query, query, flags, port, token, &cookie); 4433 if (status < FSSH_B_OK) 4434 goto out; 4435 4436 // get fd for the index directory 4437 status = get_new_fd(FDTYPE_QUERY, mount, NULL, cookie, 0, kernel); 4438 if (status >= 0) 4439 goto out; 4440 4441 // something went wrong 4442 FS_MOUNT_CALL(mount, close_query, cookie); 4443 FS_MOUNT_CALL(mount, free_query_cookie, cookie); 4444 4445out: 4446 put_mount(mount); 4447 return status; 4448} 4449 4450 4451static fssh_status_t 4452query_close(struct file_descriptor *descriptor) 4453{ 4454 struct fs_mount *mount = descriptor->u.mount; 4455 4456 FUNCTION(("query_close(descriptor = %p)\n", descriptor)); 4457 4458 if (HAS_FS_MOUNT_CALL(mount, close_query)) 4459 return FS_MOUNT_CALL(mount, close_query, descriptor->cookie); 4460 4461 return FSSH_B_OK; 4462} 4463 4464 4465static void 4466query_free_fd(struct file_descriptor *descriptor) 4467{ 4468 struct fs_mount *mount = descriptor->u.mount; 4469 4470 if (mount != NULL) { 4471 FS_MOUNT_CALL(mount, free_query_cookie, descriptor->cookie); 4472 // ToDo: find a replacement ref_count object - perhaps the root dir? 4473 //put_vnode(vnode); 4474 } 4475} 4476 4477 4478static fssh_status_t 4479query_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer, 4480 fssh_size_t bufferSize, uint32_t *_count) 4481{ 4482 struct fs_mount *mount = descriptor->u.mount; 4483 4484 if (HAS_FS_MOUNT_CALL(mount, read_query)) 4485 return FS_MOUNT_CALL(mount, read_query, descriptor->cookie, buffer, bufferSize, _count); 4486 4487 return FSSH_EOPNOTSUPP; 4488} 4489 4490 4491static fssh_status_t 4492query_rewind(struct file_descriptor *descriptor) 4493{ 4494 struct fs_mount *mount = descriptor->u.mount; 4495 4496 if (HAS_FS_MOUNT_CALL(mount, rewind_query)) 4497 return FS_MOUNT_CALL(mount, rewind_query, descriptor->cookie); 4498 4499 return FSSH_EOPNOTSUPP; 4500} 4501 4502 4503// #pragma mark - 4504// General File System functions 4505 4506 4507static fssh_dev_t 4508fs_mount(char *path, const char *device, const char *fsName, uint32_t flags, 4509 const char *args, bool kernel) 4510{ 4511 struct fs_mount *mount; 4512 fssh_status_t status = 0; 4513 4514 FUNCTION(("fs_mount: entry. path = '%s', fs_name = '%s'\n", path, fsName)); 4515 4516 // The path is always safe, we just have to make sure that fsName is 4517 // almost valid - we can't make any assumptions about args, though. 4518 // A NULL fsName is OK, if a device was given and the FS is not virtual. 4519 // We'll get it from the DDM later. 4520 if (fsName == NULL) { 4521 if (!device || flags & FSSH_B_MOUNT_VIRTUAL_DEVICE) 4522 return FSSH_B_BAD_VALUE; 4523 } else if (fsName[0] == '\0') 4524 return FSSH_B_BAD_VALUE; 4525 4526 RecursiveLocker mountOpLocker(sMountOpLock); 4527 4528 // If the file system is not a "virtual" one, the device argument should 4529 // point to a real file/device (if given at all). 4530 // get the partition 4531 KPath normalizedDevice; 4532 4533 if (!(flags & FSSH_B_MOUNT_VIRTUAL_DEVICE) && device) { 4534 // normalize the device path 4535// status = normalizedDevice.SetTo(device, true); 4536// NOTE: normalizing works only in our namespace. 4537 status = normalizedDevice.SetTo(device, false); 4538 if (status != FSSH_B_OK) 4539 return status; 4540 4541 device = normalizedDevice.Path(); 4542 // correct path to file device 4543 } 4544 4545 mount = (struct fs_mount *)malloc(sizeof(struct fs_mount)); 4546 if (mount == NULL) 4547 return FSSH_B_NO_MEMORY; 4548 4549 mount->volume = (fssh_fs_volume*)malloc(sizeof(fssh_fs_volume)); 4550 if (mount->volume == NULL) { 4551 free(mount); 4552 return FSSH_B_NO_MEMORY; 4553 } 4554 4555 list_init_etc(&mount->vnodes, fssh_offsetof(struct vnode, mount_link)); 4556 4557 mount->fs_name = get_file_system_name(fsName); 4558 if (mount->fs_name == NULL) { 4559 status = FSSH_B_NO_MEMORY; 4560 goto err1; 4561 } 4562 4563 mount->device_name = fssh_strdup(device); 4564 // "device" can be NULL 4565 4566 mount->fs = get_file_system(fsName); 4567 if (mount->fs == NULL) { 4568 status = FSSH_ENODEV; 4569 goto err3; 4570 } 4571 4572 fssh_recursive_lock_init(&mount->rlock, "mount rlock"); 4573 4574 // initialize structure 4575 mount->id = sNextMountID++; 4576 mount->root_vnode = NULL; 4577 mount->covers_vnode = NULL; 4578 mount->unmounting = false; 4579 mount->owns_file_device = false; 4580 4581 mount->volume->id = mount->id; 4582 mount->volume->layer = 0; 4583 mount->volume->private_volume = NULL; 4584 mount->volume->ops = NULL; 4585 mount->volume->sub_volume = NULL; 4586 mount->volume->super_volume = NULL; 4587 4588 // insert mount struct into list before we call FS's mount() function 4589 // so that vnodes can be created for this mount 4590 fssh_mutex_lock(&sMountMutex); 4591 hash_insert(sMountsTable, mount); 4592 fssh_mutex_unlock(&sMountMutex); 4593 4594 fssh_vnode_id rootID; 4595 4596 if (!sRoot) { 4597 // we haven't mounted anything yet 4598 if (fssh_strcmp(path, "/") != 0) { 4599 status = FSSH_B_ERROR; 4600 goto err4; 4601 } 4602 4603 status = mount->fs->mount(mount->volume, device, flags, args, &rootID); 4604 if (status < 0) { 4605 // ToDo: why should we hide the error code from the file system here? 4606 //status = ERR_VFS_GENERAL; 4607 goto err4; 4608 } 4609 } else { 4610 struct vnode *coveredVnode; 4611 status = path_to_vnode(path, true, &coveredVnode, NULL, kernel); 4612 if (status < FSSH_B_OK) 4613 goto err4; 4614 4615 // make sure covered_vnode is a DIR 4616 struct fssh_stat coveredNodeStat; 4617 status = FS_CALL(coveredVnode, read_stat, &coveredNodeStat); 4618 if (status < FSSH_B_OK) 4619 goto err4; 4620 4621 if (!FSSH_S_ISDIR(coveredNodeStat.fssh_st_mode)) { 4622 status = FSSH_B_NOT_A_DIRECTORY; 4623 goto err4; 4624 } 4625 4626 if (coveredVnode->mount->root_vnode == coveredVnode) { 4627 // this is already a mount point 4628 status = FSSH_B_BUSY; 4629 goto err4; 4630 } 4631 4632 mount->covers_vnode = coveredVnode; 4633 4634 // mount it 4635 status = mount->fs->mount(mount->volume, device, flags, args, &rootID); 4636 if (status < FSSH_B_OK) 4637 goto err5; 4638 } 4639 4640 // the root node is supposed to be owned by the file system - it must 4641 // exist at this point 4642 mount->root_vnode = lookup_vnode(mount->id, rootID); 4643 if (mount->root_vnode == NULL || mount->root_vnode->ref_count != 1) { 4644 fssh_panic("fs_mount: file system does not own its root node!\n"); 4645 status = FSSH_B_ERROR; 4646 goto err6; 4647 } 4648 4649 // No race here, since fs_mount() is the only function changing 4650 // covers_vnode (and holds sMountOpLock at that time). 4651 fssh_mutex_lock(&sVnodeCoveredByMutex); 4652 if (mount->covers_vnode) 4653 mount->covers_vnode->covered_by = mount->root_vnode; 4654 fssh_mutex_unlock(&sVnodeCoveredByMutex); 4655 4656 if (!sRoot) 4657 sRoot = mount->root_vnode; 4658 4659 return mount->id; 4660 4661err6: 4662 FS_MOUNT_CALL_NO_PARAMS(mount, unmount); 4663err5: 4664 if (mount->covers_vnode) 4665 put_vnode(mount->covers_vnode); 4666 4667err4: 4668 fssh_mutex_lock(&sMountMutex); 4669 hash_remove(sMountsTable, mount); 4670 fssh_mutex_unlock(&sMountMutex); 4671 4672 fssh_recursive_lock_destroy(&mount->rlock); 4673 4674 put_file_system(mount->fs); 4675 free(mount->device_name); 4676err3: 4677 free(mount->fs_name); 4678err1: 4679 free(mount->volume); 4680 free(mount); 4681 4682 return status; 4683} 4684 4685 4686static fssh_status_t 4687fs_unmount(char *path, uint32_t flags, bool kernel) 4688{ 4689 struct fs_mount *mount; 4690 struct vnode *vnode; 4691 fssh_status_t err; 4692 4693 FUNCTION(("vfs_unmount: entry. path = '%s', kernel %d\n", path, kernel)); 4694 4695 err = path_to_vnode(path, true, &vnode, NULL, kernel); 4696 if (err < 0) 4697 return FSSH_B_ENTRY_NOT_FOUND; 4698 4699 RecursiveLocker mountOpLocker(sMountOpLock); 4700 4701 mount = find_mount(vnode->device); 4702 if (!mount) 4703 fssh_panic("vfs_unmount: find_mount() failed on root vnode @%p of mount\n", vnode); 4704 4705 if (mount->root_vnode != vnode) { 4706 // not mountpoint 4707 put_vnode(vnode); 4708 return FSSH_B_BAD_VALUE; 4709 } 4710 4711 // grab the vnode master mutex to keep someone from creating 4712 // a vnode while we're figuring out if we can continue 4713 fssh_mutex_lock(&sVnodeMutex); 4714 4715 bool disconnectedDescriptors = false; 4716 4717 while (true) { 4718 bool busy = false; 4719 4720 // cycle through the list of vnodes associated with this mount and 4721 // make sure all of them are not busy or have refs on them 4722 vnode = NULL; 4723 while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) { 4724 // The root vnode ref_count needs to be 2 here: one for the file 4725 // system, one from the path_to_vnode() call above 4726 if (vnode->busy 4727 || ((vnode->ref_count != 0 && mount->root_vnode != vnode) 4728 || (vnode->ref_count != 2 && mount->root_vnode == vnode))) { 4729 // there are still vnodes in use on this mount, so we cannot 4730 // unmount yet 4731 busy = true; 4732 break; 4733 } 4734 } 4735 4736 if (!busy) 4737 break; 4738 4739 if ((flags & FSSH_B_FORCE_UNMOUNT) == 0) { 4740 fssh_mutex_unlock(&sVnodeMutex); 4741 put_vnode(mount->root_vnode); 4742 4743 return FSSH_B_BUSY; 4744 } 4745 4746 if (disconnectedDescriptors) { 4747 // wait a bit until the last access is finished, and then try again 4748 fssh_mutex_unlock(&sVnodeMutex); 4749 fssh_snooze(100000); 4750 // TODO: if there is some kind of bug that prevents the ref counts 4751 // from getting back to zero, this will fall into an endless loop... 4752 fssh_mutex_lock(&sVnodeMutex); 4753 continue; 4754 } 4755 4756 // the file system is still busy - but we're forced to unmount it, 4757 // so let's disconnect all open file descriptors 4758 4759 mount->unmounting = true; 4760 // prevent new vnodes from being created 4761 4762 fssh_mutex_unlock(&sVnodeMutex); 4763 4764 disconnect_mount_or_vnode_fds(mount, NULL); 4765 disconnectedDescriptors = true; 4766 4767 fssh_mutex_lock(&sVnodeMutex); 4768 } 4769 4770 // we can safely continue, mark all of the vnodes busy and this mount 4771 // structure in unmounting state 4772 mount->unmounting = true; 4773 4774 while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) { 4775 vnode->busy = true; 4776 4777 if (vnode->ref_count == 0) { 4778 // this vnode has been unused before 4779 list_remove_item(&sUnusedVnodeList, vnode); 4780 sUnusedVnodes--; 4781 } 4782 } 4783 4784 // The ref_count of the root node is 2 at this point, see above why this is 4785 mount->root_vnode->ref_count -= 2; 4786 4787 fssh_mutex_unlock(&sVnodeMutex); 4788 4789 fssh_mutex_lock(&sVnodeCoveredByMutex); 4790 mount->covers_vnode->covered_by = NULL; 4791 fssh_mutex_unlock(&sVnodeCoveredByMutex); 4792 put_vnode(mount->covers_vnode); 4793 4794 // Free all vnodes associated with this mount. 4795 // They will be removed from the mount list by free_vnode(), so 4796 // we don't have to do this. 4797 while ((vnode = (struct vnode *)list_get_first_item(&mount->vnodes)) != NULL) { 4798 free_vnode(vnode, false); 4799 } 4800 4801 // remove the mount structure from the hash table 4802 fssh_mutex_lock(&sMountMutex); 4803 hash_remove(sMountsTable, mount); 4804 fssh_mutex_unlock(&sMountMutex); 4805 4806 mountOpLocker.Unlock(); 4807 4808 FS_MOUNT_CALL_NO_PARAMS(mount, unmount); 4809 4810 // release the file system 4811 put_file_system(mount->fs); 4812 4813 free(mount->device_name); 4814 free(mount->fs_name); 4815 free(mount); 4816 4817 return FSSH_B_OK; 4818} 4819 4820 4821static fssh_status_t 4822fs_sync(fssh_dev_t device) 4823{ 4824 struct fs_mount *mount; 4825 fssh_status_t status = get_mount(device, &mount); 4826 if (status < FSSH_B_OK) 4827 return status; 4828 4829 fssh_mutex_lock(&sMountMutex); 4830 4831 if (HAS_FS_MOUNT_CALL(mount, sync)) 4832 status = FS_MOUNT_CALL_NO_PARAMS(mount, sync); 4833 4834 fssh_mutex_unlock(&sMountMutex); 4835 4836 struct vnode *previousVnode = NULL; 4837 while (true) { 4838 // synchronize access to vnode list 4839 fssh_recursive_lock_lock(&mount->rlock); 4840 4841 struct vnode *vnode = (struct vnode *)list_get_next_item(&mount->vnodes, 4842 previousVnode); 4843 4844 fssh_vnode_id id = -1; 4845 if (vnode != NULL) 4846 id = vnode->id; 4847 4848 fssh_recursive_lock_unlock(&mount->rlock); 4849 4850 if (vnode == NULL) 4851 break; 4852 4853 // acquire a reference to the vnode 4854 4855 if (get_vnode(mount->id, id, &vnode, true) == FSSH_B_OK) { 4856 if (previousVnode != NULL) 4857 put_vnode(previousVnode); 4858 4859 if (HAS_FS_CALL(vnode, fsync)) 4860 FS_CALL_NO_PARAMS(vnode, fsync); 4861 4862 // the next vnode might change until we lock the vnode list again, 4863 // but this vnode won't go away since we keep a reference to it. 4864 previousVnode = vnode; 4865 } else { 4866 fssh_dprintf("syncing of mount %d stopped due to vnode %" 4867 FSSH_B_PRIdINO ".\n", (int)mount->id, id); 4868 break; 4869 } 4870 } 4871 4872 if (previousVnode != NULL) 4873 put_vnode(previousVnode); 4874 4875 put_mount(mount); 4876 return status; 4877} 4878 4879 4880static fssh_status_t 4881fs_read_info(fssh_dev_t device, struct fssh_fs_info *info) 4882{ 4883 struct fs_mount *mount; 4884 fssh_status_t status = get_mount(device, &mount); 4885 if (status < FSSH_B_OK) 4886 return status; 4887 4888 fssh_memset(info, 0, sizeof(struct fssh_fs_info)); 4889 4890 if (HAS_FS_MOUNT_CALL(mount, read_fs_info)) 4891 status = FS_MOUNT_CALL(mount, read_fs_info, info); 4892 4893 // fill in info the file system doesn't (have to) know about 4894 if (status == FSSH_B_OK) { 4895 info->dev = mount->id; 4896 info->root = mount->root_vnode->id; 4897 fssh_strlcpy(info->fsh_name, mount->fs_name, sizeof(info->fsh_name)); 4898 if (mount->device_name != NULL) { 4899 fssh_strlcpy(info->device_name, mount->device_name, 4900 sizeof(info->device_name)); 4901 } 4902 } 4903 4904 // if the call is not supported by the file system, there are still 4905 // the parts that we filled out ourselves 4906 4907 put_mount(mount); 4908 return status; 4909} 4910 4911 4912static fssh_status_t 4913fs_write_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask) 4914{ 4915 struct fs_mount *mount; 4916 fssh_status_t status = get_mount(device, &mount); 4917 if (status < FSSH_B_OK) 4918 return status; 4919 4920 if (HAS_FS_MOUNT_CALL(mount, write_fs_info)) 4921 status = FS_MOUNT_CALL(mount, write_fs_info, info, mask); 4922 else 4923 status = FSSH_EROFS; 4924 4925 put_mount(mount); 4926 return status; 4927} 4928 4929 4930static fssh_dev_t 4931fs_next_device(int32_t *_cookie) 4932{ 4933 struct fs_mount *mount = NULL; 4934 fssh_dev_t device = *_cookie; 4935 4936 fssh_mutex_lock(&sMountMutex); 4937 4938 // Since device IDs are assigned sequentially, this algorithm 4939 // does work good enough. It makes sure that the device list 4940 // returned is sorted, and that no device is skipped when an 4941 // already visited device got unmounted. 4942 4943 while (device < sNextMountID) { 4944 mount = find_mount(device++); 4945 if (mount != NULL && mount->cookie != NULL) 4946 break; 4947 } 4948 4949 *_cookie = device; 4950 4951 if (mount != NULL) 4952 device = mount->id; 4953 else 4954 device = FSSH_B_BAD_VALUE; 4955 4956 fssh_mutex_unlock(&sMountMutex); 4957 4958 return device; 4959} 4960 4961 4962static fssh_status_t 4963get_cwd(char *buffer, fssh_size_t size, bool kernel) 4964{ 4965 // Get current working directory from io context 4966 struct io_context *context = get_current_io_context(kernel); 4967 fssh_status_t status; 4968 4969 FUNCTION(("vfs_get_cwd: buf %p, size %ld\n", buffer, size)); 4970 4971 fssh_mutex_lock(&context->io_mutex); 4972 4973 if (context->cwd) 4974 status = dir_vnode_to_path(context->cwd, buffer, size); 4975 else 4976 status = FSSH_B_ERROR; 4977 4978 fssh_mutex_unlock(&context->io_mutex); 4979 return status; 4980} 4981 4982 4983static fssh_status_t 4984set_cwd(int fd, char *path, bool kernel) 4985{ 4986 struct io_context *context; 4987 struct vnode *vnode = NULL; 4988 struct vnode *oldDirectory; 4989 struct fssh_stat stat; 4990 fssh_status_t status; 4991 4992 FUNCTION(("set_cwd: path = \'%s\'\n", path)); 4993 4994 // Get vnode for passed path, and bail if it failed 4995 status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel); 4996 if (status < 0) 4997 return status; 4998 4999 status = FS_CALL(vnode, read_stat, &stat); 5000 if (status < 0) 5001 goto err; 5002 5003 if (!FSSH_S_ISDIR(stat.fssh_st_mode)) { 5004 // nope, can't cwd to here 5005 status = FSSH_B_NOT_A_DIRECTORY; 5006 goto err; 5007 } 5008 5009 // Get current io context and lock 5010 context = get_current_io_context(kernel); 5011 fssh_mutex_lock(&context->io_mutex); 5012 5013 // save the old current working directory first 5014 oldDirectory = context->cwd; 5015 context->cwd = vnode; 5016 5017 fssh_mutex_unlock(&context->io_mutex); 5018 5019 if (oldDirectory) 5020 put_vnode(oldDirectory); 5021 5022 return FSSH_B_NO_ERROR; 5023 5024err: 5025 put_vnode(vnode); 5026 return status; 5027} 5028 5029 5030// #pragma mark - 5031// Calls from within the kernel 5032 5033 5034fssh_dev_t 5035_kern_mount(const char *path, const char *device, const char *fsName, 5036 uint32_t flags, const char *args, fssh_size_t argsLength) 5037{ 5038 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5039 if (pathBuffer.InitCheck() != FSSH_B_OK) 5040 return FSSH_B_NO_MEMORY; 5041 5042 return fs_mount(pathBuffer.LockBuffer(), device, fsName, flags, args, true); 5043} 5044 5045 5046fssh_status_t 5047_kern_unmount(const char *path, uint32_t flags) 5048{ 5049 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5050 if (pathBuffer.InitCheck() != FSSH_B_OK) 5051 return FSSH_B_NO_MEMORY; 5052 5053 return fs_unmount(pathBuffer.LockBuffer(), flags, true); 5054} 5055 5056 5057fssh_status_t 5058_kern_read_fs_info(fssh_dev_t device, struct fssh_fs_info *info) 5059{ 5060 if (info == NULL) 5061 return FSSH_B_BAD_VALUE; 5062 5063 return fs_read_info(device, info); 5064} 5065 5066 5067fssh_status_t 5068_kern_write_fs_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask) 5069{ 5070 if (info == NULL) 5071 return FSSH_B_BAD_VALUE; 5072 5073 return fs_write_info(device, info, mask); 5074} 5075 5076 5077fssh_status_t 5078_kern_sync(void) 5079{ 5080 // Note: _kern_sync() is also called from _user_sync() 5081 int32_t cookie = 0; 5082 fssh_dev_t device; 5083 while ((device = fs_next_device(&cookie)) >= 0) { 5084 fssh_status_t status = fs_sync(device); 5085 if (status != FSSH_B_OK && status != FSSH_B_BAD_VALUE) 5086 fssh_dprintf("sync: device %d couldn't sync: %s\n", (int)device, fssh_strerror(status)); 5087 } 5088 5089 return FSSH_B_OK; 5090} 5091 5092 5093fssh_dev_t 5094_kern_next_device(int32_t *_cookie) 5095{ 5096 return fs_next_device(_cookie); 5097} 5098 5099 5100int 5101_kern_open_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int openMode, int perms) 5102{ 5103 if (openMode & FSSH_O_CREAT) 5104 return file_create_entry_ref(device, inode, name, openMode, perms, true); 5105 5106 return file_open_entry_ref(device, inode, name, openMode, true); 5107} 5108 5109 5110/** \brief Opens a node specified by a FD + path pair. 5111 * 5112 * At least one of \a fd and \a path must be specified. 5113 * If only \a fd is given, the function opens the node identified by this 5114 * FD. If only a path is given, this path is opened. If both are given and 5115 * the path is absolute, \a fd is ignored; a relative path is reckoned off 5116 * of the directory (!) identified by \a fd. 5117 * 5118 * \param fd The FD. May be < 0. 5119 * \param path The absolute or relative path. May be \c NULL. 5120 * \param openMode The open mode. 5121 * \return A FD referring to the newly opened node, or an error code, 5122 * if an error occurs. 5123 */ 5124 5125int 5126_kern_open(int fd, const char *path, int openMode, int perms) 5127{ 5128 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5129 if (pathBuffer.InitCheck() != FSSH_B_OK) 5130 return FSSH_B_NO_MEMORY; 5131 5132 if (openMode & FSSH_O_CREAT) 5133 return file_create(fd, pathBuffer.LockBuffer(), openMode, perms, true); 5134 5135 return file_open(fd, pathBuffer.LockBuffer(), openMode, true); 5136} 5137 5138 5139/** \brief Opens a directory specified by entry_ref or node_ref. 5140 * 5141 * The supplied name may be \c NULL, in which case directory identified 5142 * by \a device and \a inode will be opened. Otherwise \a device and 5143 * \a inode identify the parent directory of the directory to be opened 5144 * and \a name its entry name. 5145 * 5146 * \param device If \a name is specified the ID of the device the parent 5147 * directory of the directory to be opened resides on, otherwise 5148 * the device of the directory itself. 5149 * \param inode If \a name is specified the node ID of the parent 5150 * directory of the directory to be opened, otherwise node ID of the 5151 * directory itself. 5152 * \param name The entry name of the directory to be opened. If \c NULL, 5153 * the \a device + \a inode pair identify the node to be opened. 5154 * \return The FD of the newly opened directory or an error code, if 5155 * something went wrong. 5156 */ 5157 5158int 5159_kern_open_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name) 5160{ 5161 return dir_open_entry_ref(device, inode, name, true); 5162} 5163 5164 5165/** \brief Opens a directory specified by a FD + path pair. 5166 * 5167 * At least one of \a fd and \a path must be specified. 5168 * If only \a fd is given, the function opens the directory identified by this 5169 * FD. If only a path is given, this path is opened. If both are given and 5170 * the path is absolute, \a fd is ignored; a relative path is reckoned off 5171 * of the directory (!) identified by \a fd. 5172 * 5173 * \param fd The FD. May be < 0. 5174 * \param path The absolute or relative path. May be \c NULL. 5175 * \return A FD referring to the newly opened directory, or an error code, 5176 * if an error occurs. 5177 */ 5178 5179int 5180_kern_open_dir(int fd, const char *path) 5181{ 5182 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5183 if (pathBuffer.InitCheck() != FSSH_B_OK) 5184 return FSSH_B_NO_MEMORY; 5185 5186 return dir_open(fd, pathBuffer.LockBuffer(), true); 5187} 5188 5189 5190fssh_status_t 5191_kern_fcntl(int fd, int op, uint32_t argument) 5192{ 5193 return common_fcntl(fd, op, argument, true); 5194} 5195 5196 5197fssh_status_t 5198_kern_fsync(int fd) 5199{ 5200 return common_sync(fd, true); 5201} 5202 5203 5204fssh_status_t 5205_kern_lock_node(int fd) 5206{ 5207 return common_lock_node(fd, true); 5208} 5209 5210 5211fssh_status_t 5212_kern_unlock_node(int fd) 5213{ 5214 return common_unlock_node(fd, true); 5215} 5216 5217 5218fssh_status_t 5219_kern_create_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int perms) 5220{ 5221 return dir_create_entry_ref(device, inode, name, perms, true); 5222} 5223 5224 5225/** \brief Creates a directory specified by a FD + path pair. 5226 * 5227 * \a path must always be specified (it contains the name of the new directory 5228 * at least). If only a path is given, this path identifies the location at 5229 * which the directory shall be created. If both \a fd and \a path are given and 5230 * the path is absolute, \a fd is ignored; a relative path is reckoned off 5231 * of the directory (!) identified by \a fd. 5232 * 5233 * \param fd The FD. May be < 0. 5234 * \param path The absolute or relative path. Must not be \c NULL. 5235 * \param perms The access permissions the new directory shall have. 5236 * \return \c FSSH_B_OK, if the directory has been created successfully, another 5237 * error code otherwise. 5238 */ 5239 5240fssh_status_t 5241_kern_create_dir(int fd, const char *path, int perms) 5242{ 5243 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5244 if (pathBuffer.InitCheck() != FSSH_B_OK) 5245 return FSSH_B_NO_MEMORY; 5246 5247 return dir_create(fd, pathBuffer.LockBuffer(), perms, true); 5248} 5249 5250 5251fssh_status_t 5252_kern_remove_dir(int fd, const char *path) 5253{ 5254 if (path) { 5255 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5256 if (pathBuffer.InitCheck() != FSSH_B_OK) 5257 return FSSH_B_NO_MEMORY; 5258 5259 return dir_remove(fd, pathBuffer.LockBuffer(), true); 5260 } 5261 5262 return dir_remove(fd, NULL, true); 5263} 5264 5265 5266/** \brief Reads the contents of a symlink referred to by a FD + path pair. 5267 * 5268 * At least one of \a fd and \a path must be specified. 5269 * If only \a fd is given, the function the symlink to be read is the node 5270 * identified by this FD. If only a path is given, this path identifies the 5271 * symlink to be read. If both are given and the path is absolute, \a fd is 5272 * ignored; a relative path is reckoned off of the directory (!) identified 5273 * by \a fd. 5274 * If this function fails with FSSH_B_BUFFER_OVERFLOW, the \a _bufferSize pointer 5275 * will still be updated to reflect the required buffer size. 5276 * 5277 * \param fd The FD. May be < 0. 5278 * \param path The absolute or relative path. May be \c NULL. 5279 * \param buffer The buffer into which the contents of the symlink shall be 5280 * written. 5281 * \param _bufferSize A pointer to the size of the supplied buffer. 5282 * \return The length of the link on success or an appropriate error code 5283 */ 5284 5285fssh_status_t 5286_kern_read_link(int fd, const char *path, char *buffer, fssh_size_t *_bufferSize) 5287{ 5288 if (path) { 5289 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5290 if (pathBuffer.InitCheck() != FSSH_B_OK) 5291 return FSSH_B_NO_MEMORY; 5292 5293 return common_read_link(fd, pathBuffer.LockBuffer(), 5294 buffer, _bufferSize, true); 5295 } 5296 5297 return common_read_link(fd, NULL, buffer, _bufferSize, true); 5298} 5299 5300 5301/** \brief Creates a symlink specified by a FD + path pair. 5302 * 5303 * \a path must always be specified (it contains the name of the new symlink 5304 * at least). If only a path is given, this path identifies the location at 5305 * which the symlink shall be created. If both \a fd and \a path are given and 5306 * the path is absolute, \a fd is ignored; a relative path is reckoned off 5307 * of the directory (!) identified by \a fd. 5308 * 5309 * \param fd The FD. May be < 0. 5310 * \param toPath The absolute or relative path. Must not be \c NULL. 5311 * \param mode The access permissions the new symlink shall have. 5312 * \return \c FSSH_B_OK, if the symlink has been created successfully, another 5313 * error code otherwise. 5314 */ 5315 5316fssh_status_t 5317_kern_create_symlink(int fd, const char *path, const char *toPath, int mode) 5318{ 5319 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5320 KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1); 5321 if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK) 5322 return FSSH_B_NO_MEMORY; 5323 5324 char *toBuffer = toPathBuffer.LockBuffer(); 5325 5326 fssh_status_t status = check_path(toBuffer); 5327 if (status < FSSH_B_OK) 5328 return status; 5329 5330 return common_create_symlink(fd, pathBuffer.LockBuffer(), 5331 toBuffer, mode, true); 5332} 5333 5334 5335fssh_status_t 5336_kern_create_link(const char *path, const char *toPath) 5337{ 5338 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5339 KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1); 5340 if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK) 5341 return FSSH_B_NO_MEMORY; 5342 5343 return common_create_link(pathBuffer.LockBuffer(), 5344 toPathBuffer.LockBuffer(), true); 5345} 5346 5347 5348/** \brief Removes an entry specified by a FD + path pair from its directory. 5349 * 5350 * \a path must always be specified (it contains at least the name of the entry 5351 * to be deleted). If only a path is given, this path identifies the entry 5352 * directly. If both \a fd and \a path are given and the path is absolute, 5353 * \a fd is ignored; a relative path is reckoned off of the directory (!) 5354 * identified by \a fd. 5355 * 5356 * \param fd The FD. May be < 0. 5357 * \param path The absolute or relative path. Must not be \c NULL. 5358 * \return \c FSSH_B_OK, if the entry has been removed successfully, another 5359 * error code otherwise. 5360 */ 5361 5362fssh_status_t 5363_kern_unlink(int fd, const char *path) 5364{ 5365 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5366 if (pathBuffer.InitCheck() != FSSH_B_OK) 5367 return FSSH_B_NO_MEMORY; 5368 5369 return common_unlink(fd, pathBuffer.LockBuffer(), true); 5370} 5371 5372 5373/** \brief Moves an entry specified by a FD + path pair to a an entry specified 5374 * by another FD + path pair. 5375 * 5376 * \a oldPath and \a newPath must always be specified (they contain at least 5377 * the name of the entry). If only a path is given, this path identifies the 5378 * entry directly. If both a FD and a path are given and the path is absolute, 5379 * the FD is ignored; a relative path is reckoned off of the directory (!) 5380 * identified by the respective FD. 5381 * 5382 * \param oldFD The FD of the old location. May be < 0. 5383 * \param oldPath The absolute or relative path of the old location. Must not 5384 * be \c NULL. 5385 * \param newFD The FD of the new location. May be < 0. 5386 * \param newPath The absolute or relative path of the new location. Must not 5387 * be \c NULL. 5388 * \return \c FSSH_B_OK, if the entry has been moved successfully, another 5389 * error code otherwise. 5390 */ 5391 5392fssh_status_t 5393_kern_rename(int oldFD, const char *oldPath, int newFD, const char *newPath) 5394{ 5395 KPath oldPathBuffer(oldPath, false, FSSH_B_PATH_NAME_LENGTH + 1); 5396 KPath newPathBuffer(newPath, false, FSSH_B_PATH_NAME_LENGTH + 1); 5397 if (oldPathBuffer.InitCheck() != FSSH_B_OK || newPathBuffer.InitCheck() != FSSH_B_OK) 5398 return FSSH_B_NO_MEMORY; 5399 5400 return common_rename(oldFD, oldPathBuffer.LockBuffer(), 5401 newFD, newPathBuffer.LockBuffer(), true); 5402} 5403 5404 5405fssh_status_t 5406_kern_access(const char *path, int mode) 5407{ 5408 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5409 if (pathBuffer.InitCheck() != FSSH_B_OK) 5410 return FSSH_B_NO_MEMORY; 5411 5412 return common_access(pathBuffer.LockBuffer(), mode, true); 5413} 5414 5415 5416/** \brief Reads stat data of an entity specified by a FD + path pair. 5417 * 5418 * If only \a fd is given, the stat operation associated with the type 5419 * of the FD (node, attr, attr dir etc.) is performed. If only \a path is 5420 * given, this path identifies the entry for whose node to retrieve the 5421 * stat data. If both \a fd and \a path are given and the path is absolute, 5422 * \a fd is ignored; a relative path is reckoned off of the directory (!) 5423 * identified by \a fd and specifies the entry whose stat data shall be 5424 * retrieved. 5425 * 5426 * \param fd The FD. May be < 0. 5427 * \param path The absolute or relative path. Must not be \c NULL. 5428 * \param traverseLeafLink If \a path is given, \c true specifies that the 5429 * function shall not stick to symlinks, but traverse them. 5430 * \param stat The buffer the stat data shall be written into. 5431 * \param statSize The size of the supplied stat buffer. 5432 * \return \c FSSH_B_OK, if the the stat data have been read successfully, another 5433 * error code otherwise. 5434 */ 5435 5436fssh_status_t 5437_kern_read_stat(int fd, const char *path, bool traverseLeafLink, 5438 fssh_struct_stat *stat, fssh_size_t statSize) 5439{ 5440 fssh_struct_stat completeStat; 5441 fssh_struct_stat *originalStat = NULL; 5442 fssh_status_t status; 5443 5444 if (statSize > sizeof(fssh_struct_stat)) 5445 return FSSH_B_BAD_VALUE; 5446 5447 // this supports different stat extensions 5448 if (statSize < sizeof(fssh_struct_stat)) { 5449 originalStat = stat; 5450 stat = &completeStat; 5451 } 5452 5453 if (path) { 5454 // path given: get the stat of the node referred to by (fd, path) 5455 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5456 if (pathBuffer.InitCheck() != FSSH_B_OK) 5457 return FSSH_B_NO_MEMORY; 5458 5459 status = common_path_read_stat(fd, pathBuffer.LockBuffer(), 5460 traverseLeafLink, stat, true); 5461 } else { 5462 // no path given: get the FD and use the FD operation 5463 struct file_descriptor *descriptor 5464 = get_fd(get_current_io_context(true), fd); 5465 if (descriptor == NULL) 5466 return FSSH_B_FILE_ERROR; 5467 5468 if (descriptor->ops->fd_read_stat) 5469 status = descriptor->ops->fd_read_stat(descriptor, stat); 5470 else 5471 status = FSSH_EOPNOTSUPP; 5472 5473 put_fd(descriptor); 5474 } 5475 5476 if (status == FSSH_B_OK && originalStat != NULL) 5477 fssh_memcpy(originalStat, stat, statSize); 5478 5479 return status; 5480} 5481 5482 5483/** \brief Writes stat data of an entity specified by a FD + path pair. 5484 * 5485 * If only \a fd is given, the stat operation associated with the type 5486 * of the FD (node, attr, attr dir etc.) is performed. If only \a path is 5487 * given, this path identifies the entry for whose node to write the 5488 * stat data. If both \a fd and \a path are given and the path is absolute, 5489 * \a fd is ignored; a relative path is reckoned off of the directory (!) 5490 * identified by \a fd and specifies the entry whose stat data shall be 5491 * written. 5492 * 5493 * \param fd The FD. May be < 0. 5494 * \param path The absolute or relative path. Must not be \c NULL. 5495 * \param traverseLeafLink If \a path is given, \c true specifies that the 5496 * function shall not stick to symlinks, but traverse them. 5497 * \param stat The buffer containing the stat data to be written. 5498 * \param statSize The size of the supplied stat buffer. 5499 * \param statMask A mask specifying which parts of the stat data shall be 5500 * written. 5501 * \return \c FSSH_B_OK, if the the stat data have been written successfully, 5502 * another error code otherwise. 5503 */ 5504 5505fssh_status_t 5506_kern_write_stat(int fd, const char *path, bool traverseLeafLink, 5507 const fssh_struct_stat *stat, fssh_size_t statSize, int statMask) 5508{ 5509 fssh_struct_stat completeStat; 5510 5511 if (statSize > sizeof(fssh_struct_stat)) 5512 return FSSH_B_BAD_VALUE; 5513 5514 // this supports different stat extensions 5515 if (statSize < sizeof(fssh_struct_stat)) { 5516 fssh_memset((uint8_t *)&completeStat + statSize, 0, sizeof(fssh_struct_stat) - statSize); 5517 fssh_memcpy(&completeStat, stat, statSize); 5518 stat = &completeStat; 5519 } 5520 5521 fssh_status_t status; 5522 5523 if (path) { 5524 // path given: write the stat of the node referred to by (fd, path) 5525 KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1); 5526 if (pathBuffer.InitCheck() != FSSH_B_OK) 5527 return FSSH_B_NO_MEMORY; 5528 5529 status = common_path_write_stat(fd, pathBuffer.LockBuffer(), 5530 traverseLeafLink, stat, statMask, true); 5531 } else { 5532 // no path given: get the FD and use the FD operation 5533 struct file_descriptor *descriptor 5534 = get_fd(get_current_io_context(true), fd); 5535 if (descriptor == NULL) 5536 return FSSH_B_FILE_ERROR; 5537 5538 if (descriptor->ops->fd_write_stat) 5539 status = descriptor->ops->fd_write_stat(descriptor, stat, statMask); 5540 else 5541 status = FSSH_EOPNOTSUPP; 5542 5543 put_fd(descriptor); 5544 } 5545 5546 return status; 5547} 5548 5549 5550int 5551_kern_open_attr_dir(int fd, const char *path) 5552{ 5553 KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); 5554 if (pathBuffer.InitCheck() != FSSH_B_OK) 5555 return FSSH_B_NO_MEMORY; 5556 5557 if (path != NULL) 5558 pathBuffer.SetTo(path); 5559 5560 return attr_dir_open(fd, path ? pathBuffer.LockBuffer() : NULL, true); 5561} 5562 5563 5564int 5565_kern_create_attr(int fd, const char *name, uint32_t type, int openMode) 5566{ 5567 return attr_create(fd, name, type, openMode, true); 5568} 5569 5570 5571int 5572_kern_open_attr(int fd, const char *name, int openMode) 5573{ 5574 return attr_open(fd, name, openMode, true); 5575} 5576 5577 5578fssh_status_t 5579_kern_remove_attr(int fd, const char *name) 5580{ 5581 return attr_remove(fd, name, true); 5582} 5583 5584 5585fssh_status_t 5586_kern_rename_attr(int fromFile, const char *fromName, int toFile, const char *toName) 5587{ 5588 return attr_rename(fromFile, fromName, toFile, toName, true); 5589} 5590 5591 5592int 5593_kern_open_index_dir(fssh_dev_t device) 5594{ 5595 return index_dir_open(device, true); 5596} 5597 5598 5599fssh_status_t 5600_kern_create_index(fssh_dev_t device, const char *name, uint32_t type, uint32_t flags) 5601{ 5602 return index_create(device, name, type, flags, true); 5603} 5604 5605 5606fssh_status_t 5607_kern_read_index_stat(fssh_dev_t device, const char *name, fssh_struct_stat *stat) 5608{ 5609 return index_name_read_stat(device, name, stat, true); 5610} 5611 5612 5613fssh_status_t 5614_kern_remove_index(fssh_dev_t device, const char *name) 5615{ 5616 return index_remove(device, name, true); 5617} 5618 5619 5620fssh_status_t 5621_kern_getcwd(char *buffer, fssh_size_t size) 5622{ 5623 TRACE(("_kern_getcwd: buf %p, %ld\n", buffer, size)); 5624 5625 // Call vfs to get current working directory 5626 return get_cwd(buffer, size, true); 5627} 5628 5629 5630fssh_status_t 5631_kern_setcwd(int fd, const char *path) 5632{ 5633 KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1); 5634 if (pathBuffer.InitCheck() != FSSH_B_OK) 5635 return FSSH_B_NO_MEMORY; 5636 5637 if (path != NULL) 5638 pathBuffer.SetTo(path); 5639 5640 return set_cwd(fd, path != NULL ? pathBuffer.LockBuffer() : NULL, true); 5641} 5642 5643 5644fssh_status_t 5645_kern_initialize_volume(const char* fsName, const char *partition, 5646 const char *name, const char *parameters) 5647{ 5648 if (!fsName || ! partition) 5649 return FSSH_B_BAD_VALUE; 5650 5651 // The partition argument should point to a real file/device. 5652 5653 // open partition 5654 int fd = fssh_open(partition, FSSH_O_RDWR); 5655 if (fd < 0) 5656 return fssh_errno; 5657 5658 // get the file system module 5659 fssh_file_system_module_info* fsModule = get_file_system(fsName); 5660 if (fsModule == NULL) { 5661 fssh_close(fd); 5662 return FSSH_ENODEV; 5663 } 5664 5665 // initialize 5666 fssh_status_t status; 5667 if (fsModule->initialize) { 5668 status = (*fsModule->initialize)(fd, -1, name, parameters, 0, -1); 5669 // We've got no partition or job IDs -- the FS will hopefully 5670 // ignore that. 5671 // TODO: Get the actual size! 5672 } else 5673 status = FSSH_B_NOT_SUPPORTED; 5674 5675 // put the file system module, close partition 5676 put_file_system(fsModule); 5677 fssh_close(fd); 5678 5679 return status; 5680} 5681 5682 5683fssh_status_t 5684_kern_entry_ref_to_path(fssh_dev_t device, fssh_ino_t inode, const char *leaf, 5685 char* path, fssh_size_t pathLength) 5686{ 5687 return vfs_entry_ref_to_path(device, inode, leaf, path, pathLength); 5688} 5689 5690 5691int 5692_kern_open_query(fssh_dev_t device, const char *query, fssh_size_t queryLength, 5693 uint32_t flags, fssh_port_id port, int32_t token) 5694{ 5695 return query_open(device, query, flags, port, token, false); 5696} 5697 5698 5699} // namespace FSShell 5700 5701 5702#include "vfs_request_io.cpp" 5703