#include "vfs.h"
#include <new>
#include <stdlib.h>
#include <string.h>
#include "fd.h"
#include "fssh_atomic.h"
#include "fssh_defs.h"
#include "fssh_dirent.h"
#include "fssh_errno.h"
#include "fssh_fcntl.h"
#include "fssh_fs_info.h"
#include "fssh_fs_volume.h"
#include "fssh_kernel_export.h"
#include "fssh_module.h"
#include "fssh_node_monitor.h"
#include "fssh_stat.h"
#include "fssh_stdio.h"
#include "fssh_string.h"
#include "fssh_uio.h"
#include "fssh_unistd.h"
#include "hash.h"
#include "KPath.h"
#include "posix_compatibility.h"
#include "syscalls.h"
#ifdef TRACE_VFS
# define TRACE(x) fssh_dprintf x
# define FUNCTION(x) fssh_dprintf x
#else
# define TRACE(x) ;
# define FUNCTION(x) ;
#endif
# define CALLED() TRACE((__PRETTY_FUNCTION__));
#define ADD_DEBUGGER_COMMANDS
#define ASSERT_LOCKED_MUTEX(x)
#define ASSERT(x)
namespace FSShell {
#define HAS_FS_CALL(vnode, op) (vnode->ops->op != NULL)
#define HAS_FS_MOUNT_CALL(mount, op) (mount->volume->ops->op != NULL)
#define FS_CALL(vnode, op, params...) \
vnode->ops->op(vnode->mount->volume, vnode, params)
#define FS_CALL_NO_PARAMS(vnode, op) \
vnode->ops->op(vnode->mount->volume, vnode)
#define FS_MOUNT_CALL(mount, op, params...) \
mount->volume->ops->op(mount->volume, params)
#define FS_MOUNT_CALL_NO_PARAMS(mount, op) \
mount->volume->ops->op(mount->volume)
const static uint32_t kMaxUnusedVnodes = 16;
struct vnode : fssh_fs_vnode {
struct vnode *next;
vm_cache_ref *cache;
fssh_mount_id device;
list_link mount_link;
list_link unused_link;
fssh_vnode_id id;
struct fs_mount *mount;
struct vnode *covered_by;
int32_t ref_count;
uint32_t type : 29;
uint32_t remove : 1;
uint32_t busy : 1;
uint32_t unpublished : 1;
struct file_descriptor *mandatory_locked_by;
};
struct vnode_hash_key {
fssh_mount_id device;
fssh_vnode_id vnode;
};
struct fs_mount {
struct fs_mount *next;
fssh_file_system_module_info *fs;
fssh_mount_id id;
fssh_fs_volume *volume;
char *device_name;
char *fs_name;
fssh_recursive_lock rlock;
struct vnode *root_vnode;
struct vnode *covers_vnode;
struct list vnodes;
bool unmounting;
bool owns_file_device;
};
static fssh_mutex sFileSystemsMutex;
static fssh_mutex sMountMutex;
static fssh_recursive_lock sMountOpLock;
static fssh_mutex sVnodeCoveredByMutex;
static fssh_mutex sVnodeMutex;
#define VNODE_HASH_TABLE_SIZE 1024
static hash_table *sVnodeTable;
static list sUnusedVnodeList;
static uint32_t sUnusedVnodes = 0;
static struct vnode *sRoot;
#define MOUNTS_HASH_TABLE_SIZE 16
static hash_table *sMountsTable;
static fssh_mount_id sNextMountID = 1;
#define MAX_TEMP_IO_VECS 8
fssh_mode_t __fssh_gUmask = 022;
static fssh_status_t file_read(struct file_descriptor *, fssh_off_t pos,
void *buffer, fssh_size_t *);
static fssh_status_t file_write(struct file_descriptor *, fssh_off_t pos,
const void *buffer, fssh_size_t *);
static fssh_off_t file_seek(struct file_descriptor *, fssh_off_t pos,
int seek_type);
static void file_free_fd(struct file_descriptor *);
static fssh_status_t file_close(struct file_descriptor *);
static fssh_status_t dir_read(struct file_descriptor *,
struct fssh_dirent *buffer, fssh_size_t bufferSize,
uint32_t *_count);
static fssh_status_t dir_read(struct vnode *vnode, void *cookie,
struct fssh_dirent *buffer, fssh_size_t bufferSize,
uint32_t *_count);
static fssh_status_t dir_rewind(struct file_descriptor *);
static void dir_free_fd(struct file_descriptor *);
static fssh_status_t dir_close(struct file_descriptor *);
static fssh_status_t attr_dir_read(struct file_descriptor *,
struct fssh_dirent *buffer, fssh_size_t bufferSize,
uint32_t *_count);
static fssh_status_t attr_dir_rewind(struct file_descriptor *);
static void attr_dir_free_fd(struct file_descriptor *);
static fssh_status_t attr_dir_close(struct file_descriptor *);
static fssh_status_t attr_read(struct file_descriptor *, fssh_off_t pos,
void *buffer, fssh_size_t *);
static fssh_status_t attr_write(struct file_descriptor *, fssh_off_t pos,
const void *buffer, fssh_size_t *);
static fssh_off_t attr_seek(struct file_descriptor *, fssh_off_t pos,
int seek_type);
static void attr_free_fd(struct file_descriptor *);
static fssh_status_t attr_close(struct file_descriptor *);
static fssh_status_t attr_read_stat(struct file_descriptor *,
struct fssh_stat *);
static fssh_status_t attr_write_stat(struct file_descriptor *,
const struct fssh_stat *, int statMask);
static fssh_status_t index_dir_read(struct file_descriptor *,
struct fssh_dirent *buffer, fssh_size_t bufferSize,
uint32_t *_count);
static fssh_status_t index_dir_rewind(struct file_descriptor *);
static void index_dir_free_fd(struct file_descriptor *);
static fssh_status_t index_dir_close(struct file_descriptor *);
static fssh_status_t query_read(struct file_descriptor *,
struct fssh_dirent *buffer, fssh_size_t bufferSize,
uint32_t *_count);
static fssh_status_t query_rewind(struct file_descriptor *);
static void query_free_fd(struct file_descriptor *);
static fssh_status_t query_close(struct file_descriptor *);
static fssh_status_t common_ioctl(struct file_descriptor *, uint32_t, void *buf,
fssh_size_t len);
static fssh_status_t common_read_stat(struct file_descriptor *,
struct fssh_stat *);
static fssh_status_t common_write_stat(struct file_descriptor *,
const struct fssh_stat *, int statMask);
static fssh_status_t vnode_path_to_vnode(struct vnode *vnode, char *path,
bool traverseLeafLink, int count, struct vnode **_vnode,
fssh_vnode_id *_parentID);
static fssh_status_t dir_vnode_to_path(struct vnode *vnode, char *buffer,
fssh_size_t bufferSize);
static fssh_status_t fd_and_path_to_vnode(int fd, char *path,
bool traverseLeafLink, struct vnode **_vnode,
fssh_vnode_id *_parentID, bool kernel);
static void inc_vnode_ref_count(struct vnode *vnode);
static fssh_status_t dec_vnode_ref_count(struct vnode *vnode, bool reenter);
static inline void put_vnode(struct vnode *vnode);
static struct fd_ops sFileOps = {
file_read,
file_write,
file_seek,
common_ioctl,
NULL,
NULL,
NULL,
NULL,
common_read_stat,
common_write_stat,
file_close,
file_free_fd
};
static struct fd_ops sDirectoryOps = {
NULL,
NULL,
NULL,
common_ioctl,
NULL,
NULL,
dir_read,
dir_rewind,
common_read_stat,
common_write_stat,
dir_close,
dir_free_fd
};
static struct fd_ops sAttributeDirectoryOps = {
NULL,
NULL,
NULL,
common_ioctl,
NULL,
NULL,
attr_dir_read,
attr_dir_rewind,
common_read_stat,
common_write_stat,
attr_dir_close,
attr_dir_free_fd
};
static struct fd_ops sAttributeOps = {
attr_read,
attr_write,
attr_seek,
common_ioctl,
NULL,
NULL,
NULL,
NULL,
attr_read_stat,
attr_write_stat,
attr_close,
attr_free_fd
};
static struct fd_ops sIndexDirectoryOps = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
index_dir_read,
index_dir_rewind,
NULL,
NULL,
index_dir_close,
index_dir_free_fd
};
#if 0
static struct fd_ops sIndexOps = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
index_read_stat,
NULL,
NULL,
NULL
};
#endif
static struct fd_ops sQueryOps = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
query_read,
query_rewind,
NULL,
NULL,
query_close,
query_free_fd
};
class VNodePutter {
public:
VNodePutter(struct vnode *vnode = NULL) : fVNode(vnode) {}
~VNodePutter()
{
Put();
}
void SetTo(struct vnode *vnode)
{
Put();
fVNode = vnode;
}
void Put()
{
if (fVNode) {
put_vnode(fVNode);
fVNode = NULL;
}
}
struct vnode *Detach()
{
struct vnode *vnode = fVNode;
fVNode = NULL;
return vnode;
}
private:
struct vnode *fVNode;
};
static int
mount_compare(void *_m, const void *_key)
{
struct fs_mount *mount = (fs_mount *)_m;
const fssh_mount_id *id = (fssh_mount_id *)_key;
if (mount->id == *id)
return 0;
return -1;
}
static uint32_t
mount_hash(void *_m, const void *_key, uint32_t range)
{
struct fs_mount *mount = (fs_mount *)_m;
const fssh_mount_id *id = (fssh_mount_id *)_key;
if (mount)
return mount->id % range;
return (uint32_t)*id % range;
}
static struct fs_mount *
find_mount(fssh_mount_id id)
{
ASSERT_LOCKED_MUTEX(&sMountMutex);
return (fs_mount *)hash_lookup(sMountsTable, (void *)&id);
}
static fssh_status_t
get_mount(fssh_mount_id id, struct fs_mount **_mount)
{
MutexLocker locker(&sMountMutex);
struct fs_mount *mount = find_mount(id);
if (mount == NULL)
return FSSH_B_BAD_VALUE;
if (mount->root_vnode == NULL) {
return FSSH_B_BUSY;
}
inc_vnode_ref_count(mount->root_vnode);
*_mount = mount;
return FSSH_B_OK;
}
static void
put_mount(struct fs_mount *mount)
{
if (mount)
put_vnode(mount->root_vnode);
}
static fssh_status_t
put_file_system(fssh_file_system_module_info *fs)
{
return fssh_put_module(fs->info.name);
}
static fssh_file_system_module_info *
get_file_system(const char *fsName)
{
char name[FSSH_B_FILE_NAME_LENGTH];
if (fssh_strncmp(fsName, "file_systems/", fssh_strlen("file_systems/"))) {
fssh_snprintf(name, sizeof(name), "file_systems/%s/v1", fsName);
fsName = NULL;
}
fssh_file_system_module_info *info;
if (fssh_get_module(fsName ? fsName : name, (fssh_module_info **)&info) != FSSH_B_OK)
return NULL;
return info;
}
static char *
get_file_system_name(const char *fsName)
{
const fssh_size_t length = fssh_strlen("file_systems/");
if (fssh_strncmp(fsName, "file_systems/", length)) {
return fssh_strdup(fsName);
}
fsName += length;
const char *end = fssh_strchr(fsName, '/');
if (end == NULL) {
return fssh_strdup(fsName);
}
char *name = (char *)malloc(end + 1 - fsName);
if (name == NULL)
return NULL;
fssh_strlcpy(name, fsName, end + 1 - fsName);
return name;
}
static int
vnode_compare(void *_vnode, const void *_key)
{
struct vnode *vnode = (struct vnode *)_vnode;
const struct vnode_hash_key *key = (vnode_hash_key *)_key;
if (vnode->device == key->device && vnode->id == key->vnode)
return 0;
return -1;
}
static uint32_t
vnode_hash(void *_vnode, const void *_key, uint32_t range)
{
struct vnode *vnode = (struct vnode *)_vnode;
const struct vnode_hash_key *key = (vnode_hash_key *)_key;
#define VHASH(mountid, vnodeid) (((uint32_t)((vnodeid) >> 32) + (uint32_t)(vnodeid)) ^ (uint32_t)(mountid))
if (vnode != NULL)
return VHASH(vnode->device, vnode->id) % range;
return VHASH(key->device, key->vnode) % range;
#undef VHASH
}
static void
add_vnode_to_mount_list(struct vnode *vnode, struct fs_mount *mount)
{
fssh_recursive_lock_lock(&mount->rlock);
list_add_link_to_head(&mount->vnodes, &vnode->mount_link);
fssh_recursive_lock_unlock(&mount->rlock);
}
static void
remove_vnode_from_mount_list(struct vnode *vnode, struct fs_mount *mount)
{
fssh_recursive_lock_lock(&mount->rlock);
list_remove_link(&vnode->mount_link);
vnode->mount_link.next = vnode->mount_link.prev = NULL;
fssh_recursive_lock_unlock(&mount->rlock);
}
static fssh_status_t
create_new_vnode(struct vnode **_vnode, fssh_mount_id mountID, fssh_vnode_id vnodeID)
{
CALLED();
struct vnode *vnode = (struct vnode *)malloc(sizeof(struct vnode));
if (vnode == NULL)
return FSSH_B_NO_MEMORY;
fssh_memset(vnode, 0, sizeof(struct vnode));
vnode->device = mountID;
vnode->id = vnodeID;
fssh_mutex_lock(&sMountMutex);
vnode->mount = find_mount(mountID);
if (!vnode->mount || vnode->mount->unmounting) {
fssh_mutex_unlock(&sMountMutex);
free(vnode);
return FSSH_B_ENTRY_NOT_FOUND;
}
hash_insert(sVnodeTable, vnode);
add_vnode_to_mount_list(vnode, vnode->mount);
fssh_mutex_unlock(&sMountMutex);
vnode->ref_count = 1;
*_vnode = vnode;
return FSSH_B_OK;
}
static void
free_vnode(struct vnode *vnode, bool reenter)
{
ASSERT(vnode->ref_count == 0 && vnode->busy);
if (!vnode->remove && HAS_FS_CALL(vnode, fsync))
FS_CALL(vnode, fsync, false);
if (!vnode->unpublished) {
if (vnode->remove)
FS_CALL(vnode, remove_vnode, reenter);
else
FS_CALL(vnode, put_vnode, reenter);
}
fssh_mutex_lock(&sVnodeMutex);
hash_remove(sVnodeTable, vnode);
fssh_mutex_unlock(&sVnodeMutex);
remove_vnode_from_mount_list(vnode, vnode->mount);
free(vnode);
}
static fssh_status_t
dec_vnode_ref_count(struct vnode *vnode, bool reenter)
{
fssh_mutex_lock(&sVnodeMutex);
int32_t oldRefCount = fssh_atomic_add(&vnode->ref_count, -1);
TRACE(("dec_vnode_ref_count: vnode %p, ref now %ld\n", vnode, vnode->ref_count));
if (oldRefCount == 1) {
if (vnode->busy)
fssh_panic("dec_vnode_ref_count: called on busy vnode %p\n", vnode);
bool freeNode = false;
if (vnode->remove) {
vnode->busy = true;
freeNode = true;
} else {
list_add_item(&sUnusedVnodeList, vnode);
if (++sUnusedVnodes > kMaxUnusedVnodes) {
vnode = (struct vnode *)list_remove_head_item(&sUnusedVnodeList);
vnode->busy = true;
freeNode = true;
sUnusedVnodes--;
}
}
fssh_mutex_unlock(&sVnodeMutex);
if (freeNode)
free_vnode(vnode, reenter);
} else
fssh_mutex_unlock(&sVnodeMutex);
return FSSH_B_OK;
}
static void
inc_vnode_ref_count(struct vnode *vnode)
{
fssh_atomic_add(&vnode->ref_count, 1);
TRACE(("inc_vnode_ref_count: vnode %p, ref now %ld\n", vnode, vnode->ref_count));
}
static struct vnode *
lookup_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID)
{
struct vnode_hash_key key;
key.device = mountID;
key.vnode = vnodeID;
return (vnode *)hash_lookup(sVnodeTable, &key);
}
static fssh_status_t
get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, struct vnode **_vnode, int reenter)
{
FUNCTION(("get_vnode: mountid %ld vnid 0x%Lx %p\n", mountID, vnodeID, _vnode));
fssh_mutex_lock(&sVnodeMutex);
int32_t tries = 300;
restart:
struct vnode *vnode = lookup_vnode(mountID, vnodeID);
if (vnode && vnode->busy) {
fssh_mutex_unlock(&sVnodeMutex);
if (--tries < 0) {
fssh_panic("vnode %d:%" FSSH_B_PRIdINO " is not becoming unbusy!\n",
(int)mountID, vnodeID);
return FSSH_B_BUSY;
}
fssh_snooze(10000);
fssh_mutex_lock(&sVnodeMutex);
goto restart;
}
TRACE(("get_vnode: tried to lookup vnode, got %p\n", vnode));
fssh_status_t status;
if (vnode) {
if (vnode->ref_count == 0) {
list_remove_item(&sUnusedVnodeList, vnode);
sUnusedVnodes--;
}
inc_vnode_ref_count(vnode);
} else {
status = create_new_vnode(&vnode, mountID, vnodeID);
if (status < FSSH_B_OK)
goto err;
vnode->busy = true;
fssh_mutex_unlock(&sVnodeMutex);
int type;
uint32_t flags;
status = FS_MOUNT_CALL(vnode->mount, get_vnode, vnodeID, vnode, &type,
&flags, reenter);
if (status == FSSH_B_OK && vnode->private_node == NULL)
status = FSSH_B_BAD_VALUE;
fssh_mutex_lock(&sVnodeMutex);
if (status < FSSH_B_OK)
goto err1;
vnode->type = type;
vnode->busy = false;
}
fssh_mutex_unlock(&sVnodeMutex);
TRACE(("get_vnode: returning %p\n", vnode));
*_vnode = vnode;
return FSSH_B_OK;
err1:
hash_remove(sVnodeTable, vnode);
remove_vnode_from_mount_list(vnode, vnode->mount);
err:
fssh_mutex_unlock(&sVnodeMutex);
if (vnode)
free(vnode);
return status;
}
static inline void
put_vnode(struct vnode *vnode)
{
dec_vnode_ref_count(vnode, false);
}
void
disconnect_mount_or_vnode_fds(struct fs_mount *mount,
struct vnode *vnodeToDisconnect)
{
}
static struct vnode *
resolve_mount_point_to_volume_root(struct vnode *vnode)
{
if (!vnode)
return NULL;
struct vnode *volumeRoot = NULL;
fssh_mutex_lock(&sVnodeCoveredByMutex);
if (vnode->covered_by) {
volumeRoot = vnode->covered_by;
inc_vnode_ref_count(volumeRoot);
}
fssh_mutex_unlock(&sVnodeCoveredByMutex);
return volumeRoot;
}
fssh_status_t
resolve_mount_point_to_volume_root(fssh_mount_id mountID, fssh_vnode_id nodeID,
fssh_mount_id *resolvedMountID, fssh_vnode_id *resolvedNodeID)
{
struct vnode *node;
fssh_status_t error = get_vnode(mountID, nodeID, &node, false);
if (error != FSSH_B_OK)
return error;
struct vnode *resolvedNode = resolve_mount_point_to_volume_root(node);
if (resolvedNode) {
put_vnode(node);
node = resolvedNode;
}
*resolvedMountID = node->device;
*resolvedNodeID = node->id;
put_vnode(node);
return FSSH_B_OK;
}
static struct vnode *
resolve_volume_root_to_mount_point(struct vnode *vnode)
{
if (!vnode)
return NULL;
struct vnode *mountPoint = NULL;
struct fs_mount *mount = vnode->mount;
if (vnode == mount->root_vnode && mount->covers_vnode) {
mountPoint = mount->covers_vnode;
inc_vnode_ref_count(mountPoint);
}
return mountPoint;
}
static fssh_status_t
get_dir_path_and_leaf(char *path, char *filename)
{
char *p = fssh_strrchr(path, '/');
FUNCTION(("get_dir_path_and_leaf(path = %s)\n", path));
if (!p) {
if (fssh_strlcpy(filename, path, FSSH_B_FILE_NAME_LENGTH) >= FSSH_B_FILE_NAME_LENGTH)
return FSSH_B_NAME_TOO_LONG;
fssh_strcpy(path, ".");
} else {
p++;
if (*p == '\0') {
fssh_strcpy(filename, ".");
} else {
if (fssh_strlcpy(filename, p, FSSH_B_FILE_NAME_LENGTH)
>= FSSH_B_FILE_NAME_LENGTH) {
return FSSH_B_NAME_TOO_LONG;
}
}
p[0] = '.';
p[1] = '\0';
}
return FSSH_B_OK;
}
static fssh_status_t
entry_ref_to_vnode(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, struct vnode **_vnode)
{
char clonedName[FSSH_B_FILE_NAME_LENGTH + 1];
if (fssh_strlcpy(clonedName, name, FSSH_B_FILE_NAME_LENGTH) >= FSSH_B_FILE_NAME_LENGTH)
return FSSH_B_NAME_TOO_LONG;
struct vnode *directory;
fssh_status_t status = get_vnode(mountID, directoryID, &directory, false);
if (status < 0)
return status;
return vnode_path_to_vnode(directory, clonedName, false, 0, _vnode, NULL);
}
static fssh_status_t
lookup_dir_entry(struct vnode* dir, const char* name, struct vnode** _vnode)
{
fssh_ino_t id;
fssh_status_t status = FS_CALL(dir, lookup, name, &id);
if (status < FSSH_B_OK)
return status;
fssh_mutex_lock(&sVnodeMutex);
*_vnode = lookup_vnode(dir->device, id);
fssh_mutex_unlock(&sVnodeMutex);
if (*_vnode == NULL) {
fssh_panic("lookup_dir_entry(): could not lookup vnode (mountid %d "
"vnid %" FSSH_B_PRIdINO ")\n", (int)dir->device, id);
return FSSH_B_ENTRY_NOT_FOUND;
}
return FSSH_B_OK;
}
static fssh_status_t
vnode_path_to_vnode(struct vnode *vnode, char *path, bool traverseLeafLink,
int count, struct vnode **_vnode, fssh_vnode_id *_parentID)
{
fssh_status_t status = 0;
fssh_vnode_id lastParentID = vnode->id;
FUNCTION(("vnode_path_to_vnode(vnode = %p, path = %s)\n", vnode, path));
if (path == NULL) {
put_vnode(vnode);
return FSSH_B_BAD_VALUE;
}
while (true) {
struct vnode *nextVnode;
char *nextPath;
TRACE(("vnode_path_to_vnode: top of loop. p = %p, p = '%s'\n", path, path));
if (path[0] == '\0')
break;
for (nextPath = path + 1; *nextPath != '\0' && *nextPath != '/'; nextPath++);
if (*nextPath == '/') {
*nextPath = '\0';
do
nextPath++;
while (*nextPath == '/');
}
if (!fssh_strcmp("..", path)
&& vnode->mount->root_vnode == vnode
&& vnode->mount->covers_vnode) {
nextVnode = vnode->mount->covers_vnode;
inc_vnode_ref_count(nextVnode);
put_vnode(vnode);
vnode = nextVnode;
}
if (HAS_FS_CALL(vnode, access))
status = FS_CALL(vnode, access, FSSH_X_OK);
if (status >= FSSH_B_OK)
status = lookup_dir_entry(vnode, path, &nextVnode);
if (status < FSSH_B_OK) {
put_vnode(vnode);
return status;
}
if (FSSH_S_ISLNK(nextVnode->type)
&& !(!traverseLeafLink && nextPath[0] == '\0')) {
fssh_size_t bufferSize;
char *buffer;
TRACE(("traverse link\n"));
if (count + 1 > FSSH_B_MAX_SYMLINKS) {
status = FSSH_B_LINK_LIMIT;
goto resolve_link_error;
}
buffer = (char *)malloc(bufferSize = FSSH_B_PATH_NAME_LENGTH);
if (buffer == NULL) {
status = FSSH_B_NO_MEMORY;
goto resolve_link_error;
}
if (HAS_FS_CALL(nextVnode, read_symlink)) {
bufferSize--;
status = FS_CALL(nextVnode, read_symlink, buffer,
&bufferSize);
if (status >= 0 && bufferSize < FSSH_B_PATH_NAME_LENGTH)
buffer[bufferSize] = '\0';
} else
status = FSSH_B_BAD_VALUE;
if (status < FSSH_B_OK) {
free(buffer);
resolve_link_error:
put_vnode(vnode);
put_vnode(nextVnode);
return status;
}
put_vnode(nextVnode);
path = buffer;
if (path[0] == '/') {
put_vnode(vnode);
while (*++path == '/')
;
vnode = sRoot;
inc_vnode_ref_count(vnode);
}
inc_vnode_ref_count(vnode);
status = vnode_path_to_vnode(vnode, path, traverseLeafLink, count + 1,
&nextVnode, &lastParentID);
free(buffer);
if (status < FSSH_B_OK) {
put_vnode(vnode);
return status;
}
} else
lastParentID = vnode->id;
put_vnode(vnode);
path = nextPath;
vnode = nextVnode;
struct vnode *mountPoint = resolve_mount_point_to_volume_root(vnode);
if (mountPoint) {
put_vnode(vnode);
vnode = mountPoint;
}
}
*_vnode = vnode;
if (_parentID)
*_parentID = lastParentID;
return FSSH_B_OK;
}
static fssh_status_t
path_to_vnode(char *path, bool traverseLink, struct vnode **_vnode,
fssh_vnode_id *_parentID, bool kernel)
{
struct vnode *start = NULL;
FUNCTION(("path_to_vnode(path = \"%s\")\n", path));
if (!path)
return FSSH_B_BAD_VALUE;
if (*path == '/') {
if (sRoot == NULL) {
return FSSH_B_ERROR;
}
while (*++path == '/')
;
start = sRoot;
inc_vnode_ref_count(start);
} else {
struct io_context *context = get_current_io_context(kernel);
fssh_mutex_lock(&context->io_mutex);
start = context->cwd;
if (start != NULL)
inc_vnode_ref_count(start);
fssh_mutex_unlock(&context->io_mutex);
if (start == NULL)
return FSSH_B_ERROR;
}
return vnode_path_to_vnode(start, path, traverseLink, 0, _vnode, _parentID);
}
static fssh_status_t
path_to_dir_vnode(char *path, struct vnode **_vnode, char *filename, bool kernel)
{
fssh_status_t status = get_dir_path_and_leaf(path, filename);
if (status != FSSH_B_OK)
return status;
return path_to_vnode(path, true, _vnode, NULL, kernel);
}
static fssh_status_t
fd_and_path_to_dir_vnode(int fd, char *path, struct vnode **_vnode,
char *filename, bool kernel)
{
if (!path)
return FSSH_B_BAD_VALUE;
if (fd < 0)
return path_to_dir_vnode(path, _vnode, filename, kernel);
fssh_status_t status = get_dir_path_and_leaf(path, filename);
if (status != FSSH_B_OK)
return status;
return fd_and_path_to_vnode(fd, path, true, _vnode, NULL, kernel);
}
static fssh_status_t
get_vnode_name(struct vnode *vnode, struct vnode *parent,
struct fssh_dirent *buffer, fssh_size_t bufferSize)
{
if (bufferSize < sizeof(struct fssh_dirent))
return FSSH_B_BAD_VALUE;
VNodePutter vnodePutter;
if (vnode->mount->root_vnode == vnode && vnode->mount->covers_vnode != NULL) {
vnode = vnode->mount->covers_vnode;
inc_vnode_ref_count(vnode);
vnodePutter.SetTo(vnode);
}
if (HAS_FS_CALL(vnode, get_vnode_name)) {
return FS_CALL(vnode, get_vnode_name, buffer->d_name,
(char*)buffer + bufferSize - buffer->d_name);
}
if (parent == NULL)
return FSSH_EOPNOTSUPP;
void *cookie;
fssh_status_t status = FS_CALL(parent, open_dir, &cookie);
if (status >= FSSH_B_OK) {
while (true) {
uint32_t num = 1;
status = dir_read(parent, cookie, buffer, bufferSize, &num);
if (status < FSSH_B_OK)
break;
if (num == 0) {
status = FSSH_B_ENTRY_NOT_FOUND;
break;
}
if (vnode->id == buffer->d_ino) {
break;
}
}
FS_CALL(vnode, close_dir, cookie);
FS_CALL(vnode, free_dir_cookie, cookie);
}
return status;
}
static fssh_status_t
get_vnode_name(struct vnode *vnode, struct vnode *parent, char *name,
fssh_size_t nameSize)
{
char buffer[sizeof(struct fssh_dirent) + FSSH_B_FILE_NAME_LENGTH];
struct fssh_dirent *dirent = (struct fssh_dirent *)buffer;
fssh_status_t status = get_vnode_name(vnode, parent, dirent, sizeof(buffer));
if (status != FSSH_B_OK)
return status;
if (fssh_strlcpy(name, dirent->d_name, nameSize) >= nameSize)
return FSSH_B_BUFFER_OVERFLOW;
return FSSH_B_OK;
}
static fssh_status_t
dir_vnode_to_path(struct vnode *vnode, char *buffer, fssh_size_t bufferSize)
{
FUNCTION(("dir_vnode_to_path(%p, %p, %lu)\n", vnode, buffer, bufferSize));
if (vnode == NULL || buffer == NULL)
return FSSH_B_BAD_VALUE;
KPath pathBuffer;
if (pathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
char *path = pathBuffer.LockBuffer();
int32_t insert = pathBuffer.BufferSize();
int32_t maxLevel = 256;
int32_t length;
fssh_status_t status;
inc_vnode_ref_count(vnode);
struct vnode *mountPoint = resolve_volume_root_to_mount_point(vnode);
if (mountPoint) {
put_vnode(vnode);
vnode = mountPoint;
}
path[--insert] = '\0';
while (true) {
char nameBuffer[sizeof(struct fssh_dirent) + FSSH_B_FILE_NAME_LENGTH];
char *name = &((struct fssh_dirent *)nameBuffer)->d_name[0];
struct vnode *parentVnode;
status = lookup_dir_entry(vnode, "..", &parentVnode);
if (status < FSSH_B_OK)
goto out;
status = get_vnode_name(vnode, parentVnode,
(struct fssh_dirent*)nameBuffer, sizeof(nameBuffer));
mountPoint = resolve_volume_root_to_mount_point(parentVnode);
if (mountPoint) {
put_vnode(parentVnode);
parentVnode = mountPoint;
}
bool hitRoot = (parentVnode == vnode);
put_vnode(vnode);
vnode = parentVnode;
if (status < FSSH_B_OK)
goto out;
if (hitRoot) {
break;
}
if (maxLevel-- < 0) {
status = FSSH_ELOOP;
goto out;
}
name[FSSH_B_FILE_NAME_LENGTH - 1] = '\0';
length = fssh_strlen(name);
insert -= length;
if (insert <= 0) {
status = FSSH_ENOBUFS;
goto out;
}
fssh_memcpy(path + insert, name, length);
path[--insert] = '/';
}
if (path[insert] == '\0')
path[--insert] = '/';
TRACE((" path is: %s\n", path + insert));
length = pathBuffer.BufferSize() - insert;
if (length <= (int)bufferSize)
fssh_memcpy(buffer, path + insert, length);
else
status = FSSH_ENOBUFS;
out:
put_vnode(vnode);
return status;
}
static fssh_status_t
check_path(char *to)
{
int32_t length = 0;
while (*to) {
char *begin;
if (*to == '/')
to++, length++;
begin = to;
while (*to != '/' && *to)
to++, length++;
if (to - begin > FSSH_B_FILE_NAME_LENGTH)
return FSSH_B_NAME_TOO_LONG;
}
if (length == 0)
return FSSH_B_ENTRY_NOT_FOUND;
if (*(to - 1) == '/') {
if (length > FSSH_B_PATH_NAME_LENGTH - 2)
return FSSH_B_NAME_TOO_LONG;
to[0] = '.';
to[1] = '\0';
}
return FSSH_B_OK;
}
static struct file_descriptor *
get_fd_and_vnode(int fd, struct vnode **_vnode, bool kernel)
{
struct file_descriptor *descriptor = get_fd(get_current_io_context(kernel), fd);
if (descriptor == NULL)
return NULL;
if (fd_vnode(descriptor) == NULL) {
put_fd(descriptor);
return NULL;
}
*_vnode = descriptor->u.vnode;
return descriptor;
}
static struct vnode *
get_vnode_from_fd(int fd, bool kernel)
{
struct file_descriptor *descriptor;
struct vnode *vnode;
descriptor = get_fd(get_current_io_context(kernel), fd);
if (descriptor == NULL)
return NULL;
vnode = fd_vnode(descriptor);
if (vnode != NULL)
inc_vnode_ref_count(vnode);
put_fd(descriptor);
return vnode;
}
static fssh_status_t
fd_and_path_to_vnode(int fd, char *path, bool traverseLeafLink,
struct vnode **_vnode, fssh_vnode_id *_parentID, bool kernel)
{
if (fd < 0 && !path)
return FSSH_B_BAD_VALUE;
if (fd < 0 || (path != NULL && path[0] == '/')) {
return path_to_vnode(path, traverseLeafLink, _vnode, _parentID, kernel);
}
struct vnode *vnode = get_vnode_from_fd(fd, kernel);
if (!vnode)
return FSSH_B_FILE_ERROR;
if (path != NULL) {
return vnode_path_to_vnode(vnode, path, traverseLeafLink, 0,
_vnode, _parentID);
}
*_vnode = vnode;
if (_parentID)
*_parentID = -1;
return FSSH_B_OK;
}
static int
get_new_fd(int type, struct fs_mount *mount, struct vnode *vnode,
void *cookie, int openMode, bool kernel)
{
struct file_descriptor *descriptor;
int fd;
if (vnode && vnode->mandatory_locked_by != NULL)
return FSSH_B_BUSY;
descriptor = alloc_fd();
if (!descriptor)
return FSSH_B_NO_MEMORY;
if (vnode)
descriptor->u.vnode = vnode;
else
descriptor->u.mount = mount;
descriptor->cookie = cookie;
switch (type) {
case FDTYPE_FILE:
descriptor->ops = &sFileOps;
break;
case FDTYPE_DIR:
descriptor->ops = &sDirectoryOps;
break;
case FDTYPE_ATTR:
descriptor->ops = &sAttributeOps;
break;
case FDTYPE_ATTR_DIR:
descriptor->ops = &sAttributeDirectoryOps;
break;
case FDTYPE_INDEX_DIR:
descriptor->ops = &sIndexDirectoryOps;
break;
case FDTYPE_QUERY:
descriptor->ops = &sQueryOps;
break;
default:
fssh_panic("get_new_fd() called with unknown type %d\n", type);
break;
}
descriptor->type = type;
descriptor->open_mode = openMode;
fd = new_fd(get_current_io_context(kernel), descriptor);
if (fd < 0) {
free(descriptor);
return FSSH_B_NO_MORE_FDS;
}
return fd;
}
static fssh_status_t
common_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs,
fssh_size_t fileVecCount, const fssh_iovec *vecs, fssh_size_t vecCount,
uint32_t *_vecIndex, fssh_size_t *_vecOffset, fssh_size_t *_numBytes,
bool doWrite)
{
if (fileVecCount == 0) {
return FSSH_B_BAD_VALUE;
}
fssh_size_t numBytes = *_numBytes;
uint32_t fileVecIndex;
fssh_size_t vecOffset = *_vecOffset;
uint32_t vecIndex = *_vecIndex;
fssh_status_t status;
fssh_size_t size;
if (!doWrite && vecOffset == 0) {
size = fileVecs[0].length;
if (size > numBytes)
size = numBytes;
status = fssh_read_pages(fd, fileVecs[0].offset, &vecs[vecIndex],
vecCount - vecIndex, &size);
if (status < FSSH_B_OK)
return status;
if ((uint64_t)size > (uint64_t)fileVecs[0].length) {
size = fileVecs[0].length;
}
ASSERT(size <= fileVecs[0].length);
if (size == numBytes)
return FSSH_B_OK;
if ((uint64_t)size != (uint64_t)fileVecs[0].length) {
*_numBytes = size;
return FSSH_B_OK;
}
fileVecIndex = 1;
for (; vecIndex < vecCount; vecIndex++) {
if (size < vecs[vecIndex].iov_len)
break;
size -= vecs[vecIndex].iov_len;
}
vecOffset = size;
} else {
fileVecIndex = 0;
size = 0;
}
fssh_size_t totalSize = size;
fssh_size_t bytesLeft = numBytes - size;
for (; fileVecIndex < fileVecCount; fileVecIndex++) {
const fssh_file_io_vec &fileVec = fileVecs[fileVecIndex];
fssh_off_t fileOffset = fileVec.offset;
fssh_off_t fileLeft = fssh_min_c((uint64_t)fileVec.length, (uint64_t)bytesLeft);
TRACE(("FILE VEC [%lu] length %lld\n", fileVecIndex, fileLeft));
while (fileLeft > 0) {
fssh_iovec tempVecs[MAX_TEMP_IO_VECS];
uint32_t tempCount = 0;
size = 0;
for (size = 0; (uint64_t)size < (uint64_t)fileLeft && vecIndex < vecCount
&& tempCount < MAX_TEMP_IO_VECS;) {
fssh_size_t vecLeft = vecs[vecIndex].iov_len - vecOffset;
if (vecLeft == 0) {
vecOffset = 0;
vecIndex++;
continue;
}
TRACE(("fill vec %ld, offset = %lu, size = %lu\n",
vecIndex, vecOffset, size));
fssh_size_t tempVecSize = fssh_min_c(vecLeft, fileLeft - size);
tempVecs[tempCount].iov_base
= (void *)((fssh_addr_t)vecs[vecIndex].iov_base + vecOffset);
tempVecs[tempCount].iov_len = tempVecSize;
tempCount++;
size += tempVecSize;
vecOffset += tempVecSize;
}
fssh_size_t bytes = size;
if (doWrite) {
status = fssh_write_pages(fd, fileOffset, tempVecs,
tempCount, &bytes);
} else {
status = fssh_read_pages(fd, fileOffset, tempVecs,
tempCount, &bytes);
}
if (status < FSSH_B_OK)
return status;
totalSize += bytes;
bytesLeft -= size;
fileOffset += size;
fileLeft -= size;
if (size != bytes || vecIndex >= vecCount) {
*_numBytes = totalSize;
return FSSH_B_OK;
}
}
}
*_vecIndex = vecIndex;
*_vecOffset = vecOffset;
*_numBytes = totalSize;
return FSSH_B_OK;
}
extern "C" fssh_status_t
fssh_new_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID,
void *privateNode, fssh_fs_vnode_ops *ops)
{
FUNCTION(("new_vnode(volume = %p (%ld), vnodeID = %lld, node = %p)\n",
volume, volume->id, vnodeID, privateNode));
if (privateNode == NULL)
return FSSH_B_BAD_VALUE;
fssh_mutex_lock(&sVnodeMutex);
struct vnode *vnode = lookup_vnode(volume->id, vnodeID);
if (vnode != NULL) {
fssh_panic("vnode %d:%" FSSH_B_PRIdINO " already exists (node = %p, "
"vnode->node = %p)!", (int)volume->id, vnodeID, privateNode,
vnode->private_node);
}
fssh_status_t status = create_new_vnode(&vnode, volume->id, vnodeID);
if (status == FSSH_B_OK) {
vnode->private_node = privateNode;
vnode->ops = ops;
vnode->busy = true;
vnode->unpublished = true;
}
TRACE(("returns: %s\n", strerror(status)));
fssh_mutex_unlock(&sVnodeMutex);
return status;
}
extern "C" fssh_status_t
fssh_publish_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID,
void *privateNode, fssh_fs_vnode_ops *ops, int type, uint32_t flags)
{
CALLED();
MutexLocker locker(sVnodeMutex);
struct vnode *vnode = lookup_vnode(volume->id, vnodeID);
fssh_status_t status = FSSH_B_OK;
if (vnode != NULL && vnode->busy && vnode->unpublished
&& vnode->private_node == privateNode) {
} else if (vnode == NULL && privateNode != NULL) {
status = create_new_vnode(&vnode, volume->id, vnodeID);
if (status == FSSH_B_OK) {
vnode->private_node = privateNode;
vnode->ops = ops;
vnode->busy = true;
vnode->unpublished = true;
}
} else
status = FSSH_B_BAD_VALUE;
if (status == FSSH_B_OK && volume->sub_volume != NULL) {
locker.Unlock();
fssh_fs_volume *subVolume = volume;
while (status == FSSH_B_OK && subVolume->sub_volume != NULL) {
subVolume = subVolume->sub_volume;
status = subVolume->ops->create_sub_vnode(subVolume, vnodeID,
vnode);
}
if (status != FSSH_B_OK) {
while (subVolume->super_volume != volume) {
subVolume = subVolume->super_volume;
subVolume->ops->delete_sub_vnode(subVolume, vnode);
}
}
locker.Lock();
}
if (status == FSSH_B_OK) {
vnode->type = type;
vnode->busy = false;
vnode->unpublished = false;
}
TRACE(("returns: %s\n", strerror(status)));
return status;
}
extern "C" fssh_status_t
fssh_get_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID,
void **privateNode)
{
struct vnode *vnode;
if (volume == NULL)
return FSSH_B_BAD_VALUE;
fssh_status_t status = get_vnode(volume->id, vnodeID, &vnode, true);
if (status < FSSH_B_OK)
return status;
if (HAS_FS_CALL(vnode, get_super_vnode)) {
fssh_fs_vnode resolvedNode;
fssh_status_t status = FS_CALL(vnode, get_super_vnode, volume,
&resolvedNode);
if (status != FSSH_B_OK) {
fssh_panic("get_vnode(): Failed to get super node for vnode %p, "
"volume: %p", vnode, volume);
put_vnode(vnode);
return status;
}
if (privateNode != NULL)
*privateNode = resolvedNode.private_node;
} else if (privateNode != NULL)
*privateNode = vnode->private_node;
return FSSH_B_OK;
}
extern "C" fssh_status_t
fssh_acquire_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID)
{
struct vnode *vnode;
fssh_mutex_lock(&sVnodeMutex);
vnode = lookup_vnode(volume->id, vnodeID);
fssh_mutex_unlock(&sVnodeMutex);
if (vnode == NULL)
return FSSH_B_BAD_VALUE;
inc_vnode_ref_count(vnode);
return FSSH_B_OK;
}
extern "C" fssh_status_t
fssh_put_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID)
{
struct vnode *vnode;
fssh_mutex_lock(&sVnodeMutex);
vnode = lookup_vnode(volume->id, vnodeID);
fssh_mutex_unlock(&sVnodeMutex);
if (vnode == NULL)
return FSSH_B_BAD_VALUE;
dec_vnode_ref_count(vnode, true);
return FSSH_B_OK;
}
extern "C" fssh_status_t
fssh_remove_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID)
{
struct vnode *vnode;
bool remove = false;
MutexLocker locker(sVnodeMutex);
vnode = lookup_vnode(volume->id, vnodeID);
if (vnode == NULL)
return FSSH_B_ENTRY_NOT_FOUND;
if (vnode->covered_by != NULL) {
fssh_mutex_unlock(&sVnodeMutex);
return FSSH_B_BUSY;
}
vnode->remove = true;
if (vnode->unpublished) {
vnode->busy = true;
remove = true;
}
locker.Unlock();
if (remove) {
fssh_atomic_add(&vnode->ref_count, -1);
free_vnode(vnode, true);
}
return FSSH_B_OK;
}
extern "C" fssh_status_t
fssh_unremove_vnode(fssh_fs_volume *volume, fssh_vnode_id vnodeID)
{
struct vnode *vnode;
fssh_mutex_lock(&sVnodeMutex);
vnode = lookup_vnode(volume->id, vnodeID);
if (vnode)
vnode->remove = false;
fssh_mutex_unlock(&sVnodeMutex);
return FSSH_B_OK;
}
extern "C" fssh_status_t
fssh_get_vnode_removed(fssh_fs_volume *volume, fssh_vnode_id vnodeID, bool* removed)
{
fssh_mutex_lock(&sVnodeMutex);
fssh_status_t result;
if (struct vnode* vnode = lookup_vnode(volume->id, vnodeID)) {
if (removed)
*removed = vnode->remove;
result = FSSH_B_OK;
} else
result = FSSH_B_BAD_VALUE;
fssh_mutex_unlock(&sVnodeMutex);
return result;
}
extern "C" fssh_fs_volume*
fssh_volume_for_vnode(fssh_fs_vnode *_vnode)
{
if (_vnode == NULL)
return NULL;
struct vnode* vnode = static_cast<struct vnode*>(_vnode);
return vnode->mount->volume;
}
extern "C" fssh_status_t
fssh_check_access_permissions(int accessMode, fssh_mode_t mode,
fssh_gid_t nodeGroupID, fssh_uid_t nodeUserID)
{
int userPermissions = (mode & FSSH_S_IRWXU) >> 6;
int groupPermissions = (mode & FSSH_S_IRWXG) >> 3;
int otherPermissions = mode & FSSH_S_IRWXO;
int permissions = 0;
fssh_uid_t uid = fssh_geteuid();
if (uid == 0) {
permissions = userPermissions | groupPermissions | otherPermissions
| FSSH_S_IROTH | FSSH_S_IWOTH;
if (FSSH_S_ISDIR(mode))
permissions |= FSSH_S_IXOTH;
} else if (uid == nodeUserID) {
permissions = userPermissions;
} else if (fssh_getegid() == nodeGroupID) {
permissions = groupPermissions;
} else {
permissions = otherPermissions;
}
return (accessMode & ~permissions) == 0 ? FSSH_B_OK : FSSH_B_NOT_ALLOWED;
}
extern "C" fssh_status_t
fssh_check_write_stat_permissions(fssh_gid_t nodeGroupID, fssh_uid_t nodeUserID,
fssh_mode_t nodeMode, uint32_t mask, const struct fssh_stat* stat)
{
uid_t uid = fssh_geteuid();
if (uid == 0)
return FSSH_B_OK;
const bool hasWriteAccess = fssh_check_access_permissions(FSSH_W_OK,
nodeMode, nodeGroupID, nodeUserID) == FSSH_B_OK;
if ((mask & FSSH_B_STAT_SIZE) != 0) {
if (!hasWriteAccess)
return FSSH_B_NOT_ALLOWED;
}
if ((mask & FSSH_B_STAT_UID) != 0) {
if (nodeUserID == uid && stat->fssh_st_uid == uid) {
} else
return FSSH_B_NOT_ALLOWED;
}
if ((mask & FSSH_B_STAT_GID) != 0) {
if (nodeUserID != uid)
return FSSH_B_NOT_ALLOWED;
if (fssh_getegid() != stat->fssh_st_gid)
return FSSH_B_NOT_ALLOWED;
}
if ((mask & FSSH_B_STAT_MODE) != 0) {
if (nodeUserID != uid)
return FSSH_B_NOT_ALLOWED;
}
if ((mask & (FSSH_B_STAT_CREATION_TIME | FSSH_B_STAT_MODIFICATION_TIME
| FSSH_B_STAT_CHANGE_TIME)) != 0) {
if (!hasWriteAccess && nodeUserID != uid)
return FSSH_B_NOT_ALLOWED;
}
return FSSH_B_OK;
}
extern "C" fssh_status_t
fssh_read_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs,
fssh_size_t count, fssh_size_t *_numBytes)
{
fssh_size_t toRead = 0;
for (fssh_size_t i = 0; i < count; i++)
toRead += vecs[i].iov_len;
fssh_iovec* newVecs = NULL;
if (*_numBytes < toRead) {
newVecs = new(std::nothrow) fssh_iovec[count];
if (!newVecs)
return FSSH_B_NO_MEMORY;
fssh_size_t newCount = 0;
for (fssh_size_t i = 0; i < count && toRead > 0; i++) {
fssh_size_t vecLen = fssh_min_c(vecs[i].iov_len, toRead);
newVecs[i].iov_base = vecs[i].iov_base;
newVecs[i].iov_len = vecLen;
toRead -= vecLen;
newCount++;
}
vecs = newVecs;
count = newCount;
}
fssh_ssize_t bytesRead = fssh_readv_pos(fd, pos, vecs, count);
delete[] newVecs;
if (bytesRead < 0)
return fssh_get_errno();
*_numBytes = bytesRead;
return FSSH_B_OK;
}
extern "C" fssh_status_t
fssh_write_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs,
fssh_size_t count, fssh_size_t *_numBytes)
{
fssh_size_t toWrite = 0;
for (fssh_size_t i = 0; i < count; i++)
toWrite += vecs[i].iov_len;
fssh_iovec* newVecs = NULL;
if (*_numBytes < toWrite) {
newVecs = new(std::nothrow) fssh_iovec[count];
if (!newVecs)
return FSSH_B_NO_MEMORY;
fssh_size_t newCount = 0;
for (fssh_size_t i = 0; i < count && toWrite > 0; i++) {
fssh_size_t vecLen = fssh_min_c(vecs[i].iov_len, toWrite);
newVecs[i].iov_base = vecs[i].iov_base;
newVecs[i].iov_len = vecLen;
toWrite -= vecLen;
newCount++;
}
vecs = newVecs;
count = newCount;
}
fssh_ssize_t bytesWritten = fssh_writev_pos(fd, pos, vecs, count);
delete[] newVecs;
if (bytesWritten < 0)
return fssh_get_errno();
*_numBytes = bytesWritten;
return FSSH_B_OK;
}
extern "C" fssh_status_t
fssh_read_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs,
fssh_size_t fileVecCount, const fssh_iovec *vecs, fssh_size_t vecCount,
uint32_t *_vecIndex, fssh_size_t *_vecOffset, fssh_size_t *_bytes)
{
return common_file_io_vec_pages(fd, fileVecs, fileVecCount,
vecs, vecCount, _vecIndex, _vecOffset, _bytes, false);
}
extern "C" fssh_status_t
fssh_write_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs,
fssh_size_t fileVecCount, const fssh_iovec *vecs, fssh_size_t vecCount,
uint32_t *_vecIndex, fssh_size_t *_vecOffset, fssh_size_t *_bytes)
{
return common_file_io_vec_pages(fd, fileVecs, fileVecCount,
vecs, vecCount, _vecIndex, _vecOffset, _bytes, true);
}
extern "C" fssh_status_t
fssh_entry_cache_add(fssh_dev_t mountID, fssh_ino_t dirID, const char* name,
fssh_ino_t nodeID)
{
return FSSH_B_OK;
}
extern "C" fssh_status_t
fssh_entry_cache_add_missing(fssh_dev_t mountID, fssh_ino_t dirID,
const char* name)
{
return FSSH_B_OK;
}
extern "C" fssh_status_t
fssh_entry_cache_remove(fssh_dev_t mountID, fssh_ino_t dirID, const char* name)
{
return FSSH_B_ENTRY_NOT_FOUND;
}
void
vfs_acquire_vnode(void *_vnode)
{
inc_vnode_ref_count((struct vnode *)_vnode);
}
fssh_status_t
vfs_get_cookie_from_fd(int fd, void **_cookie)
{
struct file_descriptor *descriptor;
descriptor = get_fd(get_current_io_context(true), fd);
if (descriptor == NULL)
return FSSH_B_FILE_ERROR;
*_cookie = descriptor->cookie;
return FSSH_B_OK;
}
int
vfs_get_vnode_from_fd(int fd, bool kernel, void **vnode)
{
*vnode = get_vnode_from_fd(fd, kernel);
if (*vnode == NULL)
return FSSH_B_FILE_ERROR;
return FSSH_B_NO_ERROR;
}
fssh_status_t
vfs_get_vnode_from_path(const char *path, bool kernel, void **_vnode)
{
TRACE(("vfs_get_vnode_from_path: entry. path = '%s', kernel %d\n", path, kernel));
KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
if (pathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
char *buffer = pathBuffer.LockBuffer();
fssh_strlcpy(buffer, path, pathBuffer.BufferSize());
struct vnode *vnode;
fssh_status_t status = path_to_vnode(buffer, true, &vnode, NULL, kernel);
if (status < FSSH_B_OK)
return status;
*_vnode = vnode;
return FSSH_B_OK;
}
fssh_status_t
vfs_get_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID, void **_vnode)
{
struct vnode *vnode;
fssh_status_t status = get_vnode(mountID, vnodeID, &vnode, false);
if (status < FSSH_B_OK)
return status;
*_vnode = vnode;
return FSSH_B_OK;
}
fssh_status_t
vfs_read_pages(void *_vnode, void *cookie, fssh_off_t pos,
const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes)
{
struct vnode *vnode = (struct vnode *)_vnode;
return FS_CALL(vnode, read_pages,
cookie, pos, vecs, count, _numBytes);
}
fssh_status_t
vfs_write_pages(void *_vnode, void *cookie, fssh_off_t pos,
const fssh_iovec *vecs, fssh_size_t count, fssh_size_t *_numBytes)
{
struct vnode *vnode = (struct vnode *)_vnode;
return FS_CALL(vnode, write_pages,
cookie, pos, vecs, count, _numBytes);
}
fssh_status_t
vfs_entry_ref_to_vnode(fssh_mount_id mountID, fssh_vnode_id directoryID,
const char *name, void **_vnode)
{
return entry_ref_to_vnode(mountID, directoryID, name,
(struct vnode **)_vnode);
}
void
vfs_fs_vnode_to_node_ref(void *_vnode, fssh_mount_id *_mountID,
fssh_vnode_id *_vnodeID)
{
struct vnode *vnode = (struct vnode *)_vnode;
*_mountID = vnode->device;
*_vnodeID = vnode->id;
}
fssh_status_t
vfs_lookup_vnode(fssh_mount_id mountID, fssh_vnode_id vnodeID,
struct vnode **_vnode)
{
fssh_mutex_lock(&sVnodeMutex);
struct vnode *vnode = lookup_vnode(mountID, vnodeID);
fssh_mutex_unlock(&sVnodeMutex);
if (vnode == NULL)
return FSSH_B_ERROR;
*_vnode = vnode;
return FSSH_B_OK;
}
fssh_status_t
vfs_get_fs_node_from_path(fssh_fs_volume *volume, const char *path,
bool kernel, void **_node)
{
TRACE(("vfs_get_fs_node_from_path(volume = %p (%ld), path = \"%s\", "
"kernel %d)\n", volume, volume->id, path, kernel));
KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
if (pathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
fs_mount *mount;
fssh_status_t status = get_mount(volume->id, &mount);
if (status < FSSH_B_OK)
return status;
char *buffer = pathBuffer.LockBuffer();
fssh_strlcpy(buffer, path, pathBuffer.BufferSize());
struct vnode *vnode = mount->root_vnode;
if (buffer[0] == '/')
status = path_to_vnode(buffer, true, &vnode, NULL, true);
else {
inc_vnode_ref_count(vnode);
status = vnode_path_to_vnode(vnode, buffer, true, 0, &vnode, NULL);
}
put_mount(mount);
if (status < FSSH_B_OK)
return status;
if (vnode->device != volume->id) {
put_vnode(vnode);
return FSSH_B_BAD_VALUE;
}
status = ::fssh_get_vnode(volume, vnode->id, _node);
put_vnode(vnode);
return FSSH_B_OK;
}
fssh_status_t
vfs_get_module_path(const char *basePath, const char *moduleName, char *pathBuffer,
fssh_size_t bufferSize)
{
struct vnode *dir, *file;
fssh_status_t status;
fssh_size_t length;
char *path;
if (bufferSize == 0 || fssh_strlcpy(pathBuffer, basePath, bufferSize) >= bufferSize)
return FSSH_B_BUFFER_OVERFLOW;
status = path_to_vnode(pathBuffer, true, &dir, NULL, true);
if (status < FSSH_B_OK)
return status;
length = fssh_strlcpy(pathBuffer, basePath, bufferSize);
if (pathBuffer[length - 1] != '/')
pathBuffer[length++] = '/';
path = pathBuffer + length;
bufferSize -= length;
while (moduleName) {
char *nextPath = fssh_strchr(moduleName, '/');
if (nextPath == NULL)
length = fssh_strlen(moduleName);
else {
length = nextPath - moduleName;
nextPath++;
}
if (length + 1 >= bufferSize) {
status = FSSH_B_BUFFER_OVERFLOW;
goto err;
}
fssh_memcpy(path, moduleName, length);
path[length] = '\0';
moduleName = nextPath;
status = vnode_path_to_vnode(dir, path, true, 0, &file, NULL);
if (status < FSSH_B_OK) {
return status;
}
if (FSSH_S_ISDIR(file->type)) {
path[length] = '/';
path[length + 1] = '\0';
path += length + 1;
bufferSize -= length + 1;
dir = file;
} else if (FSSH_S_ISREG(file->type)) {
put_vnode(file);
return FSSH_B_OK;
} else {
TRACE(("vfs_get_module_path(): something is strange here: %d...\n", file->type));
status = FSSH_B_ERROR;
dir = file;
goto err;
}
}
status = FSSH_B_ENTRY_NOT_FOUND;
err:
put_vnode(dir);
return status;
}
fssh_status_t
vfs_normalize_path(const char *path, char *buffer, fssh_size_t bufferSize,
bool kernel)
{
if (!path || !buffer || bufferSize < 1)
return FSSH_B_BAD_VALUE;
TRACE(("vfs_normalize_path(`%s')\n", path));
KPath mutablePathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
if (mutablePathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
char *mutablePath = mutablePathBuffer.LockBuffer();
if (fssh_strlcpy(mutablePath, path, FSSH_B_PATH_NAME_LENGTH) >= FSSH_B_PATH_NAME_LENGTH)
return FSSH_B_NAME_TOO_LONG;
struct vnode *dirNode;
char leaf[FSSH_B_FILE_NAME_LENGTH];
fssh_status_t error = path_to_dir_vnode(mutablePath, &dirNode, leaf, kernel);
if (error != FSSH_B_OK) {
TRACE(("vfs_normalize_path(): failed to get dir vnode: %s\n", strerror(error)));
return error;
}
bool isDir = (fssh_strcmp(leaf, ".") == 0 || fssh_strcmp(leaf, "..") == 0);
if (isDir)
error = vnode_path_to_vnode(dirNode, leaf, false, 0, &dirNode, NULL);
if (error != FSSH_B_OK) {
TRACE(("vfs_normalize_path(): failed to get dir vnode for \".\" or \"..\": %s\n",
strerror(error)));
return error;
}
error = dir_vnode_to_path(dirNode, buffer, bufferSize);
put_vnode(dirNode);
if (error < FSSH_B_OK) {
TRACE(("vfs_normalize_path(): failed to get dir path: %s\n", strerror(error)));
return error;
}
if (!isDir) {
if ((fssh_strcmp(buffer, "/") != 0
&& fssh_strlcat(buffer, "/", bufferSize) >= bufferSize)
|| fssh_strlcat(buffer, leaf, bufferSize) >= bufferSize) {
return FSSH_B_NAME_TOO_LONG;
}
}
TRACE(("vfs_normalize_path() -> `%s'\n", buffer));
return FSSH_B_OK;
}
void
vfs_put_vnode(void *_vnode)
{
put_vnode((struct vnode *)_vnode);
}
fssh_status_t
vfs_get_cwd(fssh_mount_id *_mountID, fssh_vnode_id *_vnodeID)
{
struct io_context *context = get_current_io_context(false);
fssh_status_t status = FSSH_B_OK;
fssh_mutex_lock(&context->io_mutex);
if (context->cwd != NULL) {
*_mountID = context->cwd->device;
*_vnodeID = context->cwd->id;
} else
status = FSSH_B_ERROR;
fssh_mutex_unlock(&context->io_mutex);
return status;
}
fssh_status_t
vfs_get_file_map(void *_vnode, fssh_off_t offset, fssh_size_t size,
fssh_file_io_vec *vecs, fssh_size_t *_count)
{
struct vnode *vnode = (struct vnode *)_vnode;
FUNCTION(("vfs_get_file_map: vnode %p, vecs %p, offset %lld, size = %u\n", vnode, vecs, offset, (unsigned)size));
return FS_CALL(vnode, get_file_map, offset, size, vecs, _count);
}
fssh_status_t
vfs_stat_vnode(void *_vnode, struct fssh_stat *stat)
{
struct vnode *vnode = (struct vnode *)_vnode;
fssh_status_t status = FS_CALL(vnode, read_stat, stat);
if (status == FSSH_B_OK) {
stat->fssh_st_dev = vnode->device;
stat->fssh_st_ino = vnode->id;
}
return status;
}
fssh_status_t
vfs_get_vnode_name(void *_vnode, char *name, fssh_size_t nameSize)
{
return get_vnode_name((struct vnode *)_vnode, NULL, name, nameSize);
}
fssh_status_t
vfs_entry_ref_to_path(fssh_dev_t device, fssh_ino_t inode, const char *leaf,
bool kernel, char *path, fssh_size_t pathLength)
{
struct vnode *vnode;
fssh_status_t status;
if (leaf != NULL && (leaf[0] == '\0' || fssh_strchr(leaf, '/')))
return FSSH_B_BAD_VALUE;
if (leaf && (fssh_strcmp(leaf, ".") == 0 || fssh_strcmp(leaf, "..") == 0)) {
status = entry_ref_to_vnode(device, inode, leaf, &vnode);
leaf = NULL;
} else
status = get_vnode(device, inode, &vnode, false);
if (status < FSSH_B_OK)
return status;
status = dir_vnode_to_path(vnode, path, pathLength);
put_vnode(vnode);
if (status < FSSH_B_OK)
return status;
if (leaf) {
if ((fssh_strcmp(path, "/") && fssh_strlcat(path, "/", pathLength)
>= pathLength)
|| fssh_strlcat(path, leaf, pathLength) >= pathLength) {
return FSSH_B_NAME_TOO_LONG;
}
}
return FSSH_B_OK;
}
void
vfs_unlock_vnode_if_locked(struct file_descriptor *descriptor)
{
struct vnode *vnode = fd_vnode(descriptor);
if (vnode != NULL && vnode->mandatory_locked_by == descriptor)
vnode->mandatory_locked_by = NULL;
}
void
vfs_exec_io_context(void *_context)
{
struct io_context *context = (struct io_context *)_context;
uint32_t i;
for (i = 0; i < context->table_size; i++) {
fssh_mutex_lock(&context->io_mutex);
struct file_descriptor *descriptor = context->fds[i];
bool remove = false;
if (descriptor != NULL && fd_close_on_exec(context, i)) {
context->fds[i] = NULL;
context->num_used_fds--;
remove = true;
}
fssh_mutex_unlock(&context->io_mutex);
if (remove) {
close_fd(descriptor);
put_fd(descriptor);
}
}
}
void *
vfs_new_io_context(void *_parentContext)
{
fssh_size_t tableSize;
struct io_context *context;
struct io_context *parentContext;
context = (io_context *)malloc(sizeof(struct io_context));
if (context == NULL)
return NULL;
fssh_memset(context, 0, sizeof(struct io_context));
parentContext = (struct io_context *)_parentContext;
if (parentContext)
tableSize = parentContext->table_size;
else
tableSize = DEFAULT_FD_TABLE_SIZE;
context->fds = (file_descriptor **)malloc(sizeof(struct file_descriptor *) * tableSize
+ (tableSize + 7) / 8);
if (context->fds == NULL) {
free(context);
return NULL;
}
fssh_memset(context->fds, 0, sizeof(struct file_descriptor *) * tableSize
+ (tableSize + 7) / 8);
context->fds_close_on_exec = (uint8_t *)(context->fds + tableSize);
fssh_mutex_init(&context->io_mutex, "I/O context");
if (parentContext) {
fssh_size_t i;
fssh_mutex_lock(&parentContext->io_mutex);
context->cwd = parentContext->cwd;
if (context->cwd)
inc_vnode_ref_count(context->cwd);
for (i = 0; i < tableSize; i++) {
struct file_descriptor *descriptor = parentContext->fds[i];
if (descriptor != NULL && !fd_close_on_exec(parentContext, i)) {
context->fds[i] = descriptor;
context->num_used_fds++;
fssh_atomic_add(&descriptor->ref_count, 1);
fssh_atomic_add(&descriptor->open_count, 1);
}
}
fssh_mutex_unlock(&parentContext->io_mutex);
} else {
context->cwd = sRoot;
if (context->cwd)
inc_vnode_ref_count(context->cwd);
}
context->table_size = tableSize;
return context;
}
fssh_status_t
vfs_free_io_context(void *_ioContext)
{
struct io_context *context = (struct io_context *)_ioContext;
uint32_t i;
if (context->cwd)
dec_vnode_ref_count(context->cwd, false);
fssh_mutex_lock(&context->io_mutex);
for (i = 0; i < context->table_size; i++) {
if (struct file_descriptor *descriptor = context->fds[i]) {
close_fd(descriptor);
put_fd(descriptor);
}
}
fssh_mutex_destroy(&context->io_mutex);
free(context->fds);
free(context);
return FSSH_B_OK;
}
fssh_status_t
vfs_init(kernel_args *args)
{
sVnodeTable = hash_init(VNODE_HASH_TABLE_SIZE, fssh_offsetof(struct vnode, next),
&vnode_compare, &vnode_hash);
if (sVnodeTable == NULL)
fssh_panic("vfs_init: error creating vnode hash table\n");
list_init_etc(&sUnusedVnodeList, fssh_offsetof(struct vnode, unused_link));
sMountsTable = hash_init(MOUNTS_HASH_TABLE_SIZE, fssh_offsetof(struct fs_mount, next),
&mount_compare, &mount_hash);
if (sMountsTable == NULL)
fssh_panic("vfs_init: error creating mounts hash table\n");
sRoot = NULL;
fssh_mutex_init(&sFileSystemsMutex, "vfs_lock");
fssh_recursive_lock_init(&sMountOpLock, "vfs_mount_op_lock");
fssh_mutex_init(&sMountMutex, "vfs_mount_lock");
fssh_mutex_init(&sVnodeCoveredByMutex, "vfs_vnode_covered_by_lock");
fssh_mutex_init(&sVnodeMutex, "vfs_vnode_lock");
if (block_cache_init() != FSSH_B_OK)
return FSSH_B_ERROR;
return file_cache_init();
}
static int
create_vnode(struct vnode *directory, const char *name, int openMode, int perms, bool kernel)
{
struct vnode *vnode;
void *cookie;
fssh_vnode_id newID;
int status;
if (!HAS_FS_CALL(directory, create))
return FSSH_EROFS;
status = FS_CALL(directory, create, name, openMode, perms, &cookie, &newID);
if (status < FSSH_B_OK)
return status;
fssh_mutex_lock(&sVnodeMutex);
vnode = lookup_vnode(directory->device, newID);
fssh_mutex_unlock(&sVnodeMutex);
if (vnode == NULL) {
fssh_dprintf("vfs: fs_create() returned success but there is no vnode!");
return FSSH_EINVAL;
}
if ((status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel)) >= 0)
return status;
FS_CALL(vnode, close, cookie);
FS_CALL(vnode, free_cookie, cookie);
put_vnode(vnode);
FS_CALL(directory, unlink, name);
return status;
}
static int
open_vnode(struct vnode *vnode, int openMode, bool kernel)
{
void *cookie;
int status;
status = FS_CALL(vnode, open, openMode, &cookie);
if (status < 0)
return status;
status = get_new_fd(FDTYPE_FILE, NULL, vnode, cookie, openMode, kernel);
if (status < 0) {
FS_CALL(vnode, close, cookie);
FS_CALL(vnode, free_cookie, cookie);
}
return status;
}
static int
open_dir_vnode(struct vnode *vnode, bool kernel)
{
void *cookie;
int status;
status = FS_CALL(vnode, open_dir, &cookie);
if (status < FSSH_B_OK)
return status;
status = get_new_fd(FDTYPE_DIR, NULL, vnode, cookie, 0, kernel);
if (status >= 0)
return status;
FS_CALL(vnode, close_dir, cookie);
FS_CALL(vnode, free_dir_cookie, cookie);
return status;
}
static int
open_attr_dir_vnode(struct vnode *vnode, bool kernel)
{
void *cookie;
int status;
if (!HAS_FS_CALL(vnode, open_attr_dir))
return FSSH_EOPNOTSUPP;
status = FS_CALL(vnode, open_attr_dir, &cookie);
if (status < 0)
return status;
status = get_new_fd(FDTYPE_ATTR_DIR, NULL, vnode, cookie, 0, kernel);
if (status >= 0)
return status;
FS_CALL(vnode, close_attr_dir, cookie);
FS_CALL(vnode, free_attr_dir_cookie, cookie);
return status;
}
static int
file_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, int perms, bool kernel)
{
struct vnode *directory;
int status;
FUNCTION(("file_create_entry_ref: name = '%s', omode %x, perms %d, kernel %d\n", name, openMode, perms, kernel));
status = get_vnode(mountID, directoryID, &directory, false);
if (status < FSSH_B_OK)
return status;
status = create_vnode(directory, name, openMode, perms, kernel);
put_vnode(directory);
return status;
}
static int
file_create(int fd, char *path, int openMode, int perms, bool kernel)
{
char name[FSSH_B_FILE_NAME_LENGTH];
struct vnode *directory;
int status;
FUNCTION(("file_create: path '%s', omode %x, perms %d, kernel %d\n", path, openMode, perms, kernel));
status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel);
if (status < 0)
return status;
status = create_vnode(directory, name, openMode, perms, kernel);
put_vnode(directory);
return status;
}
static int
file_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id directoryID, const char *name, int openMode, bool kernel)
{
struct vnode *vnode;
int status;
if (name == NULL || *name == '\0')
return FSSH_B_BAD_VALUE;
FUNCTION(("file_open_entry_ref(ref = (%ld, %lld, %s), openMode = %d)\n",
mountID, directoryID, name, openMode));
status = entry_ref_to_vnode(mountID, directoryID, name, &vnode);
if (status < FSSH_B_OK)
return status;
status = open_vnode(vnode, openMode, kernel);
if (status < FSSH_B_OK)
put_vnode(vnode);
return status;
}
static int
file_open(int fd, char *path, int openMode, bool kernel)
{
int status = FSSH_B_OK;
bool traverse = ((openMode & FSSH_O_NOTRAVERSE) == 0);
FUNCTION(("file_open: fd: %d, entry path = '%s', omode %d, kernel %d\n",
fd, path, openMode, kernel));
struct vnode *vnode = NULL;
fssh_vnode_id parentID;
status = fd_and_path_to_vnode(fd, path, traverse, &vnode, &parentID, kernel);
if (status != FSSH_B_OK)
return status;
status = open_vnode(vnode, openMode, kernel);
if (status < FSSH_B_OK)
put_vnode(vnode);
return status;
}
static fssh_status_t
file_close(struct file_descriptor *descriptor)
{
struct vnode *vnode = descriptor->u.vnode;
fssh_status_t status = FSSH_B_OK;
FUNCTION(("file_close(descriptor = %p)\n", descriptor));
if (HAS_FS_CALL(vnode, close))
status = FS_CALL(vnode, close, descriptor->cookie);
return status;
}
static void
file_free_fd(struct file_descriptor *descriptor)
{
struct vnode *vnode = descriptor->u.vnode;
if (vnode != NULL) {
FS_CALL(vnode, free_cookie, descriptor->cookie);
put_vnode(vnode);
}
}
static fssh_status_t
file_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length)
{
struct vnode *vnode = descriptor->u.vnode;
FUNCTION(("file_read: buf %p, pos %lld, len %p = %ld\n", buffer, pos, length, *length));
return FS_CALL(vnode, read, descriptor->cookie, pos, buffer, length);
}
static fssh_status_t
file_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length)
{
struct vnode *vnode = descriptor->u.vnode;
FUNCTION(("file_write: buf %p, pos %lld, len %p\n", buffer, pos, length));
return FS_CALL(vnode, write, descriptor->cookie, pos, buffer, length);
}
static fssh_off_t
file_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType)
{
fssh_off_t offset;
FUNCTION(("file_seek(pos = %lld, seekType = %d)\n", pos, seekType));
switch (seekType) {
case FSSH_SEEK_SET:
offset = 0;
break;
case FSSH_SEEK_CUR:
offset = descriptor->pos;
break;
case FSSH_SEEK_END:
{
struct vnode *vnode = descriptor->u.vnode;
struct fssh_stat stat;
fssh_status_t status;
if (!HAS_FS_CALL(vnode, read_stat))
return FSSH_EOPNOTSUPP;
status = FS_CALL(vnode, read_stat, &stat);
if (status < FSSH_B_OK)
return status;
offset = stat.fssh_st_size;
break;
}
default:
return FSSH_B_BAD_VALUE;
}
if (offset > 0 && LLONG_MAX - offset < pos)
return FSSH_EOVERFLOW;
pos += offset;
if (pos < 0)
return FSSH_B_BAD_VALUE;
return descriptor->pos = pos;
}
static fssh_status_t
dir_create_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, int perms, bool kernel)
{
struct vnode *vnode;
fssh_status_t status;
if (name == NULL || *name == '\0')
return FSSH_B_BAD_VALUE;
FUNCTION(("dir_create_entry_ref(dev = %ld, ino = %lld, name = '%s', perms = %d)\n", mountID, parentID, name, perms));
status = get_vnode(mountID, parentID, &vnode, kernel);
if (status < FSSH_B_OK)
return status;
if (HAS_FS_CALL(vnode, create_dir))
status = FS_CALL(vnode, create_dir, name, perms);
else
status = FSSH_EROFS;
put_vnode(vnode);
return status;
}
static fssh_status_t
dir_create(int fd, char *path, int perms, bool kernel)
{
char filename[FSSH_B_FILE_NAME_LENGTH];
struct vnode *vnode;
fssh_status_t status;
FUNCTION(("dir_create: path '%s', perms %d, kernel %d\n", path, perms, kernel));
status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel);
if (status < 0)
return status;
if (HAS_FS_CALL(vnode, create_dir))
status = FS_CALL(vnode, create_dir, filename, perms);
else
status = FSSH_EROFS;
put_vnode(vnode);
return status;
}
static int
dir_open_entry_ref(fssh_mount_id mountID, fssh_vnode_id parentID, const char *name, bool kernel)
{
struct vnode *vnode;
int status;
FUNCTION(("dir_open_entry_ref()\n"));
if (name && *name == '\0')
return FSSH_B_BAD_VALUE;
if (name)
status = entry_ref_to_vnode(mountID, parentID, name, &vnode);
else
status = get_vnode(mountID, parentID, &vnode, false);
if (status < FSSH_B_OK)
return status;
status = open_dir_vnode(vnode, kernel);
if (status < FSSH_B_OK)
put_vnode(vnode);
return status;
}
static int
dir_open(int fd, char *path, bool kernel)
{
int status = FSSH_B_OK;
FUNCTION(("dir_open: fd: %d, entry path = '%s', kernel %d\n", fd, path, kernel));
struct vnode *vnode = NULL;
fssh_vnode_id parentID;
status = fd_and_path_to_vnode(fd, path, true, &vnode, &parentID, kernel);
if (status != FSSH_B_OK)
return status;
status = open_dir_vnode(vnode, kernel);
if (status < FSSH_B_OK)
put_vnode(vnode);
return status;
}
static fssh_status_t
dir_close(struct file_descriptor *descriptor)
{
struct vnode *vnode = descriptor->u.vnode;
FUNCTION(("dir_close(descriptor = %p)\n", descriptor));
if (HAS_FS_CALL(vnode, close_dir))
return FS_CALL(vnode, close_dir, descriptor->cookie);
return FSSH_B_OK;
}
static void
dir_free_fd(struct file_descriptor *descriptor)
{
struct vnode *vnode = descriptor->u.vnode;
if (vnode != NULL) {
FS_CALL(vnode, free_dir_cookie, descriptor->cookie);
put_vnode(vnode);
}
}
static fssh_status_t
dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
fssh_size_t bufferSize, uint32_t *_count)
{
return dir_read(descriptor->u.vnode, descriptor->cookie, buffer, bufferSize, _count);
}
static void
fix_dirent(struct vnode *parent, struct fssh_dirent *entry)
{
entry->d_pdev = parent->device;
entry->d_pino = parent->id;
if (fssh_strcmp(entry->d_name, "..") == 0
&& parent->mount->root_vnode == parent
&& parent->mount->covers_vnode) {
inc_vnode_ref_count(parent);
struct vnode *vnode;
fssh_status_t status = vnode_path_to_vnode(parent, (char*)"..", false,
0, &vnode, NULL);
if (status == FSSH_B_OK) {
entry->d_dev = vnode->device;
entry->d_ino = vnode->id;
}
} else {
struct vnode *vnode = NULL;
fssh_status_t status = get_vnode(entry->d_dev, entry->d_ino, &vnode, false);
if (status != FSSH_B_OK)
return;
fssh_mutex_lock(&sVnodeCoveredByMutex);
if (vnode->covered_by) {
entry->d_dev = vnode->covered_by->device;
entry->d_ino = vnode->covered_by->id;
}
fssh_mutex_unlock(&sVnodeCoveredByMutex);
put_vnode(vnode);
}
}
static fssh_status_t
dir_read(struct vnode *vnode, void *cookie, struct fssh_dirent *buffer,
fssh_size_t bufferSize, uint32_t *_count)
{
if (!HAS_FS_CALL(vnode, read_dir))
return FSSH_EOPNOTSUPP;
fssh_status_t error = FS_CALL(vnode, read_dir,cookie,buffer,bufferSize,_count);
if (error != FSSH_B_OK)
return error;
if (*_count > 0) {
fix_dirent(vnode, buffer);
}
return error;
}
static fssh_status_t
dir_rewind(struct file_descriptor *descriptor)
{
struct vnode *vnode = descriptor->u.vnode;
if (HAS_FS_CALL(vnode, rewind_dir))
return FS_CALL(vnode, rewind_dir,descriptor->cookie);
return FSSH_EOPNOTSUPP;
}
static fssh_status_t
dir_remove(int fd, char *path, bool kernel)
{
char name[FSSH_B_FILE_NAME_LENGTH];
struct vnode *directory;
fssh_status_t status;
if (path != NULL) {
char *lastSlash = fssh_strrchr(path, '/');
if (lastSlash != NULL) {
char *leaf = lastSlash + 1;
if (!fssh_strcmp(leaf, ".."))
return FSSH_B_NOT_ALLOWED;
while (lastSlash > path && lastSlash[-1] == '/') {
lastSlash--;
}
if (!leaf[0]
|| !fssh_strcmp(leaf, ".")) {
lastSlash[0] = '\0';
}
} else if (!fssh_strcmp(path, ".."))
return FSSH_B_NOT_ALLOWED;
}
status = fd_and_path_to_dir_vnode(fd, path, &directory, name, kernel);
if (status < FSSH_B_OK)
return status;
if (HAS_FS_CALL(directory, remove_dir)) {
status = FS_CALL(directory, remove_dir, name);
} else
status = FSSH_EROFS;
put_vnode(directory);
return status;
}
static fssh_status_t
common_ioctl(struct file_descriptor *descriptor, uint32_t op, void *buffer,
fssh_size_t length)
{
struct vnode *vnode = descriptor->u.vnode;
if (HAS_FS_CALL(vnode, ioctl)) {
return FS_CALL(vnode, ioctl,
descriptor->cookie, op, buffer, length);
}
return FSSH_EOPNOTSUPP;
}
static fssh_status_t
common_fcntl(int fd, int op, uint32_t argument, bool kernel)
{
struct file_descriptor *descriptor;
struct vnode *vnode;
fssh_status_t status;
FUNCTION(("common_fcntl(fd = %d, op = %d, argument = %lx, %s)\n",
fd, op, argument, kernel ? "kernel" : "user"));
descriptor = get_fd_and_vnode(fd, &vnode, kernel);
if (descriptor == NULL)
return FSSH_B_FILE_ERROR;
switch (op) {
case FSSH_F_SETFD:
{
struct io_context *context = get_current_io_context(kernel);
fssh_mutex_lock(&context->io_mutex);
fd_set_close_on_exec(context, fd, argument == FSSH_FD_CLOEXEC);
fssh_mutex_unlock(&context->io_mutex);
status = FSSH_B_OK;
break;
}
case FSSH_F_GETFD:
{
struct io_context *context = get_current_io_context(kernel);
fssh_mutex_lock(&context->io_mutex);
status = fd_close_on_exec(context, fd) ? FSSH_FD_CLOEXEC : 0;
fssh_mutex_unlock(&context->io_mutex);
break;
}
case FSSH_F_SETFL:
if (HAS_FS_CALL(vnode, set_flags)) {
argument &= FSSH_O_APPEND | FSSH_O_NONBLOCK;
status = FS_CALL(vnode, set_flags, descriptor->cookie, (int)argument);
if (status == FSSH_B_OK) {
descriptor->open_mode = (descriptor->open_mode & ~(FSSH_O_APPEND | FSSH_O_NONBLOCK))
| argument;
}
} else
status = FSSH_EOPNOTSUPP;
break;
case FSSH_F_GETFL:
status = descriptor->open_mode;
break;
case FSSH_F_DUPFD:
{
struct io_context *context = get_current_io_context(kernel);
status = new_fd_etc(context, descriptor, (int)argument);
if (status >= 0) {
fssh_mutex_lock(&context->io_mutex);
fd_set_close_on_exec(context, fd, false);
fssh_mutex_unlock(&context->io_mutex);
fssh_atomic_add(&descriptor->ref_count, 1);
}
break;
}
case FSSH_F_GETLK:
case FSSH_F_SETLK:
case FSSH_F_SETLKW:
status = FSSH_B_BAD_VALUE;
break;
default:
status = FSSH_B_BAD_VALUE;
}
put_fd(descriptor);
return status;
}
static fssh_status_t
common_sync(int fd, bool dataOnly, bool kernel)
{
struct file_descriptor *descriptor;
struct vnode *vnode;
fssh_status_t status;
FUNCTION(("common_fsync: entry. fd %d kernel %d\n", fd, kernel));
descriptor = get_fd_and_vnode(fd, &vnode, kernel);
if (descriptor == NULL)
return FSSH_B_FILE_ERROR;
if (HAS_FS_CALL(vnode, fsync))
status = FS_CALL(vnode, fsync, dataOnly);
else
status = FSSH_EOPNOTSUPP;
put_fd(descriptor);
return status;
}
static fssh_status_t
common_lock_node(int fd, bool kernel)
{
struct file_descriptor *descriptor;
struct vnode *vnode;
descriptor = get_fd_and_vnode(fd, &vnode, kernel);
if (descriptor == NULL)
return FSSH_B_FILE_ERROR;
fssh_status_t status = FSSH_B_OK;
#if UINTPTR_MAX == UINT64_MAX
if (fssh_atomic_test_and_set64((int64_t *)&vnode->mandatory_locked_by,
(fssh_addr_t)descriptor, 0) != 0)
#else
if (fssh_atomic_test_and_set((int32_t *)&vnode->mandatory_locked_by,
(fssh_addr_t)descriptor, 0) != 0)
#endif
status = FSSH_B_BUSY;
put_fd(descriptor);
return status;
}
static fssh_status_t
common_unlock_node(int fd, bool kernel)
{
struct file_descriptor *descriptor;
struct vnode *vnode;
descriptor = get_fd_and_vnode(fd, &vnode, kernel);
if (descriptor == NULL)
return FSSH_B_FILE_ERROR;
fssh_status_t status = FSSH_B_OK;
#if UINTPTR_MAX == UINT64_MAX
if (fssh_atomic_test_and_set64((int64_t *)&vnode->mandatory_locked_by,
0, (fssh_addr_t)descriptor) != (int64_t)descriptor)
#else
if (fssh_atomic_test_and_set((int32_t *)&vnode->mandatory_locked_by,
0, (fssh_addr_t)descriptor) != (int32_t)descriptor)
#endif
status = FSSH_B_BAD_VALUE;
put_fd(descriptor);
return status;
}
static fssh_status_t
common_read_link(int fd, char *path, char *buffer, fssh_size_t *_bufferSize,
bool kernel)
{
struct vnode *vnode;
fssh_status_t status;
status = fd_and_path_to_vnode(fd, path, false, &vnode, NULL, kernel);
if (status < FSSH_B_OK)
return status;
if (HAS_FS_CALL(vnode, read_symlink)) {
status = FS_CALL(vnode, read_symlink, buffer, _bufferSize);
} else
status = FSSH_B_BAD_VALUE;
put_vnode(vnode);
return status;
}
static fssh_status_t
common_create_symlink(int fd, char *path, const char *toPath, int mode,
bool kernel)
{
char name[FSSH_B_FILE_NAME_LENGTH];
struct vnode *vnode;
fssh_status_t status;
FUNCTION(("common_create_symlink(fd = %d, path = %s, toPath = %s, mode = %d, kernel = %d)\n", fd, path, toPath, mode, kernel));
status = fd_and_path_to_dir_vnode(fd, path, &vnode, name, kernel);
if (status < FSSH_B_OK)
return status;
if (HAS_FS_CALL(vnode, create_symlink))
status = FS_CALL(vnode, create_symlink, name, toPath, mode);
else
status = FSSH_EROFS;
put_vnode(vnode);
return status;
}
static fssh_status_t
common_create_link(char *path, char *toPath, bool kernel)
{
char name[FSSH_B_FILE_NAME_LENGTH];
struct vnode *directory, *vnode;
fssh_status_t status;
FUNCTION(("common_create_link(path = %s, toPath = %s, kernel = %d)\n", path, toPath, kernel));
status = path_to_dir_vnode(path, &directory, name, kernel);
if (status < FSSH_B_OK)
return status;
status = path_to_vnode(toPath, true, &vnode, NULL, kernel);
if (status < FSSH_B_OK)
goto err;
if (directory->mount != vnode->mount) {
status = FSSH_B_CROSS_DEVICE_LINK;
goto err1;
}
if (HAS_FS_CALL(directory, link))
status = FS_CALL(directory, link, name, vnode);
else
status = FSSH_EROFS;
err1:
put_vnode(vnode);
err:
put_vnode(directory);
return status;
}
static fssh_status_t
common_unlink(int fd, char *path, bool kernel)
{
char filename[FSSH_B_FILE_NAME_LENGTH];
struct vnode *vnode;
fssh_status_t status;
FUNCTION(("common_unlink: fd: %d, path '%s', kernel %d\n", fd, path, kernel));
status = fd_and_path_to_dir_vnode(fd, path, &vnode, filename, kernel);
if (status < 0)
return status;
if (HAS_FS_CALL(vnode, unlink))
status = FS_CALL(vnode, unlink, filename);
else
status = FSSH_EROFS;
put_vnode(vnode);
return status;
}
static fssh_status_t
common_access(char *path, int mode, bool kernel)
{
struct vnode *vnode;
fssh_status_t status;
status = path_to_vnode(path, true, &vnode, NULL, kernel);
if (status < FSSH_B_OK)
return status;
if (HAS_FS_CALL(vnode, access))
status = FS_CALL(vnode, access, mode);
else
status = FSSH_B_OK;
put_vnode(vnode);
return status;
}
static fssh_status_t
common_rename(int fd, char *path, int newFD, char *newPath, bool kernel)
{
struct vnode *fromVnode, *toVnode;
char fromName[FSSH_B_FILE_NAME_LENGTH];
char toName[FSSH_B_FILE_NAME_LENGTH];
fssh_status_t status;
FUNCTION(("common_rename(fd = %d, path = %s, newFD = %d, newPath = %s, kernel = %d)\n", fd, path, newFD, newPath, kernel));
status = fd_and_path_to_dir_vnode(fd, path, &fromVnode, fromName, kernel);
if (status < 0)
return status;
status = fd_and_path_to_dir_vnode(newFD, newPath, &toVnode, toName, kernel);
if (status < 0)
goto err;
if (fromVnode->device != toVnode->device) {
status = FSSH_B_CROSS_DEVICE_LINK;
goto err1;
}
if (HAS_FS_CALL(fromVnode, rename))
status = FS_CALL(fromVnode, rename, fromName, toVnode, toName);
else
status = FSSH_EROFS;
err1:
put_vnode(toVnode);
err:
put_vnode(fromVnode);
return status;
}
static fssh_status_t
common_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat)
{
struct vnode *vnode = descriptor->u.vnode;
FUNCTION(("common_read_stat: stat %p\n", stat));
stat->fssh_st_atim.tv_nsec = 0;
stat->fssh_st_mtim.tv_nsec = 0;
stat->fssh_st_ctim.tv_nsec = 0;
stat->fssh_st_crtim.tv_nsec = 0;
fssh_status_t status = FS_CALL(vnode, read_stat, stat);
if (status == FSSH_B_OK) {
stat->fssh_st_dev = vnode->device;
stat->fssh_st_ino = vnode->id;
}
return status;
}
static fssh_status_t
common_write_stat(struct file_descriptor *descriptor,
const struct fssh_stat *stat, int statMask)
{
struct vnode *vnode = descriptor->u.vnode;
FUNCTION(("common_write_stat(vnode = %p, stat = %p, statMask = %d)\n", vnode, stat, statMask));
if (!HAS_FS_CALL(vnode, write_stat))
return FSSH_EROFS;
return FS_CALL(vnode, write_stat, stat, statMask);
}
static fssh_status_t
common_path_read_stat(int fd, char *path, bool traverseLeafLink,
struct fssh_stat *stat, bool kernel)
{
struct vnode *vnode;
fssh_status_t status;
FUNCTION(("common_path_read_stat: fd: %d, path '%s', stat %p,\n", fd, path, stat));
status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel);
if (status < 0)
return status;
status = FS_CALL(vnode, read_stat, stat);
if (status == FSSH_B_OK) {
stat->fssh_st_dev = vnode->device;
stat->fssh_st_ino = vnode->id;
}
put_vnode(vnode);
return status;
}
static fssh_status_t
common_path_write_stat(int fd, char *path, bool traverseLeafLink,
const struct fssh_stat *stat, int statMask, bool kernel)
{
struct vnode *vnode;
fssh_status_t status;
FUNCTION(("common_write_stat: fd: %d, path '%s', stat %p, stat_mask %d, kernel %d\n", fd, path, stat, statMask, kernel));
status = fd_and_path_to_vnode(fd, path, traverseLeafLink, &vnode, NULL, kernel);
if (status < 0)
return status;
if (HAS_FS_CALL(vnode, write_stat))
status = FS_CALL(vnode, write_stat, stat, statMask);
else
status = FSSH_EROFS;
put_vnode(vnode);
return status;
}
static int
attr_dir_open(int fd, char *path, bool kernel)
{
struct vnode *vnode;
int status;
FUNCTION(("attr_dir_open(fd = %d, path = '%s', kernel = %d)\n", fd, path, kernel));
status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel);
if (status < FSSH_B_OK)
return status;
status = open_attr_dir_vnode(vnode, kernel);
if (status < 0)
put_vnode(vnode);
return status;
}
static fssh_status_t
attr_dir_close(struct file_descriptor *descriptor)
{
struct vnode *vnode = descriptor->u.vnode;
FUNCTION(("attr_dir_close(descriptor = %p)\n", descriptor));
if (HAS_FS_CALL(vnode, close_attr_dir))
return FS_CALL(vnode, close_attr_dir, descriptor->cookie);
return FSSH_B_OK;
}
static void
attr_dir_free_fd(struct file_descriptor *descriptor)
{
struct vnode *vnode = descriptor->u.vnode;
if (vnode != NULL) {
FS_CALL(vnode, free_attr_dir_cookie, descriptor->cookie);
put_vnode(vnode);
}
}
static fssh_status_t
attr_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
fssh_size_t bufferSize, uint32_t *_count)
{
struct vnode *vnode = descriptor->u.vnode;
FUNCTION(("attr_dir_read(descriptor = %p)\n", descriptor));
if (HAS_FS_CALL(vnode, read_attr_dir))
return FS_CALL(vnode, read_attr_dir, descriptor->cookie, buffer, bufferSize, _count);
return FSSH_EOPNOTSUPP;
}
static fssh_status_t
attr_dir_rewind(struct file_descriptor *descriptor)
{
struct vnode *vnode = descriptor->u.vnode;
FUNCTION(("attr_dir_rewind(descriptor = %p)\n", descriptor));
if (HAS_FS_CALL(vnode, rewind_attr_dir))
return FS_CALL(vnode, rewind_attr_dir, descriptor->cookie);
return FSSH_EOPNOTSUPP;
}
static int
attr_create(int fd, const char *name, uint32_t type, int openMode, bool kernel)
{
struct vnode *vnode;
void *cookie;
int status;
if (name == NULL || *name == '\0')
return FSSH_B_BAD_VALUE;
vnode = get_vnode_from_fd(fd, kernel);
if (vnode == NULL)
return FSSH_B_FILE_ERROR;
if (!HAS_FS_CALL(vnode, create_attr)) {
status = FSSH_EROFS;
goto err;
}
status = FS_CALL(vnode, create_attr, name, type, openMode, &cookie);
if (status < FSSH_B_OK)
goto err;
if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0)
return status;
FS_CALL(vnode, close_attr, cookie);
FS_CALL(vnode, free_attr_cookie, cookie);
FS_CALL(vnode, remove_attr, name);
err:
put_vnode(vnode);
return status;
}
static int
attr_open(int fd, const char *name, int openMode, bool kernel)
{
struct vnode *vnode;
void *cookie;
int status;
if (name == NULL || *name == '\0')
return FSSH_B_BAD_VALUE;
vnode = get_vnode_from_fd(fd, kernel);
if (vnode == NULL)
return FSSH_B_FILE_ERROR;
if (!HAS_FS_CALL(vnode, open_attr)) {
status = FSSH_EOPNOTSUPP;
goto err;
}
status = FS_CALL(vnode, open_attr, name, openMode, &cookie);
if (status < FSSH_B_OK)
goto err;
if ((status = get_new_fd(FDTYPE_ATTR, NULL, vnode, cookie, openMode, kernel)) >= 0)
return status;
FS_CALL(vnode, close_attr, cookie);
FS_CALL(vnode, free_attr_cookie, cookie);
err:
put_vnode(vnode);
return status;
}
static fssh_status_t
attr_close(struct file_descriptor *descriptor)
{
struct vnode *vnode = descriptor->u.vnode;
FUNCTION(("attr_close(descriptor = %p)\n", descriptor));
if (HAS_FS_CALL(vnode, close_attr))
return FS_CALL(vnode, close_attr, descriptor->cookie);
return FSSH_B_OK;
}
static void
attr_free_fd(struct file_descriptor *descriptor)
{
struct vnode *vnode = descriptor->u.vnode;
if (vnode != NULL) {
FS_CALL(vnode, free_attr_cookie, descriptor->cookie);
put_vnode(vnode);
}
}
static fssh_status_t
attr_read(struct file_descriptor *descriptor, fssh_off_t pos, void *buffer, fssh_size_t *length)
{
struct vnode *vnode = descriptor->u.vnode;
FUNCTION(("attr_read: buf %p, pos %lld, len %p = %ld\n", buffer, pos, length, *length));
if (!HAS_FS_CALL(vnode, read_attr))
return FSSH_EOPNOTSUPP;
return FS_CALL(vnode, read_attr, descriptor->cookie, pos, buffer, length);
}
static fssh_status_t
attr_write(struct file_descriptor *descriptor, fssh_off_t pos, const void *buffer, fssh_size_t *length)
{
struct vnode *vnode = descriptor->u.vnode;
FUNCTION(("attr_write: buf %p, pos %lld, len %p\n", buffer, pos, length));
if (!HAS_FS_CALL(vnode, write_attr))
return FSSH_EOPNOTSUPP;
return FS_CALL(vnode, write_attr, descriptor->cookie, pos, buffer, length);
}
static fssh_off_t
attr_seek(struct file_descriptor *descriptor, fssh_off_t pos, int seekType)
{
fssh_off_t offset;
switch (seekType) {
case FSSH_SEEK_SET:
offset = 0;
break;
case FSSH_SEEK_CUR:
offset = descriptor->pos;
break;
case FSSH_SEEK_END:
{
struct vnode *vnode = descriptor->u.vnode;
struct fssh_stat stat;
fssh_status_t status;
if (!HAS_FS_CALL(vnode, read_stat))
return FSSH_EOPNOTSUPP;
status = FS_CALL(vnode, read_attr_stat, descriptor->cookie, &stat);
if (status < FSSH_B_OK)
return status;
offset = stat.fssh_st_size;
break;
}
default:
return FSSH_B_BAD_VALUE;
}
if (offset > 0 && LLONG_MAX - offset < pos)
return FSSH_EOVERFLOW;
pos += offset;
if (pos < 0)
return FSSH_B_BAD_VALUE;
return descriptor->pos = pos;
}
static fssh_status_t
attr_read_stat(struct file_descriptor *descriptor, struct fssh_stat *stat)
{
struct vnode *vnode = descriptor->u.vnode;
FUNCTION(("attr_read_stat: stat 0x%p\n", stat));
if (!HAS_FS_CALL(vnode, read_attr_stat))
return FSSH_EOPNOTSUPP;
return FS_CALL(vnode, read_attr_stat, descriptor->cookie, stat);
}
static fssh_status_t
attr_write_stat(struct file_descriptor *descriptor,
const struct fssh_stat *stat, int statMask)
{
struct vnode *vnode = descriptor->u.vnode;
FUNCTION(("attr_write_stat: stat = %p, statMask %d\n", stat, statMask));
if (!HAS_FS_CALL(vnode, write_attr_stat))
return FSSH_EROFS;
return FS_CALL(vnode, write_attr_stat, descriptor->cookie, stat, statMask);
}
static fssh_status_t
attr_remove(int fd, const char *name, bool kernel)
{
struct file_descriptor *descriptor;
struct vnode *vnode;
fssh_status_t status;
if (name == NULL || *name == '\0')
return FSSH_B_BAD_VALUE;
FUNCTION(("attr_remove: fd = %d, name = \"%s\", kernel %d\n", fd, name, kernel));
descriptor = get_fd_and_vnode(fd, &vnode, kernel);
if (descriptor == NULL)
return FSSH_B_FILE_ERROR;
if (HAS_FS_CALL(vnode, remove_attr))
status = FS_CALL(vnode, remove_attr, name);
else
status = FSSH_EROFS;
put_fd(descriptor);
return status;
}
static fssh_status_t
attr_rename(int fromfd, const char *fromName, int tofd, const char *toName, bool kernel)
{
struct file_descriptor *fromDescriptor, *toDescriptor;
struct vnode *fromVnode, *toVnode;
fssh_status_t status;
if (fromName == NULL || *fromName == '\0' || toName == NULL || *toName == '\0')
return FSSH_B_BAD_VALUE;
FUNCTION(("attr_rename: from fd = %d, from name = \"%s\", to fd = %d, to name = \"%s\", kernel %d\n", fromfd, fromName, tofd, toName, kernel));
fromDescriptor = get_fd_and_vnode(fromfd, &fromVnode, kernel);
if (fromDescriptor == NULL)
return FSSH_B_FILE_ERROR;
toDescriptor = get_fd_and_vnode(tofd, &toVnode, kernel);
if (toDescriptor == NULL) {
status = FSSH_B_FILE_ERROR;
goto err;
}
if (fromVnode->device != toVnode->device) {
status = FSSH_B_CROSS_DEVICE_LINK;
goto err1;
}
if (HAS_FS_CALL(fromVnode, rename_attr))
status = FS_CALL(fromVnode, rename_attr, fromName, toVnode, toName);
else
status = FSSH_EROFS;
err1:
put_fd(toDescriptor);
err:
put_fd(fromDescriptor);
return status;
}
static fssh_status_t
index_dir_open(fssh_mount_id mountID, bool kernel)
{
struct fs_mount *mount;
void *cookie;
FUNCTION(("index_dir_open(mountID = %ld, kernel = %d)\n", mountID, kernel));
fssh_status_t status = get_mount(mountID, &mount);
if (status < FSSH_B_OK)
return status;
if (!HAS_FS_MOUNT_CALL(mount, open_index_dir)) {
status = FSSH_EOPNOTSUPP;
goto out;
}
status = FS_MOUNT_CALL(mount, open_index_dir, &cookie);
if (status < FSSH_B_OK)
goto out;
status = get_new_fd(FDTYPE_INDEX_DIR, mount, NULL, cookie, 0, kernel);
if (status >= 0)
goto out;
FS_MOUNT_CALL(mount, close_index_dir, cookie);
FS_MOUNT_CALL(mount, free_index_dir_cookie, cookie);
out:
put_mount(mount);
return status;
}
static fssh_status_t
index_dir_close(struct file_descriptor *descriptor)
{
struct fs_mount *mount = descriptor->u.mount;
FUNCTION(("index_dir_close(descriptor = %p)\n", descriptor));
if (HAS_FS_MOUNT_CALL(mount, close_index_dir))
return FS_MOUNT_CALL(mount, close_index_dir, descriptor->cookie);
return FSSH_B_OK;
}
static void
index_dir_free_fd(struct file_descriptor *descriptor)
{
struct fs_mount *mount = descriptor->u.mount;
if (mount != NULL) {
FS_MOUNT_CALL(mount, free_index_dir_cookie, descriptor->cookie);
}
}
static fssh_status_t
index_dir_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
fssh_size_t bufferSize, uint32_t *_count)
{
struct fs_mount *mount = descriptor->u.mount;
if (HAS_FS_MOUNT_CALL(mount, read_index_dir))
return FS_MOUNT_CALL(mount, read_index_dir, descriptor->cookie, buffer, bufferSize, _count);
return FSSH_EOPNOTSUPP;
}
static fssh_status_t
index_dir_rewind(struct file_descriptor *descriptor)
{
struct fs_mount *mount = descriptor->u.mount;
if (HAS_FS_MOUNT_CALL(mount, rewind_index_dir))
return FS_MOUNT_CALL(mount, rewind_index_dir, descriptor->cookie);
return FSSH_EOPNOTSUPP;
}
static fssh_status_t
index_create(fssh_mount_id mountID, const char *name, uint32_t type, uint32_t flags, bool kernel)
{
FUNCTION(("index_create(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel));
struct fs_mount *mount;
fssh_status_t status = get_mount(mountID, &mount);
if (status < FSSH_B_OK)
return status;
if (!HAS_FS_MOUNT_CALL(mount, create_index)) {
status = FSSH_EROFS;
goto out;
}
status = FS_MOUNT_CALL(mount, create_index, name, type, flags);
out:
put_mount(mount);
return status;
}
static fssh_status_t
index_name_read_stat(fssh_mount_id mountID, const char *name,
struct fssh_stat *stat, bool kernel)
{
FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel));
struct fs_mount *mount;
fssh_status_t status = get_mount(mountID, &mount);
if (status < FSSH_B_OK)
return status;
if (!HAS_FS_MOUNT_CALL(mount, read_index_stat)) {
status = FSSH_EOPNOTSUPP;
goto out;
}
status = FS_MOUNT_CALL(mount, read_index_stat, name, stat);
out:
put_mount(mount);
return status;
}
static fssh_status_t
index_remove(fssh_mount_id mountID, const char *name, bool kernel)
{
FUNCTION(("index_remove(mountID = %ld, name = %s, kernel = %d)\n", mountID, name, kernel));
struct fs_mount *mount;
fssh_status_t status = get_mount(mountID, &mount);
if (status < FSSH_B_OK)
return status;
if (!HAS_FS_MOUNT_CALL(mount, remove_index)) {
status = FSSH_EROFS;
goto out;
}
status = FS_MOUNT_CALL(mount, remove_index, name);
out:
put_mount(mount);
return status;
}
static int
query_open(fssh_dev_t device, const char *query, uint32_t flags,
fssh_port_id port, int32_t token, bool kernel)
{
struct fs_mount *mount;
void *cookie;
FUNCTION(("query_open(device = %ld, query = \"%s\", kernel = %d)\n", device, query, kernel));
fssh_status_t status = get_mount(device, &mount);
if (status < FSSH_B_OK)
return status;
if (!HAS_FS_MOUNT_CALL(mount, open_query)) {
status = FSSH_EOPNOTSUPP;
goto out;
}
status = FS_MOUNT_CALL(mount, open_query, query, flags, port, token, &cookie);
if (status < FSSH_B_OK)
goto out;
status = get_new_fd(FDTYPE_QUERY, mount, NULL, cookie, 0, kernel);
if (status >= 0)
goto out;
FS_MOUNT_CALL(mount, close_query, cookie);
FS_MOUNT_CALL(mount, free_query_cookie, cookie);
out:
put_mount(mount);
return status;
}
static fssh_status_t
query_close(struct file_descriptor *descriptor)
{
struct fs_mount *mount = descriptor->u.mount;
FUNCTION(("query_close(descriptor = %p)\n", descriptor));
if (HAS_FS_MOUNT_CALL(mount, close_query))
return FS_MOUNT_CALL(mount, close_query, descriptor->cookie);
return FSSH_B_OK;
}
static void
query_free_fd(struct file_descriptor *descriptor)
{
struct fs_mount *mount = descriptor->u.mount;
if (mount != NULL) {
FS_MOUNT_CALL(mount, free_query_cookie, descriptor->cookie);
}
}
static fssh_status_t
query_read(struct file_descriptor *descriptor, struct fssh_dirent *buffer,
fssh_size_t bufferSize, uint32_t *_count)
{
struct fs_mount *mount = descriptor->u.mount;
if (HAS_FS_MOUNT_CALL(mount, read_query))
return FS_MOUNT_CALL(mount, read_query, descriptor->cookie, buffer, bufferSize, _count);
return FSSH_EOPNOTSUPP;
}
static fssh_status_t
query_rewind(struct file_descriptor *descriptor)
{
struct fs_mount *mount = descriptor->u.mount;
if (HAS_FS_MOUNT_CALL(mount, rewind_query))
return FS_MOUNT_CALL(mount, rewind_query, descriptor->cookie);
return FSSH_EOPNOTSUPP;
}
static fssh_dev_t
fs_mount(char *path, const char *device, const char *fsName, uint32_t flags,
const char *args, bool kernel)
{
struct fs_mount *mount;
fssh_status_t status = 0;
FUNCTION(("fs_mount: entry. path = '%s', fs_name = '%s'\n", path, fsName));
if (fsName == NULL) {
if (!device || flags & FSSH_B_MOUNT_VIRTUAL_DEVICE)
return FSSH_B_BAD_VALUE;
} else if (fsName[0] == '\0')
return FSSH_B_BAD_VALUE;
RecursiveLocker mountOpLocker(sMountOpLock);
KPath normalizedDevice;
if (!(flags & FSSH_B_MOUNT_VIRTUAL_DEVICE) && device) {
status = normalizedDevice.SetTo(device, false);
if (status != FSSH_B_OK)
return status;
device = normalizedDevice.Path();
}
mount = (struct fs_mount *)malloc(sizeof(struct fs_mount));
if (mount == NULL)
return FSSH_B_NO_MEMORY;
mount->volume = (fssh_fs_volume*)malloc(sizeof(fssh_fs_volume));
if (mount->volume == NULL) {
free(mount);
return FSSH_B_NO_MEMORY;
}
list_init_etc(&mount->vnodes, fssh_offsetof(struct vnode, mount_link));
mount->fs_name = get_file_system_name(fsName);
if (mount->fs_name == NULL) {
status = FSSH_B_NO_MEMORY;
goto err1;
}
mount->device_name = fssh_strdup(device);
mount->fs = get_file_system(fsName);
if (mount->fs == NULL) {
status = FSSH_ENODEV;
goto err3;
}
fssh_recursive_lock_init(&mount->rlock, "mount rlock");
mount->id = sNextMountID++;
mount->root_vnode = NULL;
mount->covers_vnode = NULL;
mount->unmounting = false;
mount->owns_file_device = false;
mount->volume->id = mount->id;
mount->volume->layer = 0;
mount->volume->private_volume = NULL;
mount->volume->ops = NULL;
mount->volume->sub_volume = NULL;
mount->volume->super_volume = NULL;
fssh_mutex_lock(&sMountMutex);
hash_insert(sMountsTable, mount);
fssh_mutex_unlock(&sMountMutex);
fssh_vnode_id rootID;
if (!sRoot) {
if (fssh_strcmp(path, "/") != 0) {
status = FSSH_B_ERROR;
goto err4;
}
status = mount->fs->mount(mount->volume, device, flags, args, &rootID);
if (status < 0) {
goto err4;
}
} else {
struct vnode *coveredVnode;
status = path_to_vnode(path, true, &coveredVnode, NULL, kernel);
if (status < FSSH_B_OK)
goto err4;
struct fssh_stat coveredNodeStat;
status = FS_CALL(coveredVnode, read_stat, &coveredNodeStat);
if (status < FSSH_B_OK)
goto err4;
if (!FSSH_S_ISDIR(coveredNodeStat.fssh_st_mode)) {
status = FSSH_B_NOT_A_DIRECTORY;
goto err4;
}
if (coveredVnode->mount->root_vnode == coveredVnode) {
status = FSSH_B_BUSY;
goto err4;
}
mount->covers_vnode = coveredVnode;
status = mount->fs->mount(mount->volume, device, flags, args, &rootID);
if (status < FSSH_B_OK)
goto err5;
}
mount->root_vnode = lookup_vnode(mount->id, rootID);
if (mount->root_vnode == NULL || mount->root_vnode->ref_count != 1) {
fssh_panic("fs_mount: file system does not own its root node!\n");
status = FSSH_B_ERROR;
goto err6;
}
fssh_mutex_lock(&sVnodeCoveredByMutex);
if (mount->covers_vnode)
mount->covers_vnode->covered_by = mount->root_vnode;
fssh_mutex_unlock(&sVnodeCoveredByMutex);
if (!sRoot)
sRoot = mount->root_vnode;
return mount->id;
err6:
FS_MOUNT_CALL_NO_PARAMS(mount, unmount);
err5:
if (mount->covers_vnode)
put_vnode(mount->covers_vnode);
err4:
fssh_mutex_lock(&sMountMutex);
hash_remove(sMountsTable, mount);
fssh_mutex_unlock(&sMountMutex);
fssh_recursive_lock_destroy(&mount->rlock);
put_file_system(mount->fs);
free(mount->device_name);
err3:
free(mount->fs_name);
err1:
free(mount->volume);
free(mount);
return status;
}
static fssh_status_t
fs_unmount(char *path, uint32_t flags, bool kernel)
{
struct fs_mount *mount;
struct vnode *vnode;
fssh_status_t err;
FUNCTION(("vfs_unmount: entry. path = '%s', kernel %d\n", path, kernel));
err = path_to_vnode(path, true, &vnode, NULL, kernel);
if (err < 0)
return FSSH_B_ENTRY_NOT_FOUND;
RecursiveLocker mountOpLocker(sMountOpLock);
mount = find_mount(vnode->device);
if (!mount)
fssh_panic("vfs_unmount: find_mount() failed on root vnode @%p of mount\n", vnode);
if (mount->root_vnode != vnode) {
put_vnode(vnode);
return FSSH_B_BAD_VALUE;
}
fssh_mutex_lock(&sVnodeMutex);
bool disconnectedDescriptors = false;
while (true) {
bool busy = false;
vnode = NULL;
while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) {
if (vnode->busy
|| ((vnode->ref_count != 0 && mount->root_vnode != vnode)
|| (vnode->ref_count != 2 && mount->root_vnode == vnode))) {
busy = true;
break;
}
}
if (!busy)
break;
if ((flags & FSSH_B_FORCE_UNMOUNT) == 0) {
fssh_mutex_unlock(&sVnodeMutex);
put_vnode(mount->root_vnode);
return FSSH_B_BUSY;
}
if (disconnectedDescriptors) {
fssh_mutex_unlock(&sVnodeMutex);
fssh_snooze(100000);
fssh_mutex_lock(&sVnodeMutex);
continue;
}
mount->unmounting = true;
fssh_mutex_unlock(&sVnodeMutex);
disconnect_mount_or_vnode_fds(mount, NULL);
disconnectedDescriptors = true;
fssh_mutex_lock(&sVnodeMutex);
}
mount->unmounting = true;
while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) {
vnode->busy = true;
if (vnode->ref_count == 0) {
list_remove_item(&sUnusedVnodeList, vnode);
sUnusedVnodes--;
}
}
mount->root_vnode->ref_count -= 2;
fssh_mutex_unlock(&sVnodeMutex);
fssh_mutex_lock(&sVnodeCoveredByMutex);
mount->covers_vnode->covered_by = NULL;
fssh_mutex_unlock(&sVnodeCoveredByMutex);
put_vnode(mount->covers_vnode);
while ((vnode = (struct vnode *)list_get_first_item(&mount->vnodes)) != NULL) {
free_vnode(vnode, false);
}
fssh_mutex_lock(&sMountMutex);
hash_remove(sMountsTable, mount);
fssh_mutex_unlock(&sMountMutex);
mountOpLocker.Unlock();
FS_MOUNT_CALL_NO_PARAMS(mount, unmount);
put_file_system(mount->fs);
free(mount->device_name);
free(mount->fs_name);
free(mount);
return FSSH_B_OK;
}
static fssh_status_t
fs_sync(fssh_dev_t device)
{
struct fs_mount *mount;
fssh_status_t status = get_mount(device, &mount);
if (status < FSSH_B_OK)
return status;
fssh_mutex_lock(&sMountMutex);
if (HAS_FS_MOUNT_CALL(mount, sync))
status = FS_MOUNT_CALL_NO_PARAMS(mount, sync);
fssh_mutex_unlock(&sMountMutex);
struct vnode *previousVnode = NULL;
while (true) {
fssh_recursive_lock_lock(&mount->rlock);
struct vnode *vnode = (struct vnode *)list_get_next_item(&mount->vnodes,
previousVnode);
fssh_vnode_id id = -1;
if (vnode != NULL)
id = vnode->id;
fssh_recursive_lock_unlock(&mount->rlock);
if (vnode == NULL)
break;
if (get_vnode(mount->id, id, &vnode, true) == FSSH_B_OK) {
if (previousVnode != NULL)
put_vnode(previousVnode);
if (HAS_FS_CALL(vnode, fsync))
FS_CALL(vnode, fsync, false);
previousVnode = vnode;
} else {
fssh_dprintf("syncing of mount %d stopped due to vnode %"
FSSH_B_PRIdINO ".\n", (int)mount->id, id);
break;
}
}
if (previousVnode != NULL)
put_vnode(previousVnode);
put_mount(mount);
return status;
}
static fssh_status_t
fs_read_info(fssh_dev_t device, struct fssh_fs_info *info)
{
struct fs_mount *mount;
fssh_status_t status = get_mount(device, &mount);
if (status < FSSH_B_OK)
return status;
fssh_memset(info, 0, sizeof(struct fssh_fs_info));
if (HAS_FS_MOUNT_CALL(mount, read_fs_info))
status = FS_MOUNT_CALL(mount, read_fs_info, info);
if (status == FSSH_B_OK) {
info->dev = mount->id;
info->root = mount->root_vnode->id;
fssh_strlcpy(info->fsh_name, mount->fs_name, sizeof(info->fsh_name));
if (mount->device_name != NULL) {
fssh_strlcpy(info->device_name, mount->device_name,
sizeof(info->device_name));
}
}
put_mount(mount);
return status;
}
static fssh_status_t
fs_write_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask)
{
struct fs_mount *mount;
fssh_status_t status = get_mount(device, &mount);
if (status < FSSH_B_OK)
return status;
if (HAS_FS_MOUNT_CALL(mount, write_fs_info))
status = FS_MOUNT_CALL(mount, write_fs_info, info, mask);
else
status = FSSH_EROFS;
put_mount(mount);
return status;
}
static fssh_dev_t
fs_next_device(int32_t *_cookie)
{
struct fs_mount *mount = NULL;
fssh_dev_t device = *_cookie;
fssh_mutex_lock(&sMountMutex);
while (device < sNextMountID) {
mount = find_mount(device++);
if (mount != NULL && mount->volume->private_volume != NULL)
break;
}
*_cookie = device;
if (mount != NULL)
device = mount->id;
else
device = FSSH_B_BAD_VALUE;
fssh_mutex_unlock(&sMountMutex);
return device;
}
static fssh_status_t
get_cwd(char *buffer, fssh_size_t size, bool kernel)
{
struct io_context *context = get_current_io_context(kernel);
fssh_status_t status;
FUNCTION(("vfs_get_cwd: buf %p, size %ld\n", buffer, size));
fssh_mutex_lock(&context->io_mutex);
if (context->cwd)
status = dir_vnode_to_path(context->cwd, buffer, size);
else
status = FSSH_B_ERROR;
fssh_mutex_unlock(&context->io_mutex);
return status;
}
static fssh_status_t
set_cwd(int fd, char *path, bool kernel)
{
struct io_context *context;
struct vnode *vnode = NULL;
struct vnode *oldDirectory;
struct fssh_stat stat;
fssh_status_t status;
FUNCTION(("set_cwd: path = \'%s\'\n", path));
status = fd_and_path_to_vnode(fd, path, true, &vnode, NULL, kernel);
if (status < 0)
return status;
status = FS_CALL(vnode, read_stat, &stat);
if (status < 0)
goto err;
if (!FSSH_S_ISDIR(stat.fssh_st_mode)) {
status = FSSH_B_NOT_A_DIRECTORY;
goto err;
}
context = get_current_io_context(kernel);
fssh_mutex_lock(&context->io_mutex);
oldDirectory = context->cwd;
context->cwd = vnode;
fssh_mutex_unlock(&context->io_mutex);
if (oldDirectory)
put_vnode(oldDirectory);
return FSSH_B_NO_ERROR;
err:
put_vnode(vnode);
return status;
}
fssh_dev_t
_kern_mount(const char *path, const char *device, const char *fsName,
uint32_t flags, const char *args, fssh_size_t argsLength)
{
KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
if (pathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
return fs_mount(pathBuffer.LockBuffer(), device, fsName, flags, args, true);
}
fssh_status_t
_kern_unmount(const char *path, uint32_t flags)
{
KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
if (pathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
return fs_unmount(pathBuffer.LockBuffer(), flags, true);
}
fssh_status_t
_kern_read_fs_info(fssh_dev_t device, struct fssh_fs_info *info)
{
if (info == NULL)
return FSSH_B_BAD_VALUE;
return fs_read_info(device, info);
}
fssh_status_t
_kern_write_fs_info(fssh_dev_t device, const struct fssh_fs_info *info, int mask)
{
if (info == NULL)
return FSSH_B_BAD_VALUE;
return fs_write_info(device, info, mask);
}
fssh_status_t
_kern_sync(void)
{
int32_t cookie = 0;
fssh_dev_t device;
while ((device = fs_next_device(&cookie)) >= 0) {
fssh_status_t status = fs_sync(device);
if (status != FSSH_B_OK && status != FSSH_B_BAD_VALUE)
fssh_dprintf("sync: device %d couldn't sync: %s\n", (int)device, fssh_strerror(status));
}
return FSSH_B_OK;
}
fssh_dev_t
_kern_next_device(int32_t *_cookie)
{
return fs_next_device(_cookie);
}
int
_kern_open_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int openMode, int perms)
{
if (openMode & FSSH_O_CREAT)
return file_create_entry_ref(device, inode, name, openMode, perms, true);
return file_open_entry_ref(device, inode, name, openMode, true);
}
int
_kern_open(int fd, const char *path, int openMode, int perms)
{
CALLED();
KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
if (pathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
if (openMode & FSSH_O_CREAT)
return file_create(fd, pathBuffer.LockBuffer(), openMode, perms, true);
return file_open(fd, pathBuffer.LockBuffer(), openMode, true);
}
int
_kern_open_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name)
{
return dir_open_entry_ref(device, inode, name, true);
}
int
_kern_open_dir(int fd, const char *path)
{
KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
if (pathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
return dir_open(fd, pathBuffer.LockBuffer(), true);
}
fssh_status_t
_kern_fcntl(int fd, int op, uint32_t argument)
{
return common_fcntl(fd, op, argument, true);
}
fssh_status_t
_kern_fsync(int fd)
{
return common_sync(fd, false, true);
}
fssh_status_t
_kern_lock_node(int fd)
{
return common_lock_node(fd, true);
}
fssh_status_t
_kern_unlock_node(int fd)
{
return common_unlock_node(fd, true);
}
fssh_status_t
_kern_create_dir_entry_ref(fssh_dev_t device, fssh_ino_t inode, const char *name, int perms)
{
return dir_create_entry_ref(device, inode, name, perms, true);
}
fssh_status_t
_kern_create_dir(int fd, const char *path, int perms)
{
KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
if (pathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
return dir_create(fd, pathBuffer.LockBuffer(), perms, true);
}
fssh_status_t
_kern_remove_dir(int fd, const char *path)
{
if (path) {
KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
if (pathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
return dir_remove(fd, pathBuffer.LockBuffer(), true);
}
return dir_remove(fd, NULL, true);
}
fssh_status_t
_kern_read_link(int fd, const char *path, char *buffer, fssh_size_t *_bufferSize)
{
if (path) {
KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
if (pathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
return common_read_link(fd, pathBuffer.LockBuffer(),
buffer, _bufferSize, true);
}
return common_read_link(fd, NULL, buffer, _bufferSize, true);
}
fssh_status_t
_kern_create_symlink(int fd, const char *path, const char *toPath, int mode)
{
KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
char *toBuffer = toPathBuffer.LockBuffer();
fssh_status_t status = check_path(toBuffer);
if (status < FSSH_B_OK)
return status;
return common_create_symlink(fd, pathBuffer.LockBuffer(),
toBuffer, mode, true);
}
fssh_status_t
_kern_create_link(const char *path, const char *toPath)
{
KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
KPath toPathBuffer(toPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
if (pathBuffer.InitCheck() != FSSH_B_OK || toPathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
return common_create_link(pathBuffer.LockBuffer(),
toPathBuffer.LockBuffer(), true);
}
fssh_status_t
_kern_unlink(int fd, const char *path)
{
KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
if (pathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
return common_unlink(fd, pathBuffer.LockBuffer(), true);
}
fssh_status_t
_kern_rename(int oldFD, const char *oldPath, int newFD, const char *newPath)
{
KPath oldPathBuffer(oldPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
KPath newPathBuffer(newPath, false, FSSH_B_PATH_NAME_LENGTH + 1);
if (oldPathBuffer.InitCheck() != FSSH_B_OK || newPathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
return common_rename(oldFD, oldPathBuffer.LockBuffer(),
newFD, newPathBuffer.LockBuffer(), true);
}
fssh_status_t
_kern_access(const char *path, int mode)
{
KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
if (pathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
return common_access(pathBuffer.LockBuffer(), mode, true);
}
fssh_status_t
_kern_read_stat(int fd, const char *path, bool traverseLeafLink,
fssh_struct_stat *stat, fssh_size_t statSize)
{
fssh_struct_stat completeStat;
fssh_struct_stat *originalStat = NULL;
fssh_status_t status;
if (statSize > sizeof(fssh_struct_stat))
return FSSH_B_BAD_VALUE;
if (statSize < sizeof(fssh_struct_stat)) {
originalStat = stat;
stat = &completeStat;
}
if (path) {
KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
if (pathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
status = common_path_read_stat(fd, pathBuffer.LockBuffer(),
traverseLeafLink, stat, true);
} else {
struct file_descriptor *descriptor
= get_fd(get_current_io_context(true), fd);
if (descriptor == NULL)
return FSSH_B_FILE_ERROR;
if (descriptor->ops->fd_read_stat)
status = descriptor->ops->fd_read_stat(descriptor, stat);
else
status = FSSH_EOPNOTSUPP;
put_fd(descriptor);
}
if (status == FSSH_B_OK && originalStat != NULL)
fssh_memcpy(originalStat, stat, statSize);
return status;
}
fssh_status_t
_kern_write_stat(int fd, const char *path, bool traverseLeafLink,
const fssh_struct_stat *stat, fssh_size_t statSize, int statMask)
{
fssh_struct_stat completeStat;
if (statSize > sizeof(fssh_struct_stat))
return FSSH_B_BAD_VALUE;
if (statSize < sizeof(fssh_struct_stat)) {
fssh_memset((uint8_t *)&completeStat + statSize, 0, sizeof(fssh_struct_stat) - statSize);
fssh_memcpy(&completeStat, stat, statSize);
stat = &completeStat;
}
fssh_status_t status;
if (path) {
KPath pathBuffer(path, false, FSSH_B_PATH_NAME_LENGTH + 1);
if (pathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
status = common_path_write_stat(fd, pathBuffer.LockBuffer(),
traverseLeafLink, stat, statMask, true);
} else {
struct file_descriptor *descriptor
= get_fd(get_current_io_context(true), fd);
if (descriptor == NULL)
return FSSH_B_FILE_ERROR;
if (descriptor->ops->fd_write_stat)
status = descriptor->ops->fd_write_stat(descriptor, stat, statMask);
else
status = FSSH_EOPNOTSUPP;
put_fd(descriptor);
}
return status;
}
int
_kern_open_attr_dir(int fd, const char *path)
{
KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
if (pathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
if (path != NULL)
pathBuffer.SetTo(path);
return attr_dir_open(fd, path ? pathBuffer.LockBuffer() : NULL, true);
}
int
_kern_create_attr(int fd, const char *name, uint32_t type, int openMode)
{
return attr_create(fd, name, type, openMode, true);
}
int
_kern_open_attr(int fd, const char *name, int openMode)
{
return attr_open(fd, name, openMode, true);
}
fssh_status_t
_kern_remove_attr(int fd, const char *name)
{
return attr_remove(fd, name, true);
}
fssh_status_t
_kern_rename_attr(int fromFile, const char *fromName, int toFile, const char *toName)
{
return attr_rename(fromFile, fromName, toFile, toName, true);
}
int
_kern_open_index_dir(fssh_dev_t device)
{
return index_dir_open(device, true);
}
fssh_status_t
_kern_create_index(fssh_dev_t device, const char *name, uint32_t type, uint32_t flags)
{
return index_create(device, name, type, flags, true);
}
fssh_status_t
_kern_read_index_stat(fssh_dev_t device, const char *name, fssh_struct_stat *stat)
{
return index_name_read_stat(device, name, stat, true);
}
fssh_status_t
_kern_remove_index(fssh_dev_t device, const char *name)
{
return index_remove(device, name, true);
}
fssh_status_t
_kern_getcwd(char *buffer, fssh_size_t size)
{
TRACE(("_kern_getcwd: buf %p, %ld\n", buffer, size));
return get_cwd(buffer, size, true);
}
fssh_status_t
_kern_setcwd(int fd, const char *path)
{
KPath pathBuffer(FSSH_B_PATH_NAME_LENGTH + 1);
if (pathBuffer.InitCheck() != FSSH_B_OK)
return FSSH_B_NO_MEMORY;
if (path != NULL)
pathBuffer.SetTo(path);
return set_cwd(fd, path != NULL ? pathBuffer.LockBuffer() : NULL, true);
}
fssh_status_t
_kern_initialize_volume(const char* fsName, const char *partition,
const char *name, const char *parameters)
{
if (!fsName || ! partition)
return FSSH_B_BAD_VALUE;
int fd = fssh_open(partition, FSSH_O_RDWR);
if (fd < 0)
return fssh_errno;
fssh_file_system_module_info* fsModule = get_file_system(fsName);
if (fsModule == NULL) {
fssh_close(fd);
return FSSH_ENODEV;
}
fssh_status_t status;
if (fsModule->initialize) {
status = (*fsModule->initialize)(fd, -1, name, parameters, 0, -1);
} else
status = FSSH_B_NOT_SUPPORTED;
put_file_system(fsModule);
fssh_close(fd);
return status;
}
fssh_status_t
_kern_entry_ref_to_path(fssh_dev_t device, fssh_ino_t inode, const char *leaf,
char* path, fssh_size_t pathLength)
{
return vfs_entry_ref_to_path(device, inode, leaf, true, path, pathLength);
}
int
_kern_open_query(fssh_dev_t device, const char *query, fssh_size_t queryLength,
uint32_t flags, fssh_port_id port, int32_t token)
{
return query_open(device, query, flags, port, token, false);
}
}
#include "vfs_request_io.cpp"