From 83812f67529c88d4fb4b942162a9f21142e683fa Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sat, 24 Feb 2007 00:30:19 +0000 Subject: [PATCH] Copied userlandfs code from the test tree to the haiku source tree, where it will be ported to Haiku. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20216 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/userlandfs/legacy/cache.h | 122 + headers/private/userlandfs/legacy/fsproto.h | 258 ++ headers/private/userlandfs/legacy/lock.h | 55 + .../private/userlandfs/private/AreaSupport.h | 17 + .../userlandfs/private/DispatcherDefs.h | 21 + headers/private/userlandfs/private/Port.h | 58 + headers/private/userlandfs/private/Request.h | 84 + .../userlandfs/private/RequestAllocator.h | 89 + .../userlandfs/private/RequestHandler.h | 35 + .../private/userlandfs/private/RequestPort.h | 73 + headers/private/userlandfs/private/Requests.h | 1892 +++++++++++ .../private/SingleReplyRequestHandler.h | 26 + .../userlandfs/private/userlandfs_ioctl.h | 55 + .../private/userlandfs/shared/AutoDeleter.h | 136 + .../private/userlandfs/shared/AutoLocker.h | 164 + .../private/userlandfs/shared/Compatibility.h | 51 + headers/private/userlandfs/shared/DLList.h | 384 +++ headers/private/userlandfs/shared/Debug.h | 140 + .../userlandfs/shared/DriverSettings.h | 130 + headers/private/userlandfs/shared/HashMap.h | 444 +++ headers/private/userlandfs/shared/HashSet.h | 323 ++ .../userlandfs/shared/LazyInitializable.h | 31 + headers/private/userlandfs/shared/Locker.h | 59 + .../private/userlandfs/shared/ObjectTracker.h | 80 + .../private/userlandfs/shared/OpenHashTable.h | 510 +++ .../private/userlandfs/shared/Referencable.h | 118 + headers/private/userlandfs/shared/SLList.h | 332 ++ headers/private/userlandfs/shared/String.h | 70 + headers/private/userlandfs/shared/Vector.h | 798 +++++ .../userlandfs/kernel_add_on/FileSystem.cpp | 359 +++ .../userlandfs/kernel_add_on/FileSystem.h | 80 + .../userlandfs/kernel_add_on/IOCtlInfo.h | 16 + .../userlandfs/kernel_add_on/Jamfile | 57 + .../userlandfs/kernel_add_on/KernelDebug.cpp | 109 + .../userlandfs/kernel_add_on/KernelDebug.h | 23 + .../kernel_add_on/KernelRequestHandler.cpp | 355 +++ .../kernel_add_on/KernelRequestHandler.h | 70 + .../userlandfs/kernel_add_on/PortReleaser.h | 29 + .../kernel_add_on/RequestPortPool.cpp | 130 + .../kernel_add_on/RequestPortPool.h | 48 + .../userlandfs/kernel_add_on/Settings.cpp | 254 ++ .../userlandfs/kernel_add_on/Settings.h | 35 + .../userlandfs/kernel_add_on/UserlandFS.cpp | 258 ++ .../userlandfs/kernel_add_on/UserlandFS.h | 54 + .../userlandfs/kernel_add_on/Volume.cpp | 2800 +++++++++++++++++ .../userlandfs/kernel_add_on/Volume.h | 207 ++ .../userlandfs/kernel_add_on/kernel-cpp.cpp | 16 + .../userlandfs/kernel_add_on/kernel-cpp.h | 48 + .../kernel_add_on/kernel_interface.cpp | 1140 +++++++ .../userlandfs/private/AreaSupport.cpp | 34 + .../userlandfs/private/DispatcherDefs.cpp | 6 + .../file_systems/userlandfs/private/Port.cpp | 203 ++ .../userlandfs/private/Request.cpp | 54 + .../userlandfs/private/RequestAllocator.cpp | 289 ++ .../userlandfs/private/RequestHandler.cpp | 30 + .../userlandfs/private/RequestPort.cpp | 204 ++ .../userlandfs/private/Requests.cpp | 869 +++++ .../private/SingleReplyRequestHandler.cpp | 39 + .../userlandfs/private/userlandfs_ioctl.cpp | 6 + .../file_systems/userlandfs/server/FSInfo.h | 135 + .../userlandfs/server/FileSystem.cpp | 145 + .../userlandfs/server/FileSystem.h | 46 + .../file_systems/userlandfs/server/Jamfile | 54 + .../server/KernelUserFileSystem.cpp | 45 + .../userlandfs/server/KernelUserFileSystem.h | 26 + .../userlandfs/server/KernelUserVolume.cpp | 606 ++++ .../userlandfs/server/KernelUserVolume.h | 139 + .../userlandfs/server/RequestThread.cpp | 179 ++ .../userlandfs/server/RequestThread.h | 69 + .../userlandfs/server/ServerDefs.cpp | 34 + .../userlandfs/server/ServerDefs.h | 41 + .../userlandfs/server/UserFileSystem.cpp | 14 + .../userlandfs/server/UserFileSystem.h | 27 + .../userlandfs/server/UserVolume.cpp | 479 +++ .../userlandfs/server/UserVolume.h | 143 + .../server/UserlandFSDispatcher.cpp | 416 +++ .../userlandfs/server/UserlandFSDispatcher.h | 65 + .../userlandfs/server/UserlandFSServer.cpp | 197 ++ .../userlandfs/server/UserlandFSServer.h | 39 + .../server/UserlandRequestHandler.cpp | 1851 +++++++++++ .../server/UserlandRequestHandler.h | 138 + .../file_systems/userlandfs/server/cache.c | 2427 ++++++++++++++ .../file_systems/userlandfs/server/compat.h | 204 ++ .../userlandfs/server/kernel_emu.cpp | 501 +++ .../file_systems/userlandfs/server/main.cpp | 113 + .../file_systems/userlandfs/server/sysdep.c | 350 +++ .../file_systems/userlandfs/shared/Debug.cpp | 170 + .../userlandfs/shared/DriverSettings.cpp | 492 +++ .../userlandfs/shared/LazyInitializable.cpp | 60 + .../file_systems/userlandfs/shared/Locker.cpp | 303 ++ .../userlandfs/shared/ObjectTracker.cpp | 101 + .../userlandfs/shared/Referencable.cpp | 41 + .../file_systems/userlandfs/shared/String.cpp | 145 + .../userlandfs/shared/driver_settings.c | 768 +++++ .../file_systems/userlandfs/ufs_mount/Jamfile | 15 + .../userlandfs/ufs_mount/ufs_mount.cpp | 50 + 96 files changed, 25005 insertions(+) create mode 100644 headers/private/userlandfs/legacy/cache.h create mode 100644 headers/private/userlandfs/legacy/fsproto.h create mode 100644 headers/private/userlandfs/legacy/lock.h create mode 100644 headers/private/userlandfs/private/AreaSupport.h create mode 100644 headers/private/userlandfs/private/DispatcherDefs.h create mode 100644 headers/private/userlandfs/private/Port.h create mode 100644 headers/private/userlandfs/private/Request.h create mode 100644 headers/private/userlandfs/private/RequestAllocator.h create mode 100644 headers/private/userlandfs/private/RequestHandler.h create mode 100644 headers/private/userlandfs/private/RequestPort.h create mode 100644 headers/private/userlandfs/private/Requests.h create mode 100644 headers/private/userlandfs/private/SingleReplyRequestHandler.h create mode 100644 headers/private/userlandfs/private/userlandfs_ioctl.h create mode 100644 headers/private/userlandfs/shared/AutoDeleter.h create mode 100644 headers/private/userlandfs/shared/AutoLocker.h create mode 100644 headers/private/userlandfs/shared/Compatibility.h create mode 100644 headers/private/userlandfs/shared/DLList.h create mode 100644 headers/private/userlandfs/shared/Debug.h create mode 100644 headers/private/userlandfs/shared/DriverSettings.h create mode 100644 headers/private/userlandfs/shared/HashMap.h create mode 100644 headers/private/userlandfs/shared/HashSet.h create mode 100644 headers/private/userlandfs/shared/LazyInitializable.h create mode 100644 headers/private/userlandfs/shared/Locker.h create mode 100644 headers/private/userlandfs/shared/ObjectTracker.h create mode 100644 headers/private/userlandfs/shared/OpenHashTable.h create mode 100644 headers/private/userlandfs/shared/Referencable.h create mode 100644 headers/private/userlandfs/shared/SLList.h create mode 100644 headers/private/userlandfs/shared/String.h create mode 100644 headers/private/userlandfs/shared/Vector.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/FileSystem.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/FileSystem.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/IOCtlInfo.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Jamfile create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelDebug.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelDebug.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelRequestHandler.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelRequestHandler.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/PortReleaser.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/RequestPortPool.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/RequestPortPool.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Settings.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Settings.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/UserlandFS.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/UserlandFS.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Volume.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Volume.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/kernel-cpp.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/kernel-cpp.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/kernel_interface.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/private/AreaSupport.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/private/DispatcherDefs.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/private/Port.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/private/Request.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/private/RequestAllocator.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/private/RequestHandler.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/private/RequestPort.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/private/Requests.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/private/SingleReplyRequestHandler.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/private/userlandfs_ioctl.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/FSInfo.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/FileSystem.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/FileSystem.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/Jamfile create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/KernelUserFileSystem.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/KernelUserFileSystem.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/KernelUserVolume.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/KernelUserVolume.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/RequestThread.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/RequestThread.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/ServerDefs.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/ServerDefs.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/UserFileSystem.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/UserFileSystem.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/UserVolume.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/UserVolume.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/UserlandFSDispatcher.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/UserlandFSDispatcher.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/UserlandFSServer.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/UserlandFSServer.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/UserlandRequestHandler.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/UserlandRequestHandler.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/cache.c create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/compat.h create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/kernel_emu.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/main.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/server/sysdep.c create mode 100644 src/add-ons/kernel/file_systems/userlandfs/shared/Debug.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/shared/DriverSettings.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/shared/LazyInitializable.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/shared/Locker.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/shared/ObjectTracker.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/shared/Referencable.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/shared/String.cpp create mode 100644 src/add-ons/kernel/file_systems/userlandfs/shared/driver_settings.c create mode 100644 src/add-ons/kernel/file_systems/userlandfs/ufs_mount/Jamfile create mode 100644 src/add-ons/kernel/file_systems/userlandfs/ufs_mount/ufs_mount.cpp diff --git a/headers/private/userlandfs/legacy/cache.h b/headers/private/userlandfs/legacy/cache.h new file mode 100644 index 0000000000..22f59954f9 --- /dev/null +++ b/headers/private/userlandfs/legacy/cache.h @@ -0,0 +1,122 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#ifndef _CACHE_H_ +#define _CACHE_H_ + +#include + +#include "lock.h" + +#ifndef _IMPEXP_KERNEL +#define _IMPEXP_KERNEL +#endif + +typedef struct hash_ent { + int dev; + off_t bnum; + off_t hash_val; + void *data; + struct hash_ent *next; +} hash_ent; + + +typedef struct hash_table { + hash_ent **table; + int max; + int mask; /* == max - 1 */ + int num_elements; +} hash_table; + + +#define HT_DEFAULT_MAX 128 + + +typedef struct cache_ent { + int dev; + off_t block_num; + int bsize; + volatile int flags; + + void *data; + void *clone; /* copy of data by set_block_info() */ + int lock; + + void (*func)(off_t bnum, size_t num_blocks, void *arg); + off_t logged_bnum; + void *arg; + + struct cache_ent *next, /* points toward mru end of list */ + *prev; /* points toward lru end of list */ + +} cache_ent; + +#define CE_NORMAL 0x0000 /* a nice clean pristine page */ +#define CE_DIRTY 0x0002 /* needs to be written to disk */ +#define CE_BUSY 0x0004 /* this block has i/o happening, don't touch it */ + + +typedef struct cache_ent_list { + cache_ent *lru; /* tail of the list */ + cache_ent *mru; /* head of the list */ +} cache_ent_list; + + +typedef struct block_cache { + struct lock lock; + int flags; + int cur_blocks; + int max_blocks; + hash_table ht; + + cache_ent_list normal, /* list of "normal" blocks (clean & dirty) */ + locked; /* list of clean and locked blocks */ +} block_cache; + +#if 0 /* XXXdbg -- need to deal with write through caches */ +#define DC_WRITE_THROUGH 0x0001 /* cache is write-through (for floppies) */ +#endif + +#define ALLOW_WRITES 1 +#define NO_WRITES 0 + +#ifdef __cplusplus + extern "C" { +#endif + +extern _IMPEXP_KERNEL int init_block_cache(int max_blocks, int flags); +extern _IMPEXP_KERNEL void shutdown_block_cache(void); + +extern _IMPEXP_KERNEL void force_cache_flush(int dev, int prefer_log_blocks); +extern _IMPEXP_KERNEL int flush_blocks(int dev, off_t bnum, int nblocks); +extern _IMPEXP_KERNEL int flush_device(int dev, int warn_locked); + +extern _IMPEXP_KERNEL int init_cache_for_device(int fd, off_t max_blocks); +extern _IMPEXP_KERNEL int remove_cached_device_blocks(int dev, int allow_write); + +extern _IMPEXP_KERNEL void *get_block(int dev, off_t bnum, int bsize); +extern _IMPEXP_KERNEL void *get_empty_block(int dev, off_t bnum, int bsize); +extern _IMPEXP_KERNEL int release_block(int dev, off_t bnum); +extern _IMPEXP_KERNEL int mark_blocks_dirty(int dev, off_t bnum, int nblocks); + + +extern _IMPEXP_KERNEL int cached_read(int dev, off_t bnum, void *data, off_t num_blocks, int bsize); +extern _IMPEXP_KERNEL int cached_write(int dev, off_t bnum, const void *data, + off_t num_blocks, int bsize); +extern _IMPEXP_KERNEL int cached_write_locked(int dev, off_t bnum, const void *data, + off_t num_blocks, int bsize); +extern _IMPEXP_KERNEL int set_blocks_info(int dev, off_t *blocks, int nblocks, + void (*func)(off_t bnum, size_t nblocks, void *arg), + void *arg); + + +extern _IMPEXP_KERNEL size_t read_phys_blocks (int fd, off_t bnum, void *data, uint num_blocks, int bsize); +extern _IMPEXP_KERNEL size_t write_phys_blocks(int fd, off_t bnum, void *data, uint num_blocks, int bsize); + +#ifdef __cplusplus + } // extern "C" +#endif + +#endif /* _CACHE_H_ */ diff --git a/headers/private/userlandfs/legacy/fsproto.h b/headers/private/userlandfs/legacy/fsproto.h new file mode 100644 index 0000000000..4e19c09d4c --- /dev/null +++ b/headers/private/userlandfs/legacy/fsproto.h @@ -0,0 +1,258 @@ +#ifndef _FSPROTO_H +#define _FSPROTO_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifndef _IMPEXP_KERNEL +#define _IMPEXP_KERNEL +#endif + +typedef dev_t nspace_id; +typedef ino_t vnode_id; + +/* + * PUBLIC PART OF THE FILE SYSTEM PROTOCOL + */ + +#define WSTAT_MODE 0x0001 +#define WSTAT_UID 0x0002 +#define WSTAT_GID 0x0004 +#define WSTAT_SIZE 0x0008 +#define WSTAT_ATIME 0x0010 +#define WSTAT_MTIME 0x0020 +#define WSTAT_CRTIME 0x0040 + +#define WFSSTAT_NAME 0x0001 + +#define B_ENTRY_CREATED 1 +#define B_ENTRY_REMOVED 2 +#define B_ENTRY_MOVED 3 +#define B_STAT_CHANGED 4 +#define B_ATTR_CHANGED 5 +#define B_DEVICE_MOUNTED 6 +#define B_DEVICE_UNMOUNTED 7 + +#define B_STOP_WATCHING 0x0000 +#define B_WATCH_NAME 0x0001 +#define B_WATCH_STAT 0x0002 +#define B_WATCH_ATTR 0x0004 +#define B_WATCH_DIRECTORY 0x0008 + +#define SELECT_READ 1 +#define SELECT_WRITE 2 +#define SELECT_EXCEPTION 3 + +// missing ioctl() call added +#define IOCTL_FILE_UNCACHED_IO 10000 +#define IOCTL_CREATE_TIME 10002 +#define IOCTL_MODIFIED_TIME 10003 + +#define B_CUR_FS_API_VERSION 2 + +struct attr_info; +struct index_info; + +typedef int op_read_vnode(void *ns, vnode_id vnid, char r, void **node); +typedef int op_write_vnode(void *ns, void *node, char r); +typedef int op_remove_vnode(void *ns, void *node, char r); +typedef int op_secure_vnode(void *ns, void *node); + +typedef int op_walk(void *ns, void *base, const char *file, char **newpath, + vnode_id *vnid); + +typedef int op_access(void *ns, void *node, int mode); + +typedef int op_create(void *ns, void *dir, const char *name, + int omode, int perms, vnode_id *vnid, void **cookie); +typedef int op_mkdir(void *ns, void *dir, const char *name, int perms); +typedef int op_symlink(void *ns, void *dir, const char *name, + const char *path); +typedef int op_link(void *ns, void *dir, const char *name, void *node); + +typedef int op_rename(void *ns, void *olddir, const char *oldname, + void *newdir, const char *newname); +typedef int op_unlink(void *ns, void *dir, const char *name); +typedef int op_rmdir(void *ns, void *dir, const char *name); + +typedef int op_readlink(void *ns, void *node, char *buf, size_t *bufsize); + +typedef int op_opendir(void *ns, void *node, void **cookie); +typedef int op_closedir(void *ns, void *node, void *cookie); +typedef int op_rewinddir(void *ns, void *node, void *cookie); +typedef int op_readdir(void *ns, void *node, void *cookie, long *num, + struct dirent *buf, size_t bufsize); + +typedef int op_open(void *ns, void *node, int omode, void **cookie); +typedef int op_close(void *ns, void *node, void *cookie); +typedef int op_free_cookie(void *ns, void *node, void *cookie); +typedef int op_read(void *ns, void *node, void *cookie, off_t pos, void *buf, + size_t *len); +typedef int op_write(void *ns, void *node, void *cookie, off_t pos, + const void *buf, size_t *len); +typedef int op_readv(void *ns, void *node, void *cookie, off_t pos, const iovec *vec, + size_t count, size_t *len); +typedef int op_writev(void *ns, void *node, void *cookie, off_t pos, const iovec *vec, + size_t count, size_t *len); +typedef int op_ioctl(void *ns, void *node, void *cookie, int cmd, void *buf, + size_t len); +typedef int op_setflags(void *ns, void *node, void *cookie, int flags); + +typedef int op_rstat(void *ns, void *node, struct stat *); +typedef int op_wstat(void *ns, void *node, struct stat *, long mask); +typedef int op_fsync(void *ns, void *node); + +typedef int op_select(void *ns, void *node, void *cookie, uint8 event, + uint32 ref, selectsync *sync); +typedef int op_deselect(void *ns, void *node, void *cookie, uint8 event, + selectsync *sync); + +typedef int op_initialize(const char *devname, void *parms, size_t len); +typedef int op_mount(nspace_id nsid, const char *devname, ulong flags, + void *parms, size_t len, void **data, vnode_id *vnid); +typedef int op_unmount(void *ns); +typedef int op_sync(void *ns); +typedef int op_rfsstat(void *ns, struct fs_info *); +typedef int op_wfsstat(void *ns, struct fs_info *, long mask); + + +typedef int op_open_attrdir(void *ns, void *node, void **cookie); +typedef int op_close_attrdir(void *ns, void *node, void *cookie); +typedef int op_rewind_attrdir(void *ns, void *node, void *cookie); +typedef int op_read_attrdir(void *ns, void *node, void *cookie, long *num, + struct dirent *buf, size_t bufsize); +typedef int op_remove_attr(void *ns, void *node, const char *name); +typedef int op_rename_attr(void *ns, void *node, const char *oldname, + const char *newname); +typedef int op_stat_attr(void *ns, void *node, const char *name, + struct attr_info *buf); + +typedef int op_write_attr(void *ns, void *node, const char *name, int type, + const void *buf, size_t *len, off_t pos); +typedef int op_read_attr(void *ns, void *node, const char *name, int type, + void *buf, size_t *len, off_t pos); + +typedef int op_open_indexdir(void *ns, void **cookie); +typedef int op_close_indexdir(void *ns, void *cookie); +typedef int op_rewind_indexdir(void *ns, void *cookie); +typedef int op_read_indexdir(void *ns, void *cookie, long *num, + struct dirent *buf, size_t bufsize); +typedef int op_create_index(void *ns, const char *name, int type, int flags); +typedef int op_remove_index(void *ns, const char *name); +typedef int op_rename_index(void *ns, const char *oldname, + const char *newname); +typedef int op_stat_index(void *ns, const char *name, struct index_info *buf); + +typedef int op_open_query(void *ns, const char *query, ulong flags, + port_id port, long token, void **cookie); +typedef int op_close_query(void *ns, void *cookie); +typedef int op_read_query(void *ns, void *cookie, long *num, + struct dirent *buf, size_t bufsize); + +typedef struct vnode_ops { + op_read_vnode (*read_vnode); + op_write_vnode (*write_vnode); + op_remove_vnode (*remove_vnode); + op_secure_vnode (*secure_vnode); + op_walk (*walk); + op_access (*access); + op_create (*create); + op_mkdir (*mkdir); + op_symlink (*symlink); + op_link (*link); + op_rename (*rename); + op_unlink (*unlink); + op_rmdir (*rmdir); + op_readlink (*readlink); + op_opendir (*opendir); + op_closedir (*closedir); + op_free_cookie (*free_dircookie); + op_rewinddir (*rewinddir); + op_readdir (*readdir); + op_open (*open); + op_close (*close); + op_free_cookie (*free_cookie); + op_read (*read); + op_write (*write); + op_readv (*readv); + op_writev (*writev); + op_ioctl (*ioctl); + op_setflags (*setflags); + op_rstat (*rstat); + op_wstat (*wstat); + op_fsync (*fsync); + op_initialize (*initialize); + op_mount (*mount); + op_unmount (*unmount); + op_sync (*sync); + op_rfsstat (*rfsstat); + op_wfsstat (*wfsstat); + op_select (*select); + op_deselect (*deselect); + op_open_indexdir (*open_indexdir); + op_close_indexdir (*close_indexdir); + op_free_cookie (*free_indexdircookie); + op_rewind_indexdir (*rewind_indexdir); + op_read_indexdir (*read_indexdir); + op_create_index (*create_index); + op_remove_index (*remove_index); + op_rename_index (*rename_index); + op_stat_index (*stat_index); + op_open_attrdir (*open_attrdir); + op_close_attrdir (*close_attrdir); + op_free_cookie (*free_attrdircookie); + op_rewind_attrdir (*rewind_attrdir); + op_read_attrdir (*read_attrdir); + op_write_attr (*write_attr); + op_read_attr (*read_attr); + op_remove_attr (*remove_attr); + op_rename_attr (*rename_attr); + op_stat_attr (*stat_attr); + op_open_query (*open_query); + op_close_query (*close_query); + op_free_cookie (*free_querycookie); + op_read_query (*read_query); +} vnode_ops; + +#ifdef __cplusplus + extern "C" { +#endif + +extern _IMPEXP_KERNEL int new_path(const char *path, char **copy); +extern _IMPEXP_KERNEL void free_path(char *p); + +extern _IMPEXP_KERNEL int notify_listener(int op, nspace_id nsid, + vnode_id vnida, vnode_id vnidb, + vnode_id vnidc, const char *name); +extern _IMPEXP_KERNEL void notify_select_event(selectsync *sync, uint32 ref); +extern _IMPEXP_KERNEL int send_notification(port_id port, long token, + ulong what, long op, nspace_id nsida, + nspace_id nsidb, vnode_id vnida, + vnode_id vnidb, vnode_id vnidc, + const char *name); +extern _IMPEXP_KERNEL int get_vnode(nspace_id nsid, vnode_id vnid, void **data); +extern _IMPEXP_KERNEL int put_vnode(nspace_id nsid, vnode_id vnid); +extern _IMPEXP_KERNEL int new_vnode(nspace_id nsid, vnode_id vnid, void *data); +extern _IMPEXP_KERNEL int remove_vnode(nspace_id nsid, vnode_id vnid); +extern _IMPEXP_KERNEL int unremove_vnode(nspace_id nsid, vnode_id vnid); +extern _IMPEXP_KERNEL int is_vnode_removed(nspace_id nsid, vnode_id vnid); + + +extern _EXPORT vnode_ops fs_entry; +extern _EXPORT int32 api_version; + +#ifdef __cplusplus + } // extern "C" +#endif + +#endif diff --git a/headers/private/userlandfs/legacy/lock.h b/headers/private/userlandfs/legacy/lock.h new file mode 100644 index 0000000000..0b70c9df40 --- /dev/null +++ b/headers/private/userlandfs/legacy/lock.h @@ -0,0 +1,55 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#ifndef _LOCK_H +#define _LOCK_H + +#include + +#include + +#ifndef _IMPEXP_KERNEL +#define _IMPEXP_KERNEL +#endif + +#ifdef __cplusplus + extern "C" { +#else + typedef struct lock lock; + typedef struct mlock mlock; +#endif + + +struct lock { + sem_id s; + long c; +}; + +struct mlock { + sem_id s; +}; + +extern _IMPEXP_KERNEL int new_lock(lock *l, const char *name); +extern _IMPEXP_KERNEL int free_lock(lock *l); + +#ifdef LOCK +#undef LOCK +#endif + +#define LOCK(l) if (atomic_add(&l.c, -1) <= 0) acquire_sem(l.s); +#define UNLOCK(l) if (atomic_add(&l.c, 1) < 0) release_sem(l.s); + +extern _IMPEXP_KERNEL int new_mlock(mlock *l, long c, const char *name); +extern _IMPEXP_KERNEL int free_mlock(mlock *l); + +#define LOCKM(l,cnt) acquire_sem_etc(l.s, cnt, 0, 0) +#define UNLOCKM(l,cnt) release_sem_etc(l.s, cnt, 0) + + +#ifdef __cplusplus + } // extern "C" +#endif + +#endif diff --git a/headers/private/userlandfs/private/AreaSupport.h b/headers/private/userlandfs/private/AreaSupport.h new file mode 100644 index 0000000000..64fe38a6ad --- /dev/null +++ b/headers/private/userlandfs/private/AreaSupport.h @@ -0,0 +1,17 @@ +// AreaSupport.h + +#ifndef USERLAND_FS_AREA_SUPPORT_H +#define USERLAND_FS_AREA_SUPPORT_H + +#include + +namespace UserlandFSUtil { + +status_t get_area_for_address(void* address, int32 size, area_id* area, + int32* offset, void** areaBaseAddress = NULL); + +} // namespace UserlandFSUtil + +using UserlandFSUtil::get_area_for_address; + +#endif // USERLAND_FS_AREA_SUPPORT_H diff --git a/headers/private/userlandfs/private/DispatcherDefs.h b/headers/private/userlandfs/private/DispatcherDefs.h new file mode 100644 index 0000000000..1694462ad8 --- /dev/null +++ b/headers/private/userlandfs/private/DispatcherDefs.h @@ -0,0 +1,21 @@ +// DispatcherDefs.h + +#ifndef USERLAND_FS_DISPATCHER_DEFS_H +#define USERLAND_FS_DISPATCHER_DEFS_H + +namespace UserlandFSUtil { + +extern const char* kUserlandFSDispatcherPortName; +extern const char* kUserlandFSDispatcherReplyPortName; + +} // namespace UserlandFSUtil + +using UserlandFSUtil::kUserlandFSDispatcherPortName; +using UserlandFSUtil::kUserlandFSDispatcherReplyPortName; + +enum { + UFS_DISPATCHER_CONNECT = 'cnct', + UFS_DISPATCHER_CONNECT_ACK = 'cack', +}; + +#endif // USERLAND_FS_DISPATCHER_DEFS_H diff --git a/headers/private/userlandfs/private/Port.h b/headers/private/userlandfs/private/Port.h new file mode 100644 index 0000000000..c72099b25f --- /dev/null +++ b/headers/private/userlandfs/private/Port.h @@ -0,0 +1,58 @@ +// Port.h + +#ifndef USERLAND_FS_PORT_H +#define USERLAND_FS_PORT_H + +#include + +namespace UserlandFSUtil { + +struct PortInfo { +}; + +class Port { +public: + struct Info { + port_id owner_port; + port_id client_port; + int32 size; + }; + +public: + Port(int32 size); + Port(const Info* info); + ~Port(); + + void Close(); + + status_t InitCheck() const; + + const Info* GetInfo() const; + + void* GetBuffer() const; + int32 GetCapacity() const; + + void* GetMessage() const; + int32 GetMessageSize() const; + + status_t Send(int32 size); + status_t SendAndReceive(int32 size); + status_t Receive(bigtime_t timeout = -1); + +private: + friend class KernelDebug; + + Info fInfo; + uint8* fBuffer; + int32 fCapacity; + int32 fMessageSize; + status_t fInitStatus; + bool fOwner; +}; + +} // namespace UserlandFSUtil + +using UserlandFSUtil::PortInfo; +using UserlandFSUtil::Port; + +#endif // USERLAND_FS_PORT_H diff --git a/headers/private/userlandfs/private/Request.h b/headers/private/userlandfs/private/Request.h new file mode 100644 index 0000000000..3b58a8bdbf --- /dev/null +++ b/headers/private/userlandfs/private/Request.h @@ -0,0 +1,84 @@ +// Request.h + +#ifndef USERLAND_FS_REQUEST_H +#define USERLAND_FS_REQUEST_H + +#include + +// address info flags +enum { + ADDRESS_NOT_NULL = 0x01, + ADDRESS_IS_STRING = 0x02, +}; + +namespace UserlandFSUtil { + +class RequestAllocator; + +// Address +class Address { +public: + Address(); + + void* GetData() const { return fRelocated; } + int32 GetSize() const { return fSize; } + +//private: + void SetTo(area_id area, int32 offset, int32 size); + void SetRelocatedAddress(void* address) + { fRelocated = address; } + + area_id GetArea() const { return fUnrelocated.area; } + int32 GetOffset() const + { return fUnrelocated.offset; } + +private: + friend class RequestAllocator; + + struct Unrelocated { + area_id area; + int32 offset; + }; + + union { + Unrelocated fUnrelocated; + void* fRelocated; + }; + int32 fSize; +}; + +// AddressInfo +struct AddressInfo { + Address *address; + uint32 flags; + int32 max_size; +}; + +// Request +class Request { +public: + Request(uint32 type); + + uint32 GetType() const; + + status_t Check() const; + status_t GetAddressInfos(AddressInfo* infos, + int32* count); + +private: + uint32 fType; +}; + +// implemented in Requests.cpp +bool is_kernel_request(uint32 type); +bool is_userland_request(uint32 type); + +} // namespace UserlandFSUtil + +using UserlandFSUtil::Address; +using UserlandFSUtil::AddressInfo; +using UserlandFSUtil::Request; +using UserlandFSUtil::is_kernel_request; +using UserlandFSUtil::is_userland_request; + +#endif // USERLAND_FS_REQUEST_H diff --git a/headers/private/userlandfs/private/RequestAllocator.h b/headers/private/userlandfs/private/RequestAllocator.h new file mode 100644 index 0000000000..7714d24974 --- /dev/null +++ b/headers/private/userlandfs/private/RequestAllocator.h @@ -0,0 +1,89 @@ +// RequestAllocator.h + +#ifndef USERLAND_FS_REQUEST_ALLOCATOR_H +#define USERLAND_FS_REQUEST_ALLOCATOR_H + +#include + +#include + +#include "Debug.h" +#include "Requests.h" + +namespace UserlandFSUtil { + +class Port; + +// RequestAllocator +class RequestAllocator { +public: + RequestAllocator(Port* port); + ~RequestAllocator(); + + status_t Init(Port* port); + void Uninit(); + + status_t Error() const; + + void FinishDeferredInit(); + + status_t AllocateRequest(int32 size); + status_t ReadRequest(); + + Request* GetRequest() const; + int32 GetRequestSize() const; + + status_t AllocateAddress(Address& address, int32 size, + int32 align, void** data, + bool deferredInit = false); + status_t AllocateData(Address& address, const void* data, + int32 size, int32 align, + bool deferredInit = false); + status_t AllocateString(Address& address, + const char* data, + bool deferredInit = false); +// status_t SetAddress(Address& address, void* data, +// int32 size = 0); + +private: + struct DeferredInitInfo { + Address* target; + uint8* data; // only if in port buffer + area_id area; // only if in area, otherwise -1 + int32 offset; + int32 size; + bool inPortBuffer; + }; + + status_t fError; + Port* fPort; + Request* fRequest; + int32 fRequestSize; + area_id fAllocatedAreas[MAX_REQUEST_ADDRESS_COUNT]; + int32 fAllocatedAreaCount; + DeferredInitInfo fDeferredInitInfos[MAX_REQUEST_ADDRESS_COUNT]; + int32 fDeferredInitInfoCount; + bool fRequestInPortBuffer; +}; + +// AllocateRequest +// Should be a member, but we don't have member templates on PPC. +// TODO: Actually we seem to have. Check! +template +status_t +AllocateRequest(RequestAllocator& allocator, SpecificRequest** request) +{ + if (!request) + RETURN_ERROR(B_BAD_VALUE); + status_t error = allocator.AllocateRequest(sizeof(SpecificRequest)); + if (error == B_OK) + *request = new(allocator.GetRequest()) SpecificRequest; + return error; +} + +} // namespace UserlandFSUtil + +using UserlandFSUtil::RequestAllocator; +using UserlandFSUtil::AllocateRequest; + +#endif // USERLAND_FS_REQUEST_ALLOCATOR_H diff --git a/headers/private/userlandfs/private/RequestHandler.h b/headers/private/userlandfs/private/RequestHandler.h new file mode 100644 index 0000000000..14715b08e3 --- /dev/null +++ b/headers/private/userlandfs/private/RequestHandler.h @@ -0,0 +1,35 @@ +// RequestHandler.h + +#ifndef USERLAND_FS_REQUEST_HANDLER_H +#define USERLAND_FS_REQUEST_HANDLER_H + +#include + +namespace UserlandFSUtil { + +class Request; +class RequestPort; + +class RequestHandler { +public: + RequestHandler(); + virtual ~RequestHandler(); + + void SetPort(RequestPort* port); + + bool IsDone() const; + + virtual status_t HandleRequest(Request* request) = 0; + +protected: + RequestPort* fPort; + bool fDone; +}; + +} // namespace UserlandFSUtil + +using UserlandFSUtil::Request; +using UserlandFSUtil::RequestHandler; +using UserlandFSUtil::RequestPort; + +#endif // USERLAND_FS_REQUEST_HANDLER_H diff --git a/headers/private/userlandfs/private/RequestPort.h b/headers/private/userlandfs/private/RequestPort.h new file mode 100644 index 0000000000..3bbe299085 --- /dev/null +++ b/headers/private/userlandfs/private/RequestPort.h @@ -0,0 +1,73 @@ +// RequestPort.h + +#ifndef USERLAND_FS_REQUEST_PORT_H +#define USERLAND_FS_REQUEST_PORT_H + +#include "Port.h" +#include "RequestAllocator.h" + +namespace UserlandFSUtil { + +class RequestHandler; + +// RequestPort +class RequestPort { +public: + RequestPort(int32 size); + RequestPort(const Port::Info* info); + ~RequestPort(); + + void Close(); + + status_t InitCheck() const; + + Port* GetPort(); + const Port::Info* GetPortInfo() const; + + status_t SendRequest(RequestAllocator* allocator); + status_t SendRequest(RequestAllocator* allocator, + RequestHandler* handler, + Request** reply = NULL, + bigtime_t timeout = -1); + status_t ReceiveRequest(Request** request, + bigtime_t timeout = -1); + status_t HandleRequests(RequestHandler* handler, + Request** reply = NULL, + bigtime_t timeout = -1); + + void ReleaseRequest(Request* request); + +private: + void _PopAllocator(); + +private: + friend class KernelDebug; + struct AllocatorNode; + + Port fPort; + AllocatorNode* fCurrentAllocatorNode; +}; + +// RequestReleaser +class RequestReleaser { +public: + inline RequestReleaser(RequestPort* port, Request* request) + : fPort(port), fRequest(request) {} + + inline ~RequestReleaser() + { + if (fPort && fRequest) + fPort->ReleaseRequest(fRequest); + } + +private: + RequestPort* fPort; + Request* fRequest; +}; + +} // namespace UserlandFSUtil + +using UserlandFSUtil::RequestPort; +using UserlandFSUtil::RequestReleaser; + +#endif // USERLAND_FS_REQUEST_PORT_H diff --git a/headers/private/userlandfs/private/Requests.h b/headers/private/userlandfs/private/Requests.h new file mode 100644 index 0000000000..63bdde328a --- /dev/null +++ b/headers/private/userlandfs/private/Requests.h @@ -0,0 +1,1892 @@ +// Requests.h + +#ifndef USERLAND_FS_REQUESTS_H +#define USERLAND_FS_REQUESTS_H + +#include +#include +#include + +#include "Compatibility.h" +#include "Request.h" + +enum { + MAX_REQUEST_ADDRESS_COUNT = 4, +}; + +// request types +enum { + // kernel -> userland requests + + // administrative + UFS_DISCONNECT_REQUEST = 0, + FS_CONNECT_REQUEST, + FS_CONNECT_REPLY, + // FS + MOUNT_VOLUME_REQUEST, + MOUNT_VOLUME_REPLY, + UNMOUNT_VOLUME_REQUEST, + UNMOUNT_VOLUME_REPLY, + INITIALIZE_VOLUME_REQUEST, + INITIALIZE_VOLUME_REPLY, + SYNC_VOLUME_REQUEST, + SYNC_VOLUME_REPLY, + READ_FS_STAT_REQUEST, + READ_FS_STAT_REPLY, + WRITE_FS_STAT_REQUEST, + WRITE_FS_STAT_REPLY, + // vnodes + READ_VNODE_REQUEST, + READ_VNODE_REPLY, + WRITE_VNODE_REQUEST, + WRITE_VNODE_REPLY, + FS_REMOVE_VNODE_REQUEST, + FS_REMOVE_VNODE_REPLY, + // nodes + FSYNC_REQUEST, + FSYNC_REPLY, + READ_STAT_REQUEST, + READ_STAT_REPLY, + WRITE_STAT_REQUEST, + WRITE_STAT_REPLY, + ACCESS_REQUEST, + ACCESS_REPLY, + // files + CREATE_REQUEST, + CREATE_REPLY, + OPEN_REQUEST, + OPEN_REPLY, + CLOSE_REQUEST, + CLOSE_REPLY, + FREE_COOKIE_REQUEST, + FREE_COOKIE_REPLY, + READ_REQUEST, + READ_REPLY, + WRITE_REQUEST, + WRITE_REPLY, + IOCTL_REQUEST, + IOCTL_REPLY, + SET_FLAGS_REQUEST, + SET_FLAGS_REPLY, + SELECT_REQUEST, + SELECT_REPLY, + DESELECT_REQUEST, + DESELECT_REPLY, + // hard links / symlinks + LINK_REQUEST, + LINK_REPLY, + SYMLINK_REQUEST, + SYMLINK_REPLY, + UNLINK_REQUEST, + UNLINK_REPLY, + READ_LINK_REQUEST, + READ_LINK_REPLY, + RENAME_REQUEST, + RENAME_REPLY, + // directories + MKDIR_REQUEST, + MKDIR_REPLY, + RMDIR_REQUEST, + RMDIR_REPLY, + OPEN_DIR_REQUEST, + OPEN_DIR_REPLY, + CLOSE_DIR_REQUEST, + CLOSE_DIR_REPLY, + FREE_DIR_COOKIE_REQUEST, + FREE_DIR_COOKIE_REPLY, + READ_DIR_REQUEST, + READ_DIR_REPLY, + REWIND_DIR_REQUEST, + REWIND_DIR_REPLY, + WALK_REQUEST, + WALK_REPLY, + // attributes + OPEN_ATTR_DIR_REQUEST, + OPEN_ATTR_DIR_REPLY, + CLOSE_ATTR_DIR_REQUEST, + CLOSE_ATTR_DIR_REPLY, + FREE_ATTR_DIR_COOKIE_REQUEST, + FREE_ATTR_DIR_COOKIE_REPLY, + READ_ATTR_DIR_REQUEST, + READ_ATTR_DIR_REPLY, + REWIND_ATTR_DIR_REQUEST, + REWIND_ATTR_DIR_REPLY, + READ_ATTR_REQUEST, + READ_ATTR_REPLY, + WRITE_ATTR_REQUEST, + WRITE_ATTR_REPLY, + REMOVE_ATTR_REQUEST, + REMOVE_ATTR_REPLY, + RENAME_ATTR_REQUEST, + RENAME_ATTR_REPLY, + STAT_ATTR_REQUEST, + STAT_ATTR_REPLY, + // indices + OPEN_INDEX_DIR_REQUEST, + OPEN_INDEX_DIR_REPLY, + CLOSE_INDEX_DIR_REQUEST, + CLOSE_INDEX_DIR_REPLY, + FREE_INDEX_DIR_COOKIE_REQUEST, + FREE_INDEX_DIR_COOKIE_REPLY, + READ_INDEX_DIR_REQUEST, + READ_INDEX_DIR_REPLY, + REWIND_INDEX_DIR_REQUEST, + REWIND_INDEX_DIR_REPLY, + CREATE_INDEX_REQUEST, + CREATE_INDEX_REPLY, + REMOVE_INDEX_REQUEST, + REMOVE_INDEX_REPLY, + RENAME_INDEX_REQUEST, + RENAME_INDEX_REPLY, + STAT_INDEX_REQUEST, + STAT_INDEX_REPLY, + // queries + OPEN_QUERY_REQUEST, + OPEN_QUERY_REPLY, + CLOSE_QUERY_REQUEST, + CLOSE_QUERY_REPLY, + FREE_QUERY_COOKIE_REQUEST, + FREE_QUERY_COOKIE_REPLY, + READ_QUERY_REQUEST, + READ_QUERY_REPLY, + + // userland -> kernel requests + // notifications + NOTIFY_LISTENER_REQUEST, + NOTIFY_LISTENER_REPLY, + NOTIFY_SELECT_EVENT_REQUEST, + NOTIFY_SELECT_EVENT_REPLY, + SEND_NOTIFICATION_REQUEST, + SEND_NOTIFICATION_REPLY, + // vnodes + GET_VNODE_REQUEST, + GET_VNODE_REPLY, + PUT_VNODE_REQUEST, + PUT_VNODE_REPLY, + NEW_VNODE_REQUEST, + NEW_VNODE_REPLY, + REMOVE_VNODE_REQUEST, + REMOVE_VNODE_REPLY, + UNREMOVE_VNODE_REQUEST, + UNREMOVE_VNODE_REPLY, + IS_VNODE_REMOVED_REQUEST, + IS_VNODE_REMOVED_REPLY, + + // general reply + RECEIPT_ACK_REPLY, + + // invalid request ID (e.g. for request handlers) + NO_REQUEST, +}; + +namespace UserlandFSUtil { + +// ReplyRequest +class ReplyRequest : public Request { +public: + ReplyRequest(uint32 type) : Request(type) {} + + status_t error; +}; + +// #pragma mark - +// #pragma mark ----- kernel requests ----- + +// VolumeRequest +class VolumeRequest : public Request { +public: + VolumeRequest(uint32 type) : Request(type) {} + + void* volume; +}; + +// NodeRequest +class NodeRequest : public VolumeRequest { +public: + NodeRequest(uint32 type) : VolumeRequest(type) {} + + void* node; +}; + +// FileRequest +class FileRequest : public NodeRequest { +public: + FileRequest(uint32 type) : NodeRequest(type) {} + + void* fileCookie; +}; + +// DirRequest +class DirRequest : public NodeRequest { +public: + DirRequest(uint32 type) : NodeRequest(type) {} + + void* dirCookie; +}; + +// AttrDirRequest +class AttrDirRequest : public NodeRequest { +public: + AttrDirRequest(uint32 type) : NodeRequest(type) {} + + void* attrDirCookie; +}; + +// IndexDirRequest +class IndexDirRequest : public VolumeRequest { +public: + IndexDirRequest(uint32 type) : VolumeRequest(type) {} + + void* indexDirCookie; +}; + +// #pragma mark - +// #pragma mark ----- administrative ----- + +// UFSDisconnectRequest +class UFSDisconnectRequest : public Request { +public: + UFSDisconnectRequest() : Request(UFS_DISCONNECT_REQUEST) {} +}; + +// FSConnectRequest +class FSConnectRequest : public Request { +public: + FSConnectRequest() : Request(FS_CONNECT_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address fsName; +}; + +// FSConnectReply +class FSConnectReply : public ReplyRequest { +public: + FSConnectReply() : ReplyRequest(FS_CONNECT_REPLY) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address portInfos; + int32 portInfoCount; +}; + +// #pragma mark - +// #pragma mark ----- FS ----- + +// MountVolumeRequest +class MountVolumeRequest : public Request { +public: + MountVolumeRequest() : Request(MOUNT_VOLUME_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + nspace_id nsid; + Address cwd; // current working dir of the mount() caller + Address device; + ulong flags; + Address parameters; +}; + +// MountVolumeReply +class MountVolumeReply : public ReplyRequest { +public: + MountVolumeReply() : ReplyRequest(MOUNT_VOLUME_REPLY) {} + + vnode_id rootID; + void* volume; +}; + +// UnmountVolumeRequest +class UnmountVolumeRequest : public VolumeRequest { +public: + UnmountVolumeRequest() : VolumeRequest(UNMOUNT_VOLUME_REQUEST) {} +}; + +// UnmountVolumeReply +class UnmountVolumeReply : public ReplyRequest { +public: + UnmountVolumeReply() : ReplyRequest(UNMOUNT_VOLUME_REPLY) {} +}; + +// InitializeVolumeRequest +class InitializeVolumeRequest : public Request { +public: + InitializeVolumeRequest() : Request(INITIALIZE_VOLUME_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address device; + Address parameters; +}; + +// InitializeVolumeReply +class InitializeVolumeReply : public ReplyRequest { +public: + InitializeVolumeReply() : ReplyRequest(INITIALIZE_VOLUME_REPLY) {} +}; + +// SyncVolumeRequest +class SyncVolumeRequest : public VolumeRequest { +public: + SyncVolumeRequest() : VolumeRequest(SYNC_VOLUME_REQUEST) {} +}; + +// SyncVolumeReply +class SyncVolumeReply : public ReplyRequest { +public: + SyncVolumeReply() : ReplyRequest(SYNC_VOLUME_REPLY) {} +}; + +// ReadFSStatRequest +class ReadFSStatRequest : public VolumeRequest { +public: + ReadFSStatRequest() : VolumeRequest(READ_FS_STAT_REQUEST) {} +}; + +// ReadFSStatReply +class ReadFSStatReply : public ReplyRequest { +public: + ReadFSStatReply() : ReplyRequest(READ_FS_STAT_REPLY) {} + + fs_info info; +}; + +// WriteFSStatRequest +class WriteFSStatRequest : public VolumeRequest { +public: + WriteFSStatRequest() : VolumeRequest(WRITE_FS_STAT_REQUEST) {} + + fs_info info; + long mask; +}; + +// WriteFSStatReply +class WriteFSStatReply : public ReplyRequest { +public: + WriteFSStatReply() : ReplyRequest(WRITE_FS_STAT_REPLY) {} +}; + +// #pragma mark - +// #pragma mark ----- vnodes ----- + +// ReadVNodeRequest +class ReadVNodeRequest : public VolumeRequest { +public: + ReadVNodeRequest() : VolumeRequest(READ_VNODE_REQUEST) {} + + vnode_id vnid; + bool reenter; +}; + +// ReadVNodeReply +class ReadVNodeReply : public ReplyRequest { +public: + ReadVNodeReply() : ReplyRequest(READ_VNODE_REPLY) {} + + void* node; +}; + +// WriteVNodeRequest +class WriteVNodeRequest : public NodeRequest { +public: + WriteVNodeRequest() : NodeRequest(WRITE_VNODE_REQUEST) {} + + bool reenter; +}; + +// WriteVNodeReply +class WriteVNodeReply : public ReplyRequest { +public: + WriteVNodeReply() : ReplyRequest(WRITE_VNODE_REPLY) {} +}; + +// FSRemoveVNodeRequest +class FSRemoveVNodeRequest : public NodeRequest { +public: + FSRemoveVNodeRequest() : NodeRequest(FS_REMOVE_VNODE_REQUEST) {} + + bool reenter; +}; + +// FSRemoveVNodeReply +class FSRemoveVNodeReply : public ReplyRequest { +public: + FSRemoveVNodeReply() : ReplyRequest(FS_REMOVE_VNODE_REPLY) {} +}; + +// #pragma mark - +// #pragma mark ----- nodes ----- + +// FSyncRequest +class FSyncRequest : public NodeRequest { +public: + FSyncRequest() : NodeRequest(FSYNC_REQUEST) {} +}; + +// FSyncReply +class FSyncReply : public ReplyRequest { +public: + FSyncReply() : ReplyRequest(FSYNC_REPLY) {} +}; + +// ReadStatRequest +class ReadStatRequest : public NodeRequest { +public: + ReadStatRequest() : NodeRequest(READ_STAT_REQUEST) {} +}; + +// ReadStatReply +class ReadStatReply : public ReplyRequest { +public: + ReadStatReply() : ReplyRequest(READ_STAT_REPLY) {} + + struct stat st; +}; + +// WriteStatRequest +class WriteStatRequest : public NodeRequest { +public: + WriteStatRequest() : NodeRequest(WRITE_STAT_REQUEST) {} + + struct stat st; + long mask; +}; + +// WriteStatReply +class WriteStatReply : public ReplyRequest { +public: + WriteStatReply() : ReplyRequest(WRITE_STAT_REPLY) {} +}; + +// AccessRequest +class AccessRequest : public NodeRequest { +public: + AccessRequest() : NodeRequest(ACCESS_REQUEST) {} + + int mode; +}; + +// AccessReply +class AccessReply : public ReplyRequest { +public: + AccessReply() : ReplyRequest(ACCESS_REPLY) {} +}; + +// #pragma mark - +// #pragma mark ----- files ----- + +// CreateRequest +class CreateRequest : public NodeRequest { +public: + CreateRequest() : NodeRequest(CREATE_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address name; + int openMode; + int mode; +}; + +// CreateReply +class CreateReply : public ReplyRequest { +public: + CreateReply() : ReplyRequest(CREATE_REPLY) {} + + vnode_id vnid; + void* fileCookie; +}; + +// OpenRequest +class OpenRequest : public NodeRequest { +public: + OpenRequest() : NodeRequest(OPEN_REQUEST) {} + + int openMode; +}; + +// OpenReply +class OpenReply : public ReplyRequest { +public: + OpenReply() : ReplyRequest(OPEN_REPLY) {} + + void* fileCookie; +}; + +// CloseRequest +class CloseRequest : public FileRequest { +public: + CloseRequest() : FileRequest(CLOSE_REQUEST) {} +}; + +// CloseReply +class CloseReply : public ReplyRequest { +public: + CloseReply() : ReplyRequest(CLOSE_REPLY) {} +}; + +// FreeCookieRequest +class FreeCookieRequest : public FileRequest { +public: + FreeCookieRequest() : FileRequest(FREE_COOKIE_REQUEST) {} +}; + +// FreeCookieReply +class FreeCookieReply : public ReplyRequest { +public: + FreeCookieReply() : ReplyRequest(FREE_COOKIE_REPLY) {} +}; + +// ReadRequest +class ReadRequest : public FileRequest { +public: + ReadRequest() : FileRequest(READ_REQUEST) {} + + off_t pos; + size_t size; +}; + +// ReadReply +class ReadReply : public ReplyRequest { +public: + ReadReply() : ReplyRequest(READ_REPLY) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address buffer; + size_t bytesRead; +}; + +// WriteRequest +class WriteRequest : public FileRequest { +public: + WriteRequest() : FileRequest(WRITE_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address buffer; + off_t pos; +}; + +// WriteReply +class WriteReply : public ReplyRequest { +public: + WriteReply() : ReplyRequest(WRITE_REPLY) {} + + size_t bytesWritten; +}; + +// IOCtlRequest +class IOCtlRequest : public FileRequest { +public: + IOCtlRequest() : FileRequest(IOCTL_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + int command; + void* bufferParameter; + size_t lenParameter; + bool isBuffer; // if false, just pass bufferParameter + // otherwise use buffer + Address buffer; + int32 writeSize; // ignored unless isBuffer -- then + // it indicates the size of the buffer to allocate +}; + +// IOCtlReply +class IOCtlReply : public ReplyRequest { +public: + IOCtlReply() : ReplyRequest(IOCTL_REPLY) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + status_t ioctlError; // we need a special handling since error + // may be a part of the client FS protocol + Address buffer; +}; + +// SetFlagsRequest +class SetFlagsRequest : public FileRequest { +public: + SetFlagsRequest() : FileRequest(SET_FLAGS_REQUEST) {} + + int flags; +}; + +// SetFlagsReply +class SetFlagsReply : public ReplyRequest { +public: + SetFlagsReply() : ReplyRequest(SET_FLAGS_REPLY) {} +}; + +// SelectRequest +class SelectRequest : public FileRequest { +public: + SelectRequest() : FileRequest(SELECT_REQUEST) {} + + uint8 event; + uint32 ref; + selectsync* sync; +}; + +// SelectReply +class SelectReply : public ReplyRequest { +public: + SelectReply() : ReplyRequest(SELECT_REPLY) {} +}; + +// DeselectRequest +class DeselectRequest : public FileRequest { +public: + DeselectRequest() : FileRequest(DESELECT_REQUEST) {} + + uint8 event; + selectsync* sync; +}; + +// DeselectReply +class DeselectReply : public ReplyRequest { +public: + DeselectReply() : ReplyRequest(DESELECT_REPLY) {} +}; + +// #pragma mark - +// #pragma mark ----- hard links / symlinks ----- + +// LinkRequest +class LinkRequest : public NodeRequest { +public: + LinkRequest() : NodeRequest(LINK_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address name; + void* target; +}; + +// LinkReply +class LinkReply : public ReplyRequest { +public: + LinkReply() : ReplyRequest(LINK_REPLY) {} +}; + +// UnlinkRequest +class UnlinkRequest : public NodeRequest { +public: + UnlinkRequest() : NodeRequest(UNLINK_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address name; +}; + +// UnlinkReply +class UnlinkReply : public ReplyRequest { +public: + UnlinkReply() : ReplyRequest(UNLINK_REPLY) {} +}; + +// SymlinkRequest +class SymlinkRequest : public NodeRequest { +public: + SymlinkRequest() : NodeRequest(SYMLINK_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address name; + Address target; +}; + +// SymlinkReply +class SymlinkReply : public ReplyRequest { +public: + SymlinkReply() : ReplyRequest(SYMLINK_REPLY) {} +}; + +// ReadLinkRequest +class ReadLinkRequest : public NodeRequest { +public: + ReadLinkRequest() : NodeRequest(READ_LINK_REQUEST) {} + + size_t size; +}; + +// ReadLinkReply +class ReadLinkReply : public ReplyRequest { +public: + ReadLinkReply() : ReplyRequest(READ_LINK_REPLY) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address buffer; + size_t bytesRead; +}; + +// RenameRequest +class RenameRequest : public VolumeRequest { +public: + RenameRequest() : VolumeRequest(RENAME_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + void* oldDir; + Address oldName; + void* newDir; + Address newName; +}; + +// RenameReply +class RenameReply : public ReplyRequest { +public: + RenameReply() : ReplyRequest(RENAME_REPLY) {} +}; + +// #pragma mark - +// #pragma mark ----- directories ----- + +// MkDirRequest +class MkDirRequest : public NodeRequest { +public: + MkDirRequest() : NodeRequest(MKDIR_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address name; + int mode; +}; + +// MkDirReply +class MkDirReply : public ReplyRequest { +public: + MkDirReply() : ReplyRequest(MKDIR_REPLY) {} +}; + +// RmDirRequest +class RmDirRequest : public NodeRequest { +public: + RmDirRequest() : NodeRequest(RMDIR_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address name; +}; + +// RmDirReply +class RmDirReply : public ReplyRequest { +public: + RmDirReply() : ReplyRequest(RMDIR_REPLY) {} +}; + +// OpenDirRequest +class OpenDirRequest : public NodeRequest { +public: + OpenDirRequest() : NodeRequest(OPEN_DIR_REQUEST) {} +}; + +// OpenDirReply +class OpenDirReply : public ReplyRequest { +public: + OpenDirReply() : ReplyRequest(OPEN_DIR_REPLY) {} + + void* dirCookie; +}; + +// CloseDirRequest +class CloseDirRequest : public DirRequest { +public: + CloseDirRequest() : DirRequest(CLOSE_DIR_REQUEST) {} +}; + +// CloseDirReply +class CloseDirReply : public ReplyRequest { +public: + CloseDirReply() : ReplyRequest(CLOSE_DIR_REPLY) {} +}; + +// FreeDirCookieRequest +class FreeDirCookieRequest : public DirRequest { +public: + FreeDirCookieRequest() : DirRequest(FREE_DIR_COOKIE_REQUEST) {} +}; + +// FreeDirCookieReply +class FreeDirCookieReply : public ReplyRequest { +public: + FreeDirCookieReply() : ReplyRequest(FREE_DIR_COOKIE_REPLY) {} +}; + +// ReadDirRequest +class ReadDirRequest : public DirRequest { +public: + ReadDirRequest() : DirRequest(READ_DIR_REQUEST) {} + + size_t bufferSize; + int32 count; +}; + +// ReadDirReply +class ReadDirReply : public ReplyRequest { +public: + ReadDirReply() : ReplyRequest(READ_DIR_REPLY) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + int32 count; + Address buffer; +}; + +// RewindDirRequest +class RewindDirRequest : public DirRequest { +public: + RewindDirRequest() : DirRequest(REWIND_DIR_REQUEST) {} +}; + +// RewindDirReply +class RewindDirReply : public ReplyRequest { +public: + RewindDirReply() : ReplyRequest(REWIND_DIR_REPLY) {} +}; + +// WalkRequest +class WalkRequest : public NodeRequest { +public: + WalkRequest() : NodeRequest(WALK_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address entryName; + bool traverseLink; +}; + +// WalkReply +class WalkReply : public ReplyRequest { +public: + WalkReply() : ReplyRequest(WALK_REPLY) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address resolvedPath; + vnode_id vnid; +}; + +// #pragma mark - +// #pragma mark ----- attributes ----- + +// OpenAttrDirRequest +class OpenAttrDirRequest : public NodeRequest { +public: + OpenAttrDirRequest() : NodeRequest(OPEN_ATTR_DIR_REQUEST) {} +}; + +// OpenAttrDirReply +class OpenAttrDirReply : public ReplyRequest { +public: + OpenAttrDirReply() : ReplyRequest(OPEN_ATTR_DIR_REPLY) {} + + void* attrDirCookie; +}; + +// CloseAttrDirRequest +class CloseAttrDirRequest : public AttrDirRequest { +public: + CloseAttrDirRequest() : AttrDirRequest(CLOSE_ATTR_DIR_REQUEST) {} +}; + +// CloseAttrDirReply +class CloseAttrDirReply : public ReplyRequest { +public: + CloseAttrDirReply() : ReplyRequest(CLOSE_ATTR_DIR_REPLY) {} +}; + +// FreeAttrDirCookieRequest +class FreeAttrDirCookieRequest : public AttrDirRequest { +public: + FreeAttrDirCookieRequest() : AttrDirRequest(FREE_ATTR_DIR_COOKIE_REQUEST) {} +}; + +// FreeAttrDirCookieReply +class FreeAttrDirCookieReply : public ReplyRequest { +public: + FreeAttrDirCookieReply() : ReplyRequest(FREE_ATTR_DIR_COOKIE_REPLY) {} +}; + +// ReadAttrDirRequest +class ReadAttrDirRequest : public AttrDirRequest { +public: + ReadAttrDirRequest() : AttrDirRequest(READ_ATTR_DIR_REQUEST) {} + + size_t bufferSize; + int32 count; +}; + +// ReadAttrDirReply +class ReadAttrDirReply : public ReplyRequest { +public: + ReadAttrDirReply() : ReplyRequest(READ_ATTR_DIR_REPLY) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + int32 count; + Address buffer; +}; + +// RewindAttrDirRequest +class RewindAttrDirRequest : public AttrDirRequest { +public: + RewindAttrDirRequest() : AttrDirRequest(REWIND_ATTR_DIR_REQUEST) {} +}; + +// RewindAttrDirReply +class RewindAttrDirReply : public ReplyRequest { +public: + RewindAttrDirReply() : ReplyRequest(REWIND_ATTR_DIR_REPLY) {} +}; + +// ReadAttrRequest +class ReadAttrRequest : public NodeRequest { +public: + ReadAttrRequest() : NodeRequest(READ_ATTR_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address name; + int type; + off_t pos; + size_t size; +}; + +// ReadAttrReply +class ReadAttrReply : public ReplyRequest { +public: + ReadAttrReply() : ReplyRequest(READ_ATTR_REPLY) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address buffer; + size_t bytesRead; +}; + +// WriteAttrRequest +class WriteAttrRequest : public NodeRequest { +public: + WriteAttrRequest() : NodeRequest(WRITE_ATTR_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address name; + int type; + Address buffer; + off_t pos; + size_t size; +}; + +// WriteAttrReply +class WriteAttrReply : public ReplyRequest { +public: + WriteAttrReply() : ReplyRequest(WRITE_ATTR_REPLY) {} + + size_t bytesWritten; +}; + +// RemoveAttrRequest +class RemoveAttrRequest : public NodeRequest { +public: + RemoveAttrRequest() : NodeRequest(REMOVE_ATTR_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address name; +}; + +// RemoveAttrReply +class RemoveAttrReply : public ReplyRequest { +public: + RemoveAttrReply() : ReplyRequest(REMOVE_ATTR_REPLY) {} +}; + +// RenameAttrRequest +class RenameAttrRequest : public NodeRequest { +public: + RenameAttrRequest() : NodeRequest(RENAME_ATTR_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address oldName; + Address newName; +}; + +// RenameAttrReply +class RenameAttrReply : public ReplyRequest { +public: + RenameAttrReply() : ReplyRequest(RENAME_ATTR_REPLY) {} +}; + +// StatAttrRequest +class StatAttrRequest : public NodeRequest { +public: + StatAttrRequest() : NodeRequest(STAT_ATTR_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address name; +}; + +// StatAttrReply +class StatAttrReply : public ReplyRequest { +public: + StatAttrReply() : ReplyRequest(STAT_ATTR_REPLY) {} + + attr_info info; +}; + +// #pragma mark - +// #pragma mark ----- indices ----- + +// OpenIndexDirRequest +class OpenIndexDirRequest : public VolumeRequest { +public: + OpenIndexDirRequest() : VolumeRequest(OPEN_INDEX_DIR_REQUEST) {} +}; + +// OpenIndexDirReply +class OpenIndexDirReply : public ReplyRequest { +public: + OpenIndexDirReply() : ReplyRequest(OPEN_INDEX_DIR_REPLY) {} + + void* indexDirCookie; +}; + +// CloseIndexDirRequest +class CloseIndexDirRequest : public IndexDirRequest { +public: + CloseIndexDirRequest() : IndexDirRequest(CLOSE_INDEX_DIR_REQUEST) {} +}; + +// CloseIndexDirReply +class CloseIndexDirReply : public ReplyRequest { +public: + CloseIndexDirReply() : ReplyRequest(CLOSE_INDEX_DIR_REPLY) {} +}; + +// FreeIndexDirCookieRequest +class FreeIndexDirCookieRequest : public IndexDirRequest { +public: + FreeIndexDirCookieRequest() + : IndexDirRequest(FREE_INDEX_DIR_COOKIE_REQUEST) {} +}; + +// FreeIndexDirCookieReply +class FreeIndexDirCookieReply : public ReplyRequest { +public: + FreeIndexDirCookieReply() : ReplyRequest(FREE_INDEX_DIR_COOKIE_REPLY) {} +}; + +// ReadIndexDirRequest +class ReadIndexDirRequest : public IndexDirRequest { +public: + ReadIndexDirRequest() : IndexDirRequest(READ_INDEX_DIR_REQUEST) {} + + size_t bufferSize; + int32 count; +}; + +// ReadIndexDirReply +class ReadIndexDirReply : public ReplyRequest { +public: + ReadIndexDirReply() : ReplyRequest(READ_INDEX_DIR_REPLY) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + int32 count; + Address buffer; +}; + +// RewindIndexDirRequest +class RewindIndexDirRequest : public IndexDirRequest { +public: + RewindIndexDirRequest() : IndexDirRequest(REWIND_INDEX_DIR_REQUEST) {} +}; + +// RewindIndexDirReply +class RewindIndexDirReply : public ReplyRequest { +public: + RewindIndexDirReply() : ReplyRequest(REWIND_INDEX_DIR_REPLY) {} +}; + +// CreateIndexRequest +class CreateIndexRequest : public VolumeRequest { +public: + CreateIndexRequest() : VolumeRequest(CREATE_INDEX_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address name; + int type; + int flags; +}; + +// CreateIndexReply +class CreateIndexReply : public ReplyRequest { +public: + CreateIndexReply() : ReplyRequest(CREATE_INDEX_REPLY) {} +}; + +// RemoveIndexRequest +class RemoveIndexRequest : public VolumeRequest { +public: + RemoveIndexRequest() : VolumeRequest(REMOVE_INDEX_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address name; +}; + +// RemoveIndexReply +class RemoveIndexReply : public ReplyRequest { +public: + RemoveIndexReply() : ReplyRequest(REMOVE_INDEX_REPLY) {} +}; + +// RenameIndexRequest +class RenameIndexRequest : public VolumeRequest { +public: + RenameIndexRequest() : VolumeRequest(RENAME_INDEX_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address oldName; + Address newName; +}; + +// RenameIndexReply +class RenameIndexReply : public ReplyRequest { +public: + RenameIndexReply() : ReplyRequest(RENAME_INDEX_REPLY) {} +}; + +// StatIndexRequest +class StatIndexRequest : public VolumeRequest { +public: + StatIndexRequest() : VolumeRequest(STAT_INDEX_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address name; +}; + +// StatIndexReply +class StatIndexReply : public ReplyRequest { +public: + StatIndexReply() : ReplyRequest(STAT_INDEX_REPLY) {} + + index_info info; +}; + +// #pragma mark - +// #pragma mark ----- queries ----- + +// OpenQueryRequest +class OpenQueryRequest : public VolumeRequest { +public: + OpenQueryRequest() : VolumeRequest(OPEN_QUERY_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + Address queryString; + ulong flags; + port_id port; + long token; +}; + +// OpenQueryReply +class OpenQueryReply : public ReplyRequest { +public: + OpenQueryReply() : ReplyRequest(OPEN_QUERY_REPLY) {} + + void* queryCookie; +}; + +// CloseQueryRequest +class CloseQueryRequest : public VolumeRequest { +public: + CloseQueryRequest() : VolumeRequest(CLOSE_QUERY_REQUEST) {} + + void* queryCookie; +}; + +// CloseQueryReply +class CloseQueryReply : public ReplyRequest { +public: + CloseQueryReply() : ReplyRequest(CLOSE_QUERY_REPLY) {} +}; + +// FreeQueryCookieRequest +class FreeQueryCookieRequest : public VolumeRequest { +public: + FreeQueryCookieRequest() : VolumeRequest(FREE_QUERY_COOKIE_REQUEST) {} + + void* queryCookie; +}; + +// FreeQueryCookieReply +class FreeQueryCookieReply : public ReplyRequest { +public: + FreeQueryCookieReply() : ReplyRequest(FREE_QUERY_COOKIE_REPLY) {} +}; + +// ReadQueryRequest +class ReadQueryRequest : public VolumeRequest { +public: + ReadQueryRequest() : VolumeRequest(READ_QUERY_REQUEST) {} + + void* queryCookie; + size_t bufferSize; + int32 count; +}; + +// ReadQueryReply +class ReadQueryReply : public ReplyRequest { +public: + ReadQueryReply() : ReplyRequest(READ_QUERY_REPLY) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + int32 count; + Address buffer; +}; + + +// #pragma mark - +// #pragma mark ----- userland requests ----- + +// #pragma mark - +// #pragma mark ----- notifications ----- + +// NotifyListenerRequest +class NotifyListenerRequest : public Request { +public: + NotifyListenerRequest() : Request(NOTIFY_LISTENER_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + int32 operation; + nspace_id nsid; + vnode_id vnida; + vnode_id vnidb; + vnode_id vnidc; + Address name; +}; + +// NotifyListenerReply +class NotifyListenerReply : public ReplyRequest { +public: + NotifyListenerReply() : ReplyRequest(NOTIFY_LISTENER_REPLY) {} +}; + +// NotifySelectRequest +class NotifySelectEventRequest : public Request { +public: + NotifySelectEventRequest() : Request(NOTIFY_SELECT_EVENT_REQUEST) {} + + selectsync* sync; + uint32 ref; +}; + +// NotifySelectEventReply +class NotifySelectEventReply : public ReplyRequest { +public: + NotifySelectEventReply() : ReplyRequest(NOTIFY_SELECT_EVENT_REPLY) {} +}; + +// SendNotificationRequest +class SendNotificationRequest : public Request { +public: + SendNotificationRequest() : Request(SEND_NOTIFICATION_REQUEST) {} + status_t GetAddressInfos(AddressInfo* infos, int32* count); + + port_id port; + int32 token; + uint32 what; + int32 operation; + nspace_id nsida; + nspace_id nsidb; + vnode_id vnida; + vnode_id vnidb; + vnode_id vnidc; + Address name; +}; + +// SendNotificationReply +class SendNotificationReply : public ReplyRequest { +public: + SendNotificationReply() : ReplyRequest(SEND_NOTIFICATION_REPLY) {} +}; + + +// #pragma mark - +// #pragma mark ----- vnodes ----- + +// GetVNodeRequest +class GetVNodeRequest : public Request { +public: + GetVNodeRequest() : Request(GET_VNODE_REQUEST) {} + + nspace_id nsid; + vnode_id vnid; +}; + +// GetVNodeReply +class GetVNodeReply : public ReplyRequest { +public: + GetVNodeReply() : ReplyRequest(GET_VNODE_REPLY) {} + + void* node; +}; + +// PutVNodeRequest +class PutVNodeRequest : public Request { +public: + PutVNodeRequest() : Request(PUT_VNODE_REQUEST) {} + + nspace_id nsid; + vnode_id vnid; +}; + +// PutVNodeReply +class PutVNodeReply : public ReplyRequest { +public: + PutVNodeReply() : ReplyRequest(PUT_VNODE_REPLY) {} +}; + +// NewVNodeRequest +class NewVNodeRequest : public Request { +public: + NewVNodeRequest() : Request(NEW_VNODE_REQUEST) {} + + nspace_id nsid; + vnode_id vnid; + void* node; +}; + +// NewVNodeReply +class NewVNodeReply : public ReplyRequest { +public: + NewVNodeReply() : ReplyRequest(NEW_VNODE_REPLY) {} +}; + +// RemoveVNodeRequest +class RemoveVNodeRequest : public Request { +public: + RemoveVNodeRequest() : Request(REMOVE_VNODE_REQUEST) {} + + nspace_id nsid; + vnode_id vnid; +}; + +// RemoveVNodeReply +class RemoveVNodeReply : public ReplyRequest { +public: + RemoveVNodeReply() : ReplyRequest(REMOVE_VNODE_REPLY) {} +}; + +// UnremoveVNodeRequest +class UnremoveVNodeRequest : public Request { +public: + UnremoveVNodeRequest() : Request(UNREMOVE_VNODE_REQUEST) {} + + nspace_id nsid; + vnode_id vnid; +}; + +// UnremoveVNodeReply +class UnremoveVNodeReply : public ReplyRequest { +public: + UnremoveVNodeReply() : ReplyRequest(UNREMOVE_VNODE_REPLY) {} +}; + +// IsVNodeRemovedRequest +class IsVNodeRemovedRequest : public Request { +public: + IsVNodeRemovedRequest() : Request(IS_VNODE_REMOVED_REQUEST) {} + + nspace_id nsid; + vnode_id vnid; +}; + +// IsVNodeRemovedReply +class IsVNodeRemovedReply : public ReplyRequest { +public: + IsVNodeRemovedReply() : ReplyRequest(IS_VNODE_REMOVED_REPLY) {} + + int result; +}; + + +////////////////// +// General Reply + +// ReceiptAckReply +class ReceiptAckReply : public ReplyRequest { +public: + ReceiptAckReply() : ReplyRequest(RECEIPT_ACK_REPLY) {} +}; + + +/////////////////// +// Request Checks + +// do_for_request +template +static +status_t +do_for_request(Request* request, Task& task) +{ + if (!request) + return B_BAD_VALUE; + switch (request->GetType()) { + // kernel -> userland requests + // administrative + case UFS_DISCONNECT_REQUEST: + return task((UFSDisconnectRequest*)request); + case FS_CONNECT_REQUEST: + return task((FSConnectRequest*)request); + case FS_CONNECT_REPLY: + return task((FSConnectReply*)request); + // FS + case MOUNT_VOLUME_REQUEST: + return task((MountVolumeRequest*)request); + case MOUNT_VOLUME_REPLY: + return task((MountVolumeReply*)request); + case UNMOUNT_VOLUME_REQUEST: + return task((UnmountVolumeRequest*)request); + case UNMOUNT_VOLUME_REPLY: + return task((UnmountVolumeReply*)request); + case INITIALIZE_VOLUME_REQUEST: + return task((InitializeVolumeRequest*)request); + case INITIALIZE_VOLUME_REPLY: + return task((InitializeVolumeReply*)request); + case SYNC_VOLUME_REQUEST: + return task((SyncVolumeRequest*)request); + case SYNC_VOLUME_REPLY: + return task((SyncVolumeReply*)request); + case READ_FS_STAT_REQUEST: + return task((ReadFSStatRequest*)request); + case READ_FS_STAT_REPLY: + return task((ReadFSStatReply*)request); + case WRITE_FS_STAT_REQUEST: + return task((WriteFSStatRequest*)request); + case WRITE_FS_STAT_REPLY: + return task((WriteFSStatReply*)request); + // vnodes + case READ_VNODE_REQUEST: + return task((ReadVNodeRequest*)request); + case READ_VNODE_REPLY: + return task((ReadVNodeReply*)request); + case WRITE_VNODE_REQUEST: + return task((WriteVNodeRequest*)request); + case WRITE_VNODE_REPLY: + return task((WriteVNodeReply*)request); + case FS_REMOVE_VNODE_REQUEST: + return task((FSRemoveVNodeRequest*)request); + case FS_REMOVE_VNODE_REPLY: + return task((FSRemoveVNodeReply*)request); + // nodes + case FSYNC_REQUEST: + return task((FSyncRequest*)request); + case FSYNC_REPLY: + return task((FSyncReply*)request); + case READ_STAT_REQUEST: + return task((ReadStatRequest*)request); + case READ_STAT_REPLY: + return task((ReadStatReply*)request); + case WRITE_STAT_REQUEST: + return task((WriteStatRequest*)request); + case WRITE_STAT_REPLY: + return task((WriteStatReply*)request); + case ACCESS_REQUEST: + return task((AccessRequest*)request); + case ACCESS_REPLY: + return task((AccessReply*)request); + // files + case CREATE_REQUEST: + return task((CreateRequest*)request); + case CREATE_REPLY: + return task((CreateReply*)request); + case OPEN_REQUEST: + return task((OpenRequest*)request); + case OPEN_REPLY: + return task((OpenReply*)request); + case CLOSE_REQUEST: + return task((CloseRequest*)request); + case CLOSE_REPLY: + return task((CloseReply*)request); + case FREE_COOKIE_REQUEST: + return task((FreeCookieRequest*)request); + case FREE_COOKIE_REPLY: + return task((FreeCookieReply*)request); + case READ_REQUEST: + return task((ReadRequest*)request); + case READ_REPLY: + return task((ReadReply*)request); + case WRITE_REQUEST: + return task((WriteRequest*)request); + case WRITE_REPLY: + return task((WriteReply*)request); + case IOCTL_REQUEST: + return task((IOCtlRequest*)request); + case IOCTL_REPLY: + return task((IOCtlReply*)request); + case SET_FLAGS_REQUEST: + return task((SetFlagsRequest*)request); + case SET_FLAGS_REPLY: + return task((SetFlagsReply*)request); + case SELECT_REQUEST: + return task((SelectRequest*)request); + case SELECT_REPLY: + return task((SelectReply*)request); + case DESELECT_REQUEST: + return task((DeselectRequest*)request); + case DESELECT_REPLY: + return task((DeselectReply*)request); + // hard links / symlinks + case LINK_REQUEST: + return task((LinkRequest*)request); + case LINK_REPLY: + return task((LinkReply*)request); + case UNLINK_REQUEST: + return task((UnlinkRequest*)request); + case UNLINK_REPLY: + return task((UnlinkReply*)request); + case SYMLINK_REQUEST: + return task((SymlinkRequest*)request); + case SYMLINK_REPLY: + return task((SymlinkReply*)request); + case READ_LINK_REQUEST: + return task((ReadLinkRequest*)request); + case READ_LINK_REPLY: + return task((ReadLinkReply*)request); + case RENAME_REQUEST: + return task((RenameRequest*)request); + case RENAME_REPLY: + return task((RenameReply*)request); + // directories + case MKDIR_REQUEST: + return task((MkDirRequest*)request); + case MKDIR_REPLY: + return task((MkDirReply*)request); + case RMDIR_REQUEST: + return task((RmDirRequest*)request); + case RMDIR_REPLY: + return task((RmDirReply*)request); + case OPEN_DIR_REQUEST: + return task((OpenDirRequest*)request); + case OPEN_DIR_REPLY: + return task((OpenDirReply*)request); + case CLOSE_DIR_REQUEST: + return task((CloseDirRequest*)request); + case CLOSE_DIR_REPLY: + return task((CloseDirReply*)request); + case FREE_DIR_COOKIE_REQUEST: + return task((FreeDirCookieRequest*)request); + case FREE_DIR_COOKIE_REPLY: + return task((FreeDirCookieReply*)request); + case READ_DIR_REQUEST: + return task((ReadDirRequest*)request); + case READ_DIR_REPLY: + return task((ReadDirReply*)request); + case REWIND_DIR_REQUEST: + return task((RewindDirRequest*)request); + case REWIND_DIR_REPLY: + return task((RewindDirReply*)request); + case WALK_REQUEST: + return task((WalkRequest*)request); + case WALK_REPLY: + return task((WalkReply*)request); + // attributes + case OPEN_ATTR_DIR_REQUEST: + return task((OpenAttrDirRequest*)request); + case OPEN_ATTR_DIR_REPLY: + return task((OpenAttrDirReply*)request); + case CLOSE_ATTR_DIR_REQUEST: + return task((CloseAttrDirRequest*)request); + case CLOSE_ATTR_DIR_REPLY: + return task((CloseAttrDirReply*)request); + case FREE_ATTR_DIR_COOKIE_REQUEST: + return task((FreeAttrDirCookieRequest*)request); + case FREE_ATTR_DIR_COOKIE_REPLY: + return task((FreeAttrDirCookieReply*)request); + case READ_ATTR_DIR_REQUEST: + return task((ReadAttrDirRequest*)request); + case READ_ATTR_DIR_REPLY: + return task((ReadAttrDirReply*)request); + case REWIND_ATTR_DIR_REQUEST: + return task((RewindAttrDirRequest*)request); + case REWIND_ATTR_DIR_REPLY: + return task((RewindAttrDirReply*)request); + case READ_ATTR_REQUEST: + return task((ReadAttrRequest*)request); + case READ_ATTR_REPLY: + return task((ReadAttrReply*)request); + case WRITE_ATTR_REQUEST: + return task((WriteAttrRequest*)request); + case WRITE_ATTR_REPLY: + return task((WriteAttrReply*)request); + case REMOVE_ATTR_REQUEST: + return task((RemoveAttrRequest*)request); + case REMOVE_ATTR_REPLY: + return task((RemoveAttrReply*)request); + case RENAME_ATTR_REQUEST: + return task((RenameAttrRequest*)request); + case RENAME_ATTR_REPLY: + return task((RenameAttrReply*)request); + case STAT_ATTR_REQUEST: + return task((StatAttrRequest*)request); + case STAT_ATTR_REPLY: + return task((StatAttrReply*)request); + // indices + case OPEN_INDEX_DIR_REQUEST: + return task((OpenIndexDirRequest*)request); + case OPEN_INDEX_DIR_REPLY: + return task((OpenIndexDirReply*)request); + case CLOSE_INDEX_DIR_REQUEST: + return task((CloseIndexDirRequest*)request); + case CLOSE_INDEX_DIR_REPLY: + return task((CloseIndexDirReply*)request); + case FREE_INDEX_DIR_COOKIE_REQUEST: + return task((FreeIndexDirCookieRequest*)request); + case FREE_INDEX_DIR_COOKIE_REPLY: + return task((FreeIndexDirCookieReply*)request); + case READ_INDEX_DIR_REQUEST: + return task((ReadIndexDirRequest*)request); + case READ_INDEX_DIR_REPLY: + return task((ReadIndexDirReply*)request); + case REWIND_INDEX_DIR_REQUEST: + return task((RewindIndexDirRequest*)request); + case REWIND_INDEX_DIR_REPLY: + return task((RewindIndexDirReply*)request); + case CREATE_INDEX_REQUEST: + return task((CreateIndexRequest*)request); + case CREATE_INDEX_REPLY: + return task((CreateIndexReply*)request); + case REMOVE_INDEX_REQUEST: + return task((RemoveIndexRequest*)request); + case REMOVE_INDEX_REPLY: + return task((RemoveIndexReply*)request); + case RENAME_INDEX_REQUEST: + return task((RenameIndexRequest*)request); + case RENAME_INDEX_REPLY: + return task((RenameIndexReply*)request); + case STAT_INDEX_REQUEST: + return task((StatIndexRequest*)request); + case STAT_INDEX_REPLY: + return task((StatIndexReply*)request); + // queries + case OPEN_QUERY_REQUEST: + return task((OpenQueryRequest*)request); + case OPEN_QUERY_REPLY: + return task((OpenQueryReply*)request); + case CLOSE_QUERY_REQUEST: + return task((CloseQueryRequest*)request); + case CLOSE_QUERY_REPLY: + return task((CloseQueryReply*)request); + case FREE_QUERY_COOKIE_REQUEST: + return task((FreeQueryCookieRequest*)request); + case FREE_QUERY_COOKIE_REPLY: + return task((FreeQueryCookieReply*)request); + case READ_QUERY_REQUEST: + return task((ReadQueryRequest*)request); + case READ_QUERY_REPLY: + return task((ReadQueryReply*)request); + + // userland -> kernel requests + // notifications + case NOTIFY_LISTENER_REQUEST: + return task((NotifyListenerRequest*)request); + case NOTIFY_LISTENER_REPLY: + return task((NotifyListenerReply*)request); + case NOTIFY_SELECT_EVENT_REQUEST: + return task((NotifySelectEventRequest*)request); + case NOTIFY_SELECT_EVENT_REPLY: + return task((NotifySelectEventReply*)request); + case SEND_NOTIFICATION_REQUEST: + return task((SendNotificationRequest*)request); + case SEND_NOTIFICATION_REPLY: + return task((SendNotificationReply*)request); + // vnodes + case GET_VNODE_REQUEST: + return task((GetVNodeRequest*)request); + case GET_VNODE_REPLY: + return task((GetVNodeReply*)request); + case PUT_VNODE_REQUEST: + return task((PutVNodeRequest*)request); + case PUT_VNODE_REPLY: + return task((PutVNodeReply*)request); + case NEW_VNODE_REQUEST: + return task((NewVNodeRequest*)request); + case NEW_VNODE_REPLY: + return task((NewVNodeReply*)request); + case REMOVE_VNODE_REQUEST: + return task((RemoveVNodeRequest*)request); + case REMOVE_VNODE_REPLY: + return task((RemoveVNodeReply*)request); + case UNREMOVE_VNODE_REQUEST: + return task((UnremoveVNodeRequest*)request); + case UNREMOVE_VNODE_REPLY: + return task((UnremoveVNodeReply*)request); + case IS_VNODE_REMOVED_REQUEST: + return task((IsVNodeRemovedRequest*)request); + case IS_VNODE_REMOVED_REPLY: + return task((IsVNodeRemovedReply*)request); + // general reply + case RECEIPT_ACK_REPLY: + return task((ReceiptAckReply*)request); + default: + return B_BAD_DATA; + } + return task(request); +} + +status_t get_request_address_infos(Request* request, AddressInfo* infos, + int32* count); +status_t check_request(Request* request); +status_t relocate_request(Request* request, int32 requestBufferSize, + area_id* areas, int32* count); + +} // namespace UserlandFSUtil + +using UserlandFSUtil::ReplyRequest; +using UserlandFSUtil::VolumeRequest; +using UserlandFSUtil::NodeRequest; +using UserlandFSUtil::FileRequest; +using UserlandFSUtil::DirRequest; +using UserlandFSUtil::AttrDirRequest; +using UserlandFSUtil::IndexDirRequest; + +// kernel -> userland requests +// administrative +using UserlandFSUtil::UFSDisconnectRequest; +using UserlandFSUtil::FSConnectRequest; +using UserlandFSUtil::FSConnectReply; +// FS +using UserlandFSUtil::MountVolumeRequest; +using UserlandFSUtil::MountVolumeReply; +using UserlandFSUtil::UnmountVolumeRequest; +using UserlandFSUtil::UnmountVolumeReply; +using UserlandFSUtil::InitializeVolumeRequest; +using UserlandFSUtil::InitializeVolumeReply; +using UserlandFSUtil::SyncVolumeRequest; +using UserlandFSUtil::SyncVolumeReply; +using UserlandFSUtil::ReadFSStatRequest; +using UserlandFSUtil::ReadFSStatReply; +using UserlandFSUtil::WriteFSStatRequest; +using UserlandFSUtil::WriteFSStatReply; +// vnodes +using UserlandFSUtil::ReadVNodeRequest; +using UserlandFSUtil::ReadVNodeReply; +using UserlandFSUtil::WriteVNodeRequest; +using UserlandFSUtil::WriteVNodeReply; +using UserlandFSUtil::FSRemoveVNodeRequest; +using UserlandFSUtil::FSRemoveVNodeReply; +// nodes +using UserlandFSUtil::FSyncRequest; +using UserlandFSUtil::FSyncReply; +using UserlandFSUtil::ReadStatRequest; +using UserlandFSUtil::ReadStatReply; +using UserlandFSUtil::WriteStatRequest; +using UserlandFSUtil::WriteStatReply; +using UserlandFSUtil::AccessRequest; +using UserlandFSUtil::AccessReply; +// files +using UserlandFSUtil::CreateRequest; +using UserlandFSUtil::CreateReply; +using UserlandFSUtil::OpenRequest; +using UserlandFSUtil::OpenReply; +using UserlandFSUtil::CloseRequest; +using UserlandFSUtil::CloseReply; +using UserlandFSUtil::FreeCookieRequest; +using UserlandFSUtil::FreeCookieReply; +using UserlandFSUtil::ReadRequest; +using UserlandFSUtil::ReadReply; +using UserlandFSUtil::WriteRequest; +using UserlandFSUtil::WriteReply; +using UserlandFSUtil::IOCtlRequest; +using UserlandFSUtil::IOCtlReply; +using UserlandFSUtil::SetFlagsRequest; +using UserlandFSUtil::SetFlagsReply; +using UserlandFSUtil::SelectRequest; +using UserlandFSUtil::SelectReply; +using UserlandFSUtil::DeselectRequest; +using UserlandFSUtil::DeselectReply; +// hard links / symlinks +using UserlandFSUtil::LinkRequest; +using UserlandFSUtil::LinkReply; +using UserlandFSUtil::UnlinkRequest; +using UserlandFSUtil::UnlinkReply; +using UserlandFSUtil::SymlinkRequest; +using UserlandFSUtil::SymlinkReply; +using UserlandFSUtil::ReadLinkRequest; +using UserlandFSUtil::ReadLinkReply; +using UserlandFSUtil::RenameRequest; +using UserlandFSUtil::RenameReply; +// directories +using UserlandFSUtil::MkDirRequest; +using UserlandFSUtil::MkDirReply; +using UserlandFSUtil::RmDirRequest; +using UserlandFSUtil::RmDirReply; +using UserlandFSUtil::OpenDirRequest; +using UserlandFSUtil::OpenDirReply; +using UserlandFSUtil::CloseDirRequest; +using UserlandFSUtil::CloseDirReply; +using UserlandFSUtil::FreeDirCookieRequest; +using UserlandFSUtil::FreeDirCookieReply; +using UserlandFSUtil::ReadDirRequest; +using UserlandFSUtil::ReadDirReply; +using UserlandFSUtil::RewindDirRequest; +using UserlandFSUtil::RewindDirReply; +using UserlandFSUtil::WalkRequest; +using UserlandFSUtil::WalkReply; +// attributes +using UserlandFSUtil::OpenAttrDirRequest; +using UserlandFSUtil::OpenAttrDirReply; +using UserlandFSUtil::CloseAttrDirRequest; +using UserlandFSUtil::CloseAttrDirReply; +using UserlandFSUtil::FreeAttrDirCookieRequest; +using UserlandFSUtil::FreeAttrDirCookieReply; +using UserlandFSUtil::ReadAttrDirRequest; +using UserlandFSUtil::ReadAttrDirReply; +using UserlandFSUtil::RewindAttrDirRequest; +using UserlandFSUtil::RewindAttrDirReply; +using UserlandFSUtil::ReadAttrRequest; +using UserlandFSUtil::ReadAttrReply; +using UserlandFSUtil::WriteAttrRequest; +using UserlandFSUtil::WriteAttrReply; +using UserlandFSUtil::RemoveAttrRequest; +using UserlandFSUtil::RemoveAttrReply; +using UserlandFSUtil::RenameAttrRequest; +using UserlandFSUtil::RenameAttrReply; +using UserlandFSUtil::StatAttrRequest; +using UserlandFSUtil::StatAttrReply; +// indices +using UserlandFSUtil::OpenIndexDirRequest; +using UserlandFSUtil::OpenIndexDirReply; +using UserlandFSUtil::CloseIndexDirRequest; +using UserlandFSUtil::CloseIndexDirReply; +using UserlandFSUtil::FreeIndexDirCookieRequest; +using UserlandFSUtil::FreeIndexDirCookieReply; +using UserlandFSUtil::ReadIndexDirRequest; +using UserlandFSUtil::ReadIndexDirReply; +using UserlandFSUtil::RewindIndexDirRequest; +using UserlandFSUtil::RewindIndexDirReply; +using UserlandFSUtil::CreateIndexRequest; +using UserlandFSUtil::CreateIndexReply; +using UserlandFSUtil::RemoveIndexRequest; +using UserlandFSUtil::RemoveIndexReply; +using UserlandFSUtil::RenameIndexRequest; +using UserlandFSUtil::RenameIndexReply; +using UserlandFSUtil::StatIndexRequest; +using UserlandFSUtil::StatIndexReply; +// queries +using UserlandFSUtil::OpenQueryRequest; +using UserlandFSUtil::OpenQueryReply; +using UserlandFSUtil::CloseQueryRequest; +using UserlandFSUtil::CloseQueryReply; +using UserlandFSUtil::FreeQueryCookieRequest; +using UserlandFSUtil::FreeQueryCookieReply; +using UserlandFSUtil::ReadQueryRequest; +using UserlandFSUtil::ReadQueryReply; + +// userland -> kernel requests +// notifications +using UserlandFSUtil::NotifyListenerRequest; +using UserlandFSUtil::NotifyListenerReply; +using UserlandFSUtil::NotifySelectEventRequest; +using UserlandFSUtil::NotifySelectEventReply; +using UserlandFSUtil::SendNotificationRequest; +using UserlandFSUtil::SendNotificationReply; +// vnodes +using UserlandFSUtil::GetVNodeRequest; +using UserlandFSUtil::GetVNodeReply; +using UserlandFSUtil::PutVNodeRequest; +using UserlandFSUtil::PutVNodeReply; +using UserlandFSUtil::NewVNodeRequest; +using UserlandFSUtil::NewVNodeReply; +using UserlandFSUtil::RemoveVNodeRequest; +using UserlandFSUtil::RemoveVNodeReply; +using UserlandFSUtil::UnremoveVNodeRequest; +using UserlandFSUtil::UnremoveVNodeReply; +using UserlandFSUtil::IsVNodeRemovedRequest; +using UserlandFSUtil::IsVNodeRemovedReply; +// general reply +using UserlandFSUtil::ReceiptAckReply; + +using UserlandFSUtil::do_for_request; +using UserlandFSUtil::get_request_address_infos; +using UserlandFSUtil::check_request; +using UserlandFSUtil::relocate_request; + +#endif // USERLAND_FS_REQUESTS_H diff --git a/headers/private/userlandfs/private/SingleReplyRequestHandler.h b/headers/private/userlandfs/private/SingleReplyRequestHandler.h new file mode 100644 index 0000000000..5ae0b33a2e --- /dev/null +++ b/headers/private/userlandfs/private/SingleReplyRequestHandler.h @@ -0,0 +1,26 @@ +// SingleReplyRequestHandler.h + +#ifndef USERLAND_FS_SINGLE_REPLY_REQUEST_HANDLER_H +#define USERLAND_FS_SINGLE_REPLY_REQUEST_HANDLER_H + +#include "RequestHandler.h" + +namespace UserlandFSUtil { + +class SingleReplyRequestHandler : public RequestHandler { +public: + SingleReplyRequestHandler(); + SingleReplyRequestHandler(uint32 expectedReply); + + virtual status_t HandleRequest(Request* request); + +private: + bool fAcceptAnyRequest; + uint32 fExpectedReply; +}; + +} // namespace UserlandFSUtil + +using UserlandFSUtil::SingleReplyRequestHandler; + +#endif // USERLAND_FS_SINGLE_REPLY_REQUEST_HANDLER_H diff --git a/headers/private/userlandfs/private/userlandfs_ioctl.h b/headers/private/userlandfs/private/userlandfs_ioctl.h new file mode 100644 index 0000000000..e7867850ad --- /dev/null +++ b/headers/private/userlandfs/private/userlandfs_ioctl.h @@ -0,0 +1,55 @@ +// userlandfs_ioctl.h + +#ifndef USERLAND_FS_IOCTL_H +#define USERLAND_FS_IOCTL_H + +#include + +// the ioctl command we use for tunnelling our commands +enum { + USERLANDFS_IOCTL = B_DEVICE_OP_CODES_END + 666, +}; + +// the supported commands +enum { + USERLAND_IOCTL_PUT_ALL_PENDING_VNODES = 1, +}; + +// the length of the magic we use +enum { + USERLAND_IOCTL_MAGIC_LENGTH = 20, +}; + +// the version of the ioctl protocol +enum { + USERLAND_IOCTL_CURRENT_VERSION = 1, +}; + +// the errors +enum { + USERLAND_IOCTL_STILL_CONNECTED = B_ERRORS_END + 666, + USERLAND_IOCTL_VNODE_COUNTING_DISABLED, + USERLAND_IOCTL_OPEN_FILES, + USERLAND_IOCTL_OPEN_DIRECTORIES, + USERLAND_IOCTL_OPEN_ATTRIBUTE_DIRECTORIES, + USERLAND_IOCTL_OPEN_INDEX_DIRECTORIES, + USERLAND_IOCTL_OPEN_QUERIES, +}; + +namespace UserlandFSUtil { + +struct userlandfs_ioctl { + char magic[USERLAND_IOCTL_MAGIC_LENGTH]; + int version; + int command; + status_t error; +}; + +extern const char kUserlandFSIOCtlMagic[USERLAND_IOCTL_MAGIC_LENGTH]; + +} // namespace UserlandFSUtil + +using UserlandFSUtil::userlandfs_ioctl; +using UserlandFSUtil::kUserlandFSIOCtlMagic; + +#endif // USERLAND_FS_IOCTL_H diff --git a/headers/private/userlandfs/shared/AutoDeleter.h b/headers/private/userlandfs/shared/AutoDeleter.h new file mode 100644 index 0000000000..e858c6f0a9 --- /dev/null +++ b/headers/private/userlandfs/shared/AutoDeleter.h @@ -0,0 +1,136 @@ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2004, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: AutoDeleter.h +// Author(s): Ingo Weinhold (bonefish@users.sf.net) +// Description: Scope-based automatic deletion of objects/arrays. +// ObjectDeleter - deletes an object +// ArrayDeleter - deletes an array +// MemoryDeleter - free()s malloc()ed memory +//------------------------------------------------------------------------------ + +#ifndef _AUTO_DELETER_H +#define _AUTO_DELETER_H + +#include + +namespace BPrivate { + +// AutoDeleter + +template +class AutoDeleter { +public: + inline AutoDeleter() + : fObject(NULL) + { + } + + inline AutoDeleter(C *object) + : fObject(object) + { + } + + inline ~AutoDeleter() + { + fDelete(fObject); + } + + inline void SetTo(C *object) + { + fDelete(fObject); + fObject = object; + } + + inline C *Detach() + { + C *object = fObject; + fObject = NULL; + return object; + } + +private: + C *fObject; + Delete fDelete; +}; + + +// ObjectDeleter + +template +struct ObjectDelete +{ + inline void operator()(C *object) + { + delete object; + } +}; + +template +struct ObjectDeleter : AutoDeleter > +{ + ObjectDeleter() : AutoDeleter >() {} + ObjectDeleter(C *object) : AutoDeleter >(object) {} +}; + + +// ArrayDeleter + +template +struct ArrayDelete +{ + inline void operator()(C *array) + { + delete[] array; + } +}; + +template +struct ArrayDeleter : AutoDeleter > +{ + ArrayDeleter() : AutoDeleter >() {} + ArrayDeleter(C *array) : AutoDeleter >(array) {} +}; + + +// MemoryDeleter + +struct MemoryDelete +{ + inline void operator()(void *memory) + { + free(memory); + } +}; + +struct MemoryDeleter : AutoDeleter +{ + MemoryDeleter() : AutoDeleter() {} + MemoryDeleter(void *memory) : AutoDeleter(memory) {} +}; + +} // namespace BPrivate + +using BPrivate::ObjectDeleter; +using BPrivate::ArrayDeleter; +using BPrivate::MemoryDeleter; + +#endif // _AUTO_DELETER_H diff --git a/headers/private/userlandfs/shared/AutoLocker.h b/headers/private/userlandfs/shared/AutoLocker.h new file mode 100644 index 0000000000..c622978839 --- /dev/null +++ b/headers/private/userlandfs/shared/AutoLocker.h @@ -0,0 +1,164 @@ +// AutoLocker.h +// +// Copyright (c) 2004, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#ifndef AUTO_LOCKER_H +#define AUTO_LOCKER_H + +#include + +// locking + +// AutoLockerStandardLocking +template +class AutoLockerStandardLocking { +public: + inline bool Lock(Lockable *lockable) + { + return lockable->Lock(); + } + + inline void Unlock(Lockable *lockable) + { + lockable->Unlock(); + } +}; + +// AutoLockerReadLocking +template +class AutoLockerReadLocking { +public: + inline bool Lock(Lockable *lockable) + { + return lockable->ReadLock(); + } + + inline void Unlock(Lockable *lockable) + { + lockable->ReadUnlock(); + } +}; + +// AutoLockerWriteLocking +template +class AutoLockerWriteLocking { +public: + inline bool Lock(Lockable *lockable) + { + return lockable->WriteLock(); + } + + inline void Unlock(Lockable *lockable) + { + lockable->WriteUnlock(); + } +}; + +// AutoLocker +template > +class AutoLocker { +private: + typedef AutoLocker ThisClass; +public: + inline AutoLocker(Lockable *lockable, bool alreadyLocked = false) + : fLockable(lockable), + fLocked(fLockable && alreadyLocked) + { + if (!fLocked) + _Lock(); + } + + inline AutoLocker(Lockable &lockable, bool alreadyLocked = false) + : fLockable(&lockable), + fLocked(fLockable && alreadyLocked) + { + if (!fLocked) + _Lock(); + } + + inline ~AutoLocker() + { + Unlock(); + } + + inline void SetTo(Lockable *lockable, bool alreadyLocked) + { + Unlock(); + fLockable = lockable; + fLocked = alreadyLocked; + if (!fLocked) + _Lock(); + } + + inline void SetTo(Lockable &lockable, bool alreadyLocked) + { + SetTo(&lockable, alreadyLocked); + } + + inline void Unset() + { + Unlock(); + } + + inline AutoLocker &operator=(Lockable *lockable) + { + SetTo(lockable); + return *this; + } + + inline AutoLocker &operator=(Lockable &lockable) + { + SetTo(&lockable); + return *this; + } + + inline bool IsLocked() const { return fLocked; } + + inline void Unlock() + { + if (fLockable && fLocked) { + fLocking.Unlock(fLockable); + fLocked = false; + } + } + + inline operator bool() const { return fLocked; } + +private: + inline void _Lock() + { + if (fLockable) + fLocked = fLocking.Lock(fLockable); + } + +private: + Lockable *fLockable; + bool fLocked; + Locking fLocking; +}; + +#endif // AUTO_LOCKER_H diff --git a/headers/private/userlandfs/shared/Compatibility.h b/headers/private/userlandfs/shared/Compatibility.h new file mode 100644 index 0000000000..4059d0d356 --- /dev/null +++ b/headers/private/userlandfs/shared/Compatibility.h @@ -0,0 +1,51 @@ +// Compatibility.h +// +// Copyright (c) 2004, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#ifndef USERLAND_FS_COMPATIBILITY_H +#define USERLAND_FS_COMPATIBILITY_H + +#include + +#if B_BEOS_VERSION <= B_BEOS_VERSION_5 +//# define B_BAD_DATA -2147483632L +#else +# ifndef closesocket +# define closesocket(fd) close(fd) +# endif +#endif + +// a Haiku definition +#ifndef B_BUFFER_OVERFLOW +# define B_BUFFER_OVERFLOW EOVERFLOW +#endif + +// make Zeta R5 source compatible without needing to link against libzeta.so +#ifdef find_directory +# undef find_directory +#endif + +#endif // USERLAND_FS_COMPATIBILITY_H diff --git a/headers/private/userlandfs/shared/DLList.h b/headers/private/userlandfs/shared/DLList.h new file mode 100644 index 0000000000..a4469b47a1 --- /dev/null +++ b/headers/private/userlandfs/shared/DLList.h @@ -0,0 +1,384 @@ +// DLList.h +// +// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#ifndef DL_LIST_H +#define DL_LIST_H + +#include + +namespace UserlandFSUtil { + +// DLListLink +template +class DLListLink { +public: + DLListLink() : previous(NULL), next(NULL) {} + ~DLListLink() {} + + Element *previous; + Element *next; +}; + +// DLListLinkImpl +template +class DLListLinkImpl { +private: + typedef DLListLink MyLink; + +public: + DLListLinkImpl() : fDLListLink() {} + ~DLListLinkImpl() {} + + MyLink *GetDLListLink() { return &fDLListLink; } + const MyLink *GetDLListLink() const { return &fDLListLink; } + +private: + MyLink fDLListLink; +}; + +// DLListStandardGetLink +template +class DLListStandardGetLink { +private: + typedef DLListLink Link; + +public: + inline Link *operator()(Element *element) const + { + return element->GetDLListLink(); + } + + inline const Link *operator()(const Element *element) const + { + return element->GetDLListLink(); + } +}; + +// for convenience +#define DL_LIST_TEMPLATE_LIST template +#define DL_LIST_CLASS_NAME DLList + +// DLList +template > +class DLList { +private: + typedef DLList List; + typedef DLListLink Link; + +public: + class Iterator { + public: + Iterator(List *list) + : fList(list), + fCurrent(NULL), + fNext(fList->GetFirst()) + { + } + + Iterator(const Iterator &other) + { + *this = other; + } + + bool HasNext() const + { + return fNext; + } + + Element *Next() + { + fCurrent = fNext; + if (fNext) + fNext = fList->GetNext(fNext); + return fCurrent; + } + + Element *Remove() + { + Element *element = fCurrent; + if (fCurrent) { + fList->Remove(fCurrent); + fCurrent = NULL; + } + return element; + } + + Iterator &operator=(const Iterator &other) + { + fList = other.fList; + fCurrent = other.fCurrent; + fNext = other.fNext; + return *this; + } + + private: + List *fList; + Element *fCurrent; + Element *fNext; + }; + + class ConstIterator { + public: + ConstIterator(const List *list) + : fList(list), + fNext(list->GetFirst()) + { + } + + ConstIterator(const ConstIterator &other) + { + *this = other; + } + + bool HasNext() const + { + return fNext; + } + + Element *Next() + { + Element *element = fNext; + if (fNext) + fNext = fList->GetNext(fNext); + return element; + } + + ConstIterator &operator=(const ConstIterator &other) + { + fList = other.fList; + fNext = other.fNext; + return *this; + } + + private: + const List *fList; + Element *fNext; + }; + +public: + DLList() : fFirst(NULL), fLast(NULL) {} + DLList(const GetLink &getLink) + : fFirst(NULL), fLast(NULL), fGetLink(getLink) {} + ~DLList() {} + + inline bool IsEmpty() const { return (fFirst == NULL); } + + inline void Insert(Element *element, bool back = true); + inline void Remove(Element *element); + + inline void Swap(Element *a, Element *b); + + inline void MoveFrom(DL_LIST_CLASS_NAME *fromList); + + inline void RemoveAll(); + + inline Element *GetFirst() const { return fFirst; } + inline Element *GetLast() const { return fLast; } + + inline Element *GetHead() const { return fFirst; } + inline Element *GetTail() const { return fLast; } + + inline Element *GetPrevious(Element *element) const; + inline Element *GetNext(Element *element) const; + + inline int32 Size() const; + // O(n)! + + inline Iterator GetIterator() { return Iterator(this); } + inline ConstIterator GetIterator() const { return ConstIterator(this); } + +private: + Element *fFirst; + Element *fLast; + GetLink fGetLink; +}; + +} // namespace UserlandFSUtil + +using UserlandFSUtil::DLList; +using UserlandFSUtil::DLListLink; +using UserlandFSUtil::DLListLinkImpl; + + +// inline methods + +// Insert +DL_LIST_TEMPLATE_LIST +void +DL_LIST_CLASS_NAME::Insert(Element *element, bool back) +{ + if (element) { + if (back) { + // append + Link *elLink = fGetLink(element); + elLink->previous = fLast; + elLink->next = NULL; + if (fLast) + fGetLink(fLast)->next = element; + else + fFirst = element; + fLast = element; + } else { + // prepend + Link *elLink = fGetLink(element); + elLink->previous = NULL; + elLink->next = fFirst; + if (fFirst) + fGetLink(fFirst)->previous = element; + else + fLast = element; + fFirst = element; + } + } +} + +// Remove +DL_LIST_TEMPLATE_LIST +void +DL_LIST_CLASS_NAME::Remove(Element *element) +{ + if (element) { + Link *elLink = fGetLink(element); + if (elLink->previous) + fGetLink(elLink->previous)->next = elLink->next; + else + fFirst = elLink->next; + if (elLink->next) + fGetLink(elLink->next)->previous = elLink->previous; + else + fLast = elLink->previous; + elLink->previous = NULL; + elLink->next = NULL; + } +} + +// Swap +DL_LIST_TEMPLATE_LIST +void +DL_LIST_CLASS_NAME::Swap(Element *a, Element *b) +{ + if (a && b && a != b) { + Link *aLink = fGetLink(a); + Link *bLink = fGetLink(b); + Element *aPrev = aLink->previous; + Element *bPrev = bLink->previous; + Element *aNext = aLink->next; + Element *bNext = bLink->next; + // place a + if (bPrev) + fGetLink(bPrev)->next = a; + else + fFirst = a; + if (bNext) + fGetLink(bNext)->previous = a; + else + fLast = a; + aLink->previous = bPrev; + aLink->next = bNext; + // place b + if (aPrev) + fGetLink(aPrev)->next = b; + else + fFirst = b; + if (aNext) + fGetLink(aNext)->previous = b; + else + fLast = b; + bLink->previous = aPrev; + bLink->next = aNext; + } +} + +// MoveFrom +DL_LIST_TEMPLATE_LIST +void +DL_LIST_CLASS_NAME::MoveFrom(DL_LIST_CLASS_NAME *fromList) +{ + if (fromList && fromList->fFirst) { + if (fFirst) { + fGetLink(fLast)->next = fromList->fFirst; + fGetLink(fFirst)->previous = fLast; + fLast = fromList->fLast; + } else { + fFirst = fromList->fFirst; + fLast = fromList->fLast; + } + fromList->fFirst = NULL; + fromList->fLast = NULL; + } +} + +// RemoveAll +DL_LIST_TEMPLATE_LIST +void +DL_LIST_CLASS_NAME::RemoveAll() +{ + Element *element = fFirst; + while (element) { + Link *elLink = fGetLink(element); + element = elLink->next; + elLink->previous = NULL; + elLink->next = NULL; + } + fFirst = NULL; + fLast = NULL; +} + +// GetPrevious +DL_LIST_TEMPLATE_LIST +Element * +DL_LIST_CLASS_NAME::GetPrevious(Element *element) const +{ + Element *result = NULL; + if (element) + result = fGetLink(element)->previous; + return result; +} + +// GetNext +DL_LIST_TEMPLATE_LIST +Element * +DL_LIST_CLASS_NAME::GetNext(Element *element) const +{ + Element *result = NULL; + if (element) + result = fGetLink(element)->next; + return result; +} + +// Size +DL_LIST_TEMPLATE_LIST +int32 +DL_LIST_CLASS_NAME::Size() const +{ + int32 count = 0; + for (Element* element = GetFirst(); element; element = GetNext(element)) + count++; + return count; +} + +#endif // DL_LIST_H diff --git a/headers/private/userlandfs/shared/Debug.h b/headers/private/userlandfs/shared/Debug.h new file mode 100644 index 0000000000..595fec4e22 --- /dev/null +++ b/headers/private/userlandfs/shared/Debug.h @@ -0,0 +1,140 @@ +#ifndef DEBUG_H +#define DEBUG_H +/* Debug - debug stuff +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ +#include + +#if !USER +# include +#endif +#include +#include + +// define all macros we work with -- undefined macros are set to defaults +#ifndef USER +# define USER 0 +#endif +#ifndef DEBUG +# define DEBUG 0 +#endif +#if !DEBUG +# undef DEBUG_PRINT +# define DEBUG_PRINT 0 +#endif +#ifndef DEBUG_PRINT +# define DEBUG_PRINT 0 +#endif +#ifndef DEBUG_APP +# define DEBUG_APP "debug" +#endif +#ifndef DEBUG_PRINT_FILE +# define DEBUG_PRINT_FILE "/var/log/" DEBUG_APP ".log" +#endif + +// define the debug output function +#if USER +# include +# if DEBUG_PRINT +# define __out dbg_printf +# else +# define __out printf +# endif +#else +# include +# include +# if DEBUG_PRINT +# define __out dbg_printf +# else +# define __out dprintf +# endif +#endif + +// define the PANIC() macro +#ifndef PANIC +# if USER +# define PANIC(str) debugger(str) +# else +# define PANIC(str) panic(str) +# endif +#endif + +// functions exported by this module +status_t init_debugging(); +status_t exit_debugging(); +void dbg_printf_begin(); +void dbg_printf_end(); +#if DEBUG_PRINT + void dbg_printf(const char *format,...); +#else + static inline void dbg_printf(const char *,...) {} +#endif + +// Short overview over the debug output macros: +// PRINT() +// is for general messages that very unlikely should appear in a release build +// FATAL() +// this is for fatal messages, when something has really gone wrong +// INFORM() +// general information, as disk size, etc. +// REPORT_ERROR(status_t) +// prints out error information +// RETURN_ERROR(status_t) +// calls REPORT_ERROR() and return the value +// D() +// the statements in D() are only included if DEBUG is defined + +#if __MWERKS__ +# define __FUNCTION__ "" +#endif + +#define DEBUG_THREAD find_thread(NULL) +#define DEBUG_CONTEXT(x) { dbg_printf_begin(); __out(DEBUG_APP " [%Ld: %5ld] ", system_time(), DEBUG_THREAD); x; dbg_printf_end(); } +#define DEBUG_CONTEXT_FUNCTION(prefix, x) { dbg_printf_begin(); __out(DEBUG_APP " [%Ld: %5ld] %s()" prefix, system_time(), DEBUG_THREAD, __FUNCTION__); x; dbg_printf_end(); } +#define DEBUG_CONTEXT_LINE(x) { dbg_printf_begin(); __out(DEBUG_APP " [%Ld: %5ld] %s():%d: ", system_time(), DEBUG_THREAD, __FUNCTION__, __LINE__); x; dbg_printf_end(); } + +#define TPRINT(x) DEBUG_CONTEXT( __out x ) +#define TREPORT_ERROR(status) DEBUG_CONTEXT_LINE( __out("%s\n", strerror(status)) ) +#define TRETURN_ERROR(err) { status_t _status = err; if (_status < B_OK) TREPORT_ERROR(_status); return _status;} +#define TSET_ERROR(var, err) { status_t _status = err; if (_status < B_OK) TREPORT_ERROR(_status); var = _status; } +#define TFUNCTION(x) DEBUG_CONTEXT_FUNCTION( ": ", __out x ) +#define TFUNCTION_START() DEBUG_CONTEXT_FUNCTION( "\n", ) +#define TFUNCTION_END() DEBUG_CONTEXT_FUNCTION( " done\n", ) + +#if DEBUG + #define PRINT(x) TPRINT(x) + #define REPORT_ERROR(status) TREPORT_ERROR(status) + #define RETURN_ERROR(err) TRETURN_ERROR(err) + #define SET_ERROR(var, err) TSET_ERROR(var, err) + #define FATAL(x) DEBUG_CONTEXT( __out x ) + #define ERROR(x) DEBUG_CONTEXT( __out x ) + #define WARN(x) DEBUG_CONTEXT( __out x ) + #define INFORM(x) DEBUG_CONTEXT( __out x ) + #define FUNCTION(x) TFUNCTION(x) + #define FUNCTION_START() TFUNCTION_START() + #define FUNCTION_END() TFUNCTION_END() + #define DARG(x) x + #define D(x) {x;}; +#else + #define PRINT(x) ; + #define REPORT_ERROR(status) ; + #define RETURN_ERROR(status) return status; + #define SET_ERROR(var, err) var = err; + #define FATAL(x) DEBUG_CONTEXT( __out x ) + #define ERROR(x) DEBUG_CONTEXT( __out x ) + #define WARN(x) DEBUG_CONTEXT( __out x ) + #define INFORM(x) DEBUG_CONTEXT( __out x ) + #define FUNCTION(x) ; + #define FUNCTION_START() ; + #define FUNCTION_END() ; + #define DARG(x) + #define D(x) ; +#endif + +#ifndef TOUCH +#define TOUCH(var) (void)var +#endif + +#endif /* DEBUG_H */ diff --git a/headers/private/userlandfs/shared/DriverSettings.h b/headers/private/userlandfs/shared/DriverSettings.h new file mode 100644 index 0000000000..eb61b69b3e --- /dev/null +++ b/headers/private/userlandfs/shared/DriverSettings.h @@ -0,0 +1,130 @@ +// DriverSettings.h + +#ifndef USERLAND_FS_DRIVER_SETTINGS_H +#define USERLAND_FS_DRIVER_SETTINGS_H + +struct driver_parameter; +struct driver_settings; + +namespace UserlandFSUtil { + +class DriverParameter; +class DriverParameterContainer; + +// DriverParameterIterator +class DriverParameterIterator { +public: + DriverParameterIterator(); + DriverParameterIterator( + const DriverParameterIterator& other); + ~DriverParameterIterator(); + + bool HasNext() const; + bool GetNext(DriverParameter* parameter); + + DriverParameterIterator& operator=( + const DriverParameterIterator& other); + +private: + friend class DriverParameterContainer; + class Delegate; + + DriverParameterIterator(Delegate* delegate); + void _SetTo(Delegate* delegate, bool addReference); + + Delegate* fDelegate; +}; + +// DriverParameterContainer +class DriverParameterContainer { +public: + DriverParameterContainer(); + virtual ~DriverParameterContainer(); + + int32 CountParameters() const; + const driver_parameter* GetParameters() const; + bool GetParameterAt(int32 index, + DriverParameter* parameter) const; + bool FindParameter(const char* name, + DriverParameter* parameter) const; + + DriverParameterIterator GetParameterIterator() const; + DriverParameterIterator GetParameterIterator( + const char* name) const; + + const char* GetParameterValue(const char* name, + const char* unknownValue = NULL, + const char* noValue = NULL) const; + bool GetBoolParameterValue(const char* name, + bool unknownValue = false, + bool noValue = false) const; + int32 GetInt32ParameterValue(const char* name, + int32 unknownValue = 0, + int32 noValue = 0) const; + int64 GetInt64ParameterValue(const char* name, + int64 unknownValue = 0, + int64 noValue = 0) const; + +protected: + virtual const driver_parameter* + GetParametersAndCount(int32* count) const = 0; + +private: + class Iterator; + class NameIterator; +}; + +// DriverSettings +class DriverSettings : public DriverParameterContainer { +public: + DriverSettings(); + virtual ~DriverSettings(); + + status_t Load(const char* driverName); + void Unset(); + +protected: + virtual const driver_parameter* + GetParametersAndCount(int32* count) const; + +private: + void* fSettingsHandle; + const driver_settings* fSettings; +}; + +// DriverParameter +class DriverParameter : public DriverParameterContainer { +public: + DriverParameter(); + virtual ~DriverParameter(); + + void SetTo(const driver_parameter* parameter); + + const char* GetName() const; + int32 CountValues() const; + const char* const* GetValues() const; + const char* ValueAt(int32 index, + const char* noValue = NULL) const; + bool BoolValueAt(int32 index, + bool noValue = false) const; + int32 Int32ValueAt(int32 index, + int32 noValue = 0) const; + int64 Int64ValueAt(int32 index, + int64 noValue = 0) const; + +protected: + virtual const driver_parameter* + GetParametersAndCount(int32* count) const; + +private: + const driver_parameter* fParameter; +}; + +} // namespace UserlandFSUtil + +using UserlandFSUtil::DriverParameterIterator; +using UserlandFSUtil::DriverParameterContainer; +using UserlandFSUtil::DriverSettings; +using UserlandFSUtil::DriverParameter; + +#endif // USERLAND_FS_DRIVER_SETTINGS_H diff --git a/headers/private/userlandfs/shared/HashMap.h b/headers/private/userlandfs/shared/HashMap.h new file mode 100644 index 0000000000..1733cffa8f --- /dev/null +++ b/headers/private/userlandfs/shared/HashMap.h @@ -0,0 +1,444 @@ +// HashMap.h +// +// Copyright (c) 2004, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#ifndef HASH_MAP_H +#define HASH_MAP_H + +//#include + +#include "AutoLocker.h" +#include "Locker.h" +#include "OpenHashTable.h" + +// HashMapElement +template +class HashMapElement : public OpenHashElement { +private: + typedef HashMapElement Element; +public: + + HashMapElement() : OpenHashElement(), fKey(), fValue() + { + fNext = -1; + } + + inline uint32 Hash() const + { + return fKey.GetHashCode(); + } + + inline bool operator==(const OpenHashElement &_element) const + { + const Element &element = static_cast(_element); + return (fKey == element.fKey); + } + + inline void Adopt(Element &element) + { + fKey = element.fKey; + fValue = element.fValue; + } + + Key fKey; + Value fValue; +}; + +// HashMap +template +class HashMap { +public: + class Entry { + public: + Entry() {} + Entry(const Key& key, Value value) : key(key), value(value) {} + + Key key; + Value value; + }; + + class Iterator { + private: + typedef HashMapElement Element; + public: + Iterator(const Iterator& other) + : fMap(other.fMap), + fIndex(other.fIndex), + fElement(other.fElement), + fLastElement(other.fElement) + { + } + + bool HasNext() const + { + return fElement; + } + + Entry Next() + { + if (!fElement) + return Entry(); + Entry result(fElement->fKey, fElement->fValue); + _FindNext(); + return result; + } + + Entry Remove() + { + if (!fLastElement) + return Entry(); + Entry result(fLastElement->fKey, fLastElement->fValue); + fMap->fTable.Remove(fLastElement, true); + fLastElement = NULL; + return result; + } + + Iterator& operator=(const Iterator& other) + { + fMap = other.fMap; + fIndex = other.fIndex; + fElement = other.fElement; + fLastElement = other.fLastElement; + return *this; + } + + private: + Iterator(HashMap* map) + : fMap(map), + fIndex(0), + fElement(NULL), + fLastElement(NULL) + { + // find first + _FindNext(); + } + + void _FindNext() + { + fLastElement = fElement; + if (fElement && fElement->fNext >= 0) { + fElement = fMap->fTable.ElementAt(fElement->fNext); + return; + } + fElement = NULL; + int32 arraySize = fMap->fTable.ArraySize(); + for (; !fElement && fIndex < arraySize; fIndex++) + fElement = fMap->fTable.FindFirst(fIndex); + } + + private: + friend class HashMap; + + HashMap* fMap; + int32 fIndex; + Element* fElement; + Element* fLastElement; + }; + + HashMap(); + ~HashMap(); + + status_t InitCheck() const; + + status_t Put(const Key& key, Value value); + Value Remove(const Key& key); + void Clear(); + Value Get(const Key& key) const; + + bool ContainsKey(const Key& key) const; + + int32 Size() const; + + Iterator GetIterator(); + +protected: + typedef HashMapElement Element; + friend class Iterator; + +private: + Element *_FindElement(const Key& key) const; + +protected: + OpenHashElementArray fElementArray; + OpenHashTable > fTable; +}; + +// SynchronizedHashMap +template +class SynchronizedHashMap : public Locker { +public: + typedef HashMap::Entry Entry; + typedef HashMap::Iterator Iterator; + + SynchronizedHashMap() : Locker("synchronized hash map") {} + ~SynchronizedHashMap() { Lock(); } + + status_t InitCheck() const + { + return fMap.InitCheck(); + } + + status_t Put(const Key& key, Value value) + { + MapLocker locker(this); + if (!locker.IsLocked()) + return B_ERROR; + return fMap.Put(key, value); + } + + Value Remove(const Key& key) + { + MapLocker locker(this); + if (!locker.IsLocked()) + return Value(); + return fMap.Remove(key); + } + + void Clear() + { + MapLocker locker(this); + return fMap.Clear(); + } + + Value Get(const Key& key) const + { + const Locker* lock = this; + MapLocker locker(const_cast(lock)); + if (!locker.IsLocked()) + return Value(); + return fMap.Get(key); + } + + bool ContainsKey(const Key& key) const + { + const Locker* lock = this; + MapLocker locker(const_cast(lock)); + if (!locker.IsLocked()) + return false; + return fMap.ContainsKey(key); + } + + int32 Size() const + { + const Locker* lock = this; + MapLocker locker(const_cast(lock)); + return fMap.Size(); + } + + Iterator GetIterator() + { + return fMap.GetIterator(); + } + + // for debugging only + const HashMap& GetUnsynchronizedMap() const { return fMap; } + HashMap& GetUnsynchronizedMap() { return fMap; } + +protected: + typedef AutoLocker MapLocker; + + HashMap fMap; +}; + +// HashKey32 +template +struct HashKey32 { + HashKey32() {} + HashKey32(const Value& value) : value(value) {} + + uint32 GetHashCode() const + { + return (uint32)value; + } + + HashKey32 operator=(const HashKey32& other) + { + value = other.value; + return *this; + } + + bool operator==(const HashKey32& other) const + { + return (value == other.value); + } + + bool operator!=(const HashKey32& other) const + { + return (value != other.value); + } + + Value value; +}; + + +// HashKey64 +template +struct HashKey64 { + HashKey64() {} + HashKey64(const Value& value) : value(value) {} + + uint32 GetHashCode() const + { + uint64 v = (uint64)value; + return (uint32)(v >> 32) ^ (uint32)v; + } + + HashKey64 operator=(const HashKey64& other) + { + value = other.value; + return *this; + } + + bool operator==(const HashKey64& other) const + { + return (value == other.value); + } + + bool operator!=(const HashKey64& other) const + { + return (value != other.value); + } + + Value value; +}; + + +// HashMap + +// constructor +template +HashMap::HashMap() + : fElementArray(1000), + fTable(1000, &fElementArray) +{ +} + +// destructor +template +HashMap::~HashMap() +{ +} + +// InitCheck +template +status_t +HashMap::InitCheck() const +{ + return (fTable.InitCheck() && fElementArray.InitCheck() + ? B_OK : B_NO_MEMORY); +} + +// Put +template +status_t +HashMap::Put(const Key& key, Value value) +{ + Element* element = _FindElement(key); + if (element) { + // already contains the key: just set the new value + element->fValue = value; + return B_OK; + } + // does not contain the key yet: add an element + element = fTable.Add(key.GetHashCode()); + if (!element) + return B_NO_MEMORY; + element->fKey = key; + element->fValue = value; + return B_OK; +} + +// Remove +template +Value +HashMap::Remove(const Key& key) +{ + Value value = Value(); + if (Element* element = _FindElement(key)) { + value = element->fValue; + fTable.Remove(element); + } + return value; +} + +// Clear +template +void +HashMap::Clear() +{ + fTable.RemoveAll(); +} + +// Get +template +Value +HashMap::Get(const Key& key) const +{ + if (Element* element = _FindElement(key)) + return element->fValue; + return Value(); +} + +// ContainsKey +template +bool +HashMap::ContainsKey(const Key& key) const +{ + return _FindElement(key); +} + +// Size +template +int32 +HashMap::Size() const +{ + return fTable.CountElements(); +} + +// GetIterator +template +HashMap::Iterator +HashMap::GetIterator() +{ + return Iterator(this); +} + +// _FindElement +template +HashMap::Element * +HashMap::_FindElement(const Key& key) const +{ + Element* element = fTable.FindFirst(key.GetHashCode()); + while (element && element->fKey != key) { + if (element->fNext >= 0) + element = fTable.ElementAt(element->fNext); + else + element = NULL; + } + return element; +} + +#endif // HASH_MAP_H diff --git a/headers/private/userlandfs/shared/HashSet.h b/headers/private/userlandfs/shared/HashSet.h new file mode 100644 index 0000000000..9c5e39cfb1 --- /dev/null +++ b/headers/private/userlandfs/shared/HashSet.h @@ -0,0 +1,323 @@ +// HashSet.h +// +// Copyright (c) 2004, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#ifndef HASH_SET_H +#define HASH_SET_H + +#include "AutoLocker.h" +#include "Locker.h" +#include "OpenHashTable.h" + +// HashSetElement +template +class HashSetElement : public OpenHashElement { +private: + typedef HashSetElement Element; +public: + + HashSetElement() : OpenHashElement(), fKey() + { + fNext = -1; + } + + inline uint32 Hash() const + { + return fKey.GetHashCode(); + } + + inline bool operator==(const OpenHashElement &_element) const + { + const Element &element = static_cast(_element); + return (fKey == element.fKey); + } + + inline void Adopt(Element &element) + { + fKey = element.fKey; + } + + Key fKey; +}; + +// HashSet +template +class HashSet { +public: + class Iterator { + private: + typedef HashSetElement Element; + public: + Iterator(const Iterator& other) + : fSet(other.fSet), + fIndex(other.fIndex), + fElement(other.fElement), + fLastElement(other.fElement) + { + } + + bool HasNext() const + { + return fElement; + } + + Key Next() + { + if (!fElement) + return Key(); + Key result(fElement->fKey); + _FindNext(); + return result; + } + + bool Remove() + { + if (!fLastElement) + return false; + fSet->fTable.Remove(fLastElement); + fLastElement = NULL; + return true; + } + + Iterator& operator=(const Iterator& other) + { + fSet = other.fSet; + fIndex = other.fIndex; + fElement = other.fElement; + fLastElement = other.fLastElement; + return *this; + } + + private: + Iterator(HashSet* map) + : fSet(map), + fIndex(0), + fElement(NULL), + fLastElement(NULL) + { + // find first + _FindNext(); + } + + void _FindNext() + { + fLastElement = fElement; + if (fElement && fElement->fNext >= 0) { + fElement = fSet->fTable.ElementAt(fElement->fNext); + return; + } + fElement = NULL; + int32 arraySize = fSet->fTable.ArraySize(); + for (; !fElement && fIndex < arraySize; fIndex++) + fElement = fSet->fTable.FindFirst(fIndex); + } + + private: + friend class HashSet; + + HashSet* fSet; + int32 fIndex; + Element* fElement; + Element* fLastElement; + }; + + HashSet(); + ~HashSet(); + + status_t InitCheck() const; + + status_t Add(const Key& key); + bool Remove(const Key& key); + bool Contains(const Key& key) const; + + int32 Size() const; + + Iterator GetIterator(); + +protected: + typedef HashSetElement Element; + friend class Iterator; + +private: + Element *_FindElement(const Key& key) const; + +protected: + OpenHashElementArray fElementArray; + OpenHashTable > fTable; +}; + +// SynchronizedHashSet +template +class SynchronizedHashSet : public Locker { +public: + typedef HashSet::Iterator Iterator; + + SynchronizedHashSet() : Locker("synchronized hash map") {} + ~SynchronizedHashSet() { Lock(); } + + status_t InitCheck() const + { + return fSet.InitCheck(); + } + + status_t Add(const Key& key) + { + MapLocker locker(this); + if (!locker.IsLocked()) + return B_ERROR; + return fSet.Add(key); + } + + bool Remove(const Key& key) + { + MapLocker locker(this); + if (!locker.IsLocked()) + return false; + return fSet.Remove(key); + } + + bool Contains(const Key& key) const + { + const Locker* lock = this; + MapLocker locker(const_cast(lock)); + if (!locker.IsLocked()) + return false; + return fSet.Contains(key); + } + + int32 Size() const + { + const Locker* lock = this; + MapLocker locker(const_cast(lock)); + return fSet.Size(); + } + + Iterator GetIterator() + { + return fSet.GetIterator(); + } + + // for debugging only + const HashSet& GetUnsynchronizedSet() const { return fSet; } + HashSet& GetUnsynchronizedSet() { return fSet; } + +protected: + typedef AutoLocker MapLocker; + + HashSet fSet; +}; + +// HashSet + +// constructor +template +HashSet::HashSet() + : fElementArray(1000), + fTable(1000, &fElementArray) +{ +} + +// destructor +template +HashSet::~HashSet() +{ +} + +// InitCheck +template +status_t +HashSet::InitCheck() const +{ + return (fTable.InitCheck() && fElementArray.InitCheck() + ? B_OK : B_NO_MEMORY); +} + +// Add +template +status_t +HashSet::Add(const Key& key) +{ + if (Contains(key)) + return B_OK; + Element* element = fTable.Add(key.GetHashCode()); + if (!element) + return B_NO_MEMORY; + element->fKey = key; + return B_OK; +} + +// Remove +template +bool +HashSet::Remove(const Key& key) +{ + if (Element* element = _FindElement(key)) { + fTable.Remove(element); + return true; + } + return false; +} + +// Contains +template +bool +HashSet::Contains(const Key& key) const +{ + return _FindElement(key); +} + +// Size +template +int32 +HashSet::Size() const +{ + return fTable.CountElements(); +} + +// GetIterator +template +HashSet::Iterator +HashSet::GetIterator() +{ + return Iterator(this); +} + +// _FindElement +template +HashSet::Element * +HashSet::_FindElement(const Key& key) const +{ + Element* element = fTable.FindFirst(key.GetHashCode()); + while (element && element->fKey != key) { + if (element->fNext >= 0) + element = fTable.ElementAt(element->fNext); + else + element = NULL; + } + return element; +} + +#endif // HASH_SET_H diff --git a/headers/private/userlandfs/shared/LazyInitializable.h b/headers/private/userlandfs/shared/LazyInitializable.h new file mode 100644 index 0000000000..4d6892ced9 --- /dev/null +++ b/headers/private/userlandfs/shared/LazyInitializable.h @@ -0,0 +1,31 @@ +// LazyInitializable.h + +#ifndef USERLAND_FS_LAZY_INITIALIZABLE_H +#define USERLAND_FS_LAZY_INITIALIZABLE_H + +#include + +namespace UserlandFSUtil { + +class LazyInitializable { +public: + LazyInitializable(); + LazyInitializable(bool init); + virtual ~LazyInitializable(); + + status_t Access(); + status_t InitCheck() const; + +protected: + virtual status_t FirstTimeInit() = 0; + +protected: + status_t fInitStatus; + sem_id fInitSemaphore; +}; + +} // namespace UserlandFSUtil + +using UserlandFSUtil::LazyInitializable; + +#endif // USERLAND_FS_LAZY_INITIALIZABLE_H diff --git a/headers/private/userlandfs/shared/Locker.h b/headers/private/userlandfs/shared/Locker.h new file mode 100644 index 0000000000..b04c2f7537 --- /dev/null +++ b/headers/private/userlandfs/shared/Locker.h @@ -0,0 +1,59 @@ +// +// $Id: Locker.h,v 1.1 2002/07/09 12:24:33 ejakowatz Exp $ +// +// This is the Locker interface for OpenBeOS. It has been created to +// be source and binary compatible with the BeOS version of Locker. +// +// bonefish: +// * Removed `virtual' from destructor and FBC reserved space. +// * Renamed to Locker. + + +#ifndef _OPENBEOS_LOCKER_H +#define _OPENBEOS_LOCKER_H + + +#include +#include + +namespace UserlandFSUtil { + +class Locker { +public: + Locker(); + Locker(const char *name); + Locker(bool benaphore_style); + Locker(const char *name, bool benaphore_style); + + // The following constructor is not documented in the BeBook + // and is only listed here to ensure binary compatibility. + // DO NOT USE THIS CONSTRUCTOR! + Locker(const char *name, bool benaphore_style, bool); + + ~Locker(); + + bool Lock(void); + status_t LockWithTimeout(bigtime_t timeout); + void Unlock(void); + + thread_id LockingThread(void) const; + bool IsLocked(void) const; + int32 CountLocks(void) const; + int32 CountLockRequests(void) const; + sem_id Sem(void) const; + +private: + void InitLocker(const char *name, bool benaphore_style); + bool AcquireLock(bigtime_t timeout, status_t *error); + + int32 fBenaphoreCount; + sem_id fSemaphoreID; + thread_id fLockOwner; + int32 fRecursiveCount; +}; + +} // namespace UserlandFSUtil + +using UserlandFSUtil::Locker; + +#endif // _OPENBEOS_LOCKER_H diff --git a/headers/private/userlandfs/shared/ObjectTracker.h b/headers/private/userlandfs/shared/ObjectTracker.h new file mode 100644 index 0000000000..2bc5815570 --- /dev/null +++ b/headers/private/userlandfs/shared/ObjectTracker.h @@ -0,0 +1,80 @@ +// ObjectTracker.h + +#ifndef USERLAND_FS_OBJECT_TRACKER_H +#define USERLAND_FS_OBJECT_TRACKER_H + +#include "DLList.h" +#include "Locker.h" + +namespace UserlandFSUtil { + +class ObjectTracker; +class GetObjectTrackableLink; + +// ObjectTrackable +class ObjectTrackable { +public: + ObjectTrackable(); + virtual ~ObjectTrackable(); + +private: + friend class ObjectTracker; + friend class GetObjectTrackableLink; + + DLListLink fLink; +}; + +// GetObjectTrackableLink +struct GetObjectTrackableLink { + inline DLListLink *operator()( + ObjectTrackable* trackable) const + { + return &trackable->fLink; + } + + inline const DLListLink *operator()( + const ObjectTrackable* trackable) const + { + return &trackable->fLink; + } +}; + +// ObjectTracker +class ObjectTracker { +private: + ObjectTracker(); + ~ObjectTracker(); + +public: + + static ObjectTracker* InitDefault(); + static void ExitDefault(); + static ObjectTracker* GetDefault(); + +private: + friend class ObjectTrackable; + + void AddTrackable(ObjectTrackable* trackable); + void RemoveTrackable(ObjectTrackable* trackable); + +private: + Locker fLock; + DLList fTrackables; + + static ObjectTracker* sTracker; +}; + +} // namespace UserlandFSUtil + +using UserlandFSUtil::ObjectTrackable; +using UserlandFSUtil::ObjectTracker; + +#ifdef DEBUG_OBJECT_TRACKING +# define ONLY_OBJECT_TRACKABLE_BASE_CLASS : private ObjectTrackable +# define FIRST_OBJECT_TRACKABLE_BASE_CLASS private ObjectTrackable, +#else +# define ONLY_OBJECT_TRACKABLE_BASE_CLASS +# define FIRST_OBJECT_TRACKABLE_BASE_CLASS +#endif + +#endif // USERLAND_FS_OBJECT_TRACKER_H diff --git a/headers/private/userlandfs/shared/OpenHashTable.h b/headers/private/userlandfs/shared/OpenHashTable.h new file mode 100644 index 0000000000..80b294bedc --- /dev/null +++ b/headers/private/userlandfs/shared/OpenHashTable.h @@ -0,0 +1,510 @@ +/* +Open Tracker License + +Terms and Conditions + +Copyright (c) 1991-2000, Be Incorporated. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice applies to all licensees +and shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of Be Incorporated shall not be +used in advertising or otherwise to promote the sale, use or other dealings in +this Software without prior written authorization from Be Incorporated. + +Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks +of Be Incorporated in the United States and other countries. Other brand product +names are registered trademarks or trademarks of their respective holders. +All rights reserved. +*/ + +// bonefish: +// * removed need for exceptions +// * fixed warnings +// * implemented rehashing +// * added RemoveAll() +// TODO: +// * shrinking of element vectors + +// Hash table with open addresssing + +#ifndef __OPEN_HASH_TABLE__ +#define __OPEN_HASH_TABLE__ + +#include +#include + +// don't include +#define ASSERT(E) (void)0 +#define TRESPASS() (void)0 + +//namespace BPrivate { + +template +class ElementVector { + // element vector for OpenHashTable needs to implement this + // interface +public: + Element &At(int32 index); + Element *Add(); + int32 IndexOf(const Element &) const; + void Remove(int32 index); +}; + +class OpenHashElement { +public: + uint32 Hash() const; + bool operator==(const OpenHashElement &) const; + void Adopt(OpenHashElement &); + // low overhead copy, original element is in undefined state + // after call (calls Adopt on BString members, etc.) + int32 fNext; +}; + +const uint32 kPrimes [] = { + 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, + 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859, + 134217689, 268435399, 536870909, 1073741789, 2147483647, 0 +}; + +template > +class OpenHashTable { +public: + OpenHashTable(int32 minSize, ElementVec *elementVector = 0, + float maxLoadFactor = 0.8); + // it is up to the subclass of OpenHashTable to supply + // elementVector + ~OpenHashTable(); + + bool InitCheck() const; + + void SetElementVector(ElementVec *elementVector); + + Element *FindFirst(uint32 elementHash) const; + Element *Add(uint32 elementHash); + + void Remove(Element *element, bool dontRehash = false); + void RemoveAll(); + + // when calling Add, any outstanding element pointer may become + // invalid; to deal with this, get the element index and restore + // it after the add + int32 ElementIndex(const Element *) const; + Element *ElementAt(int32 index) const; + + int32 ArraySize() const; + int32 VectorSize() const; + int32 CountElements() const; + +protected: + static int32 OptimalSize(int32 minSize); + +private: + bool _RehashIfNeeded(); + bool _Rehash(); + + int32 fArraySize; + int32 fInitialSize; + int32 fElementCount; + int32 *fHashArray; + ElementVec *fElementVector; + float fMaxLoadFactor; +}; + +template +class OpenHashElementArray : public ElementVector { + // this is a straightforward implementation of an element vector + // deleting is handled by linking deleted elements into a free list + // the vector never shrinks +public: + OpenHashElementArray(int32 initialSize); + ~OpenHashElementArray(); + + bool InitCheck() const; + + Element &At(int32 index); + const Element &At(int32 index) const; + Element *Add(const Element &); + Element *Add(); + void Remove(int32 index); + int32 IndexOf(const Element &) const; + int32 Size() const; + +private: + Element *fData; + int32 fSize; + int32 fNextFree; + int32 fNextDeleted; +}; + + +//----------------------------------- + +template +OpenHashTable::OpenHashTable(int32 minSize, + ElementVec *elementVector, float maxLoadFactor) + : fArraySize(OptimalSize(minSize)), + fInitialSize(fArraySize), + fElementCount(0), + fElementVector(elementVector), + fMaxLoadFactor(maxLoadFactor) +{ + // sanity check the maximal load factor + if (fMaxLoadFactor < 0.5) + fMaxLoadFactor = 0.5; + // allocate and init the array + fHashArray = (int32*)calloc(fArraySize, sizeof(int32)); + if (fHashArray) { + for (int32 index = 0; index < fArraySize; index++) + fHashArray[index] = -1; + } +} + +template +OpenHashTable::~OpenHashTable() +{ + RemoveAll(); + free(fHashArray); +} + +template +bool +OpenHashTable::InitCheck() const +{ + return (fHashArray && fElementVector); +} + +template +int32 +OpenHashTable::OptimalSize(int32 minSize) +{ + for (int32 index = 0; ; index++) + if (!kPrimes[index] || kPrimes[index] >= (uint32)minSize) + return (int32)kPrimes[index]; + + return 0; +} + +template +Element * +OpenHashTable::FindFirst(uint32 hash) const +{ + ASSERT(fElementVector); + hash %= fArraySize; + if (fHashArray[hash] < 0) + return 0; + + return &fElementVector->At(fHashArray[hash]); +} + +template +int32 +OpenHashTable::ElementIndex(const Element *element) const +{ + return fElementVector->IndexOf(*element); +} + +template +Element * +OpenHashTable::ElementAt(int32 index) const +{ + return &fElementVector->At(index); +} + +template +int32 +OpenHashTable::ArraySize() const +{ + return fArraySize; +} + +template +int32 +OpenHashTable::VectorSize() const +{ + return fElementVector->Size(); +} + +template +int32 +OpenHashTable::CountElements() const +{ + return fElementCount; +} + + +template +Element * +OpenHashTable::Add(uint32 hash) +{ + ASSERT(fElementVector); + _RehashIfNeeded(); + hash %= fArraySize; + Element *result = fElementVector->Add(); + if (result) { + result->fNext = fHashArray[hash]; + fHashArray[hash] = fElementVector->IndexOf(*result); + fElementCount++; + } + return result; +} + +template +void +OpenHashTable::Remove(Element *element, bool dontRehash) +{ + if (!dontRehash) + _RehashIfNeeded(); + uint32 hash = element->Hash() % fArraySize; + int32 next = fHashArray[hash]; + ASSERT(next >= 0); + + if (&fElementVector->At(next) == element) { + fHashArray[hash] = element->fNext; + fElementVector->Remove(next); + fElementCount--; + return; + } + + for (int32 index = next; index >= 0; ) { + // look for an existing match in table + next = fElementVector->At(index).fNext; + if (next < 0) { + TRESPASS(); + return; + } + + if (&fElementVector->At(next) == element) { + fElementVector->At(index).fNext = element->fNext; + fElementVector->Remove(next); + fElementCount--; + return; + } + index = next; + } +} + +template +void +OpenHashTable::RemoveAll() +{ + for (int32 i = 0; fElementCount > 0 && i < fArraySize; i++) { + int32 index = fHashArray[i]; + while (index >= 0) { + Element* element = &fElementVector->At(index); + int32 next = element->fNext; + fElementVector->Remove(index); + fElementCount--; + index = next; + } + fHashArray[i] = -1; + } + _RehashIfNeeded(); +} + +template +void +OpenHashTable::SetElementVector(ElementVec *elementVector) +{ + fElementVector = elementVector; +} + +// _RehashIfNeeded +template +bool +OpenHashTable::_RehashIfNeeded() +{ + // The load factor range [fMaxLoadFactor / 3, fMaxLoadFactor] is fine, + // I think. After rehashing the load factor will be about + // fMaxLoadFactor * 2 / 3, respectively fMaxLoadFactor / 2. + float loadFactor = (float)fElementCount / (float)fArraySize; + if (loadFactor > fMaxLoadFactor + || (fArraySize > fInitialSize && loadFactor < fMaxLoadFactor / 3)) { + return _Rehash(); + } + return true; +} + +// _Rehash +template +bool +OpenHashTable::_Rehash() +{ + bool result = true; + int32 newSize = int32(fElementCount * 1.73 * fMaxLoadFactor); + newSize = (fInitialSize > newSize ? fInitialSize : newSize); + if (newSize != fArraySize) { + // allocate a new array + int32 *newHashArray = (int32*)calloc(newSize, sizeof(int32)); + if (newHashArray) { + // init the new hash array + for (int32 index = 0; index < newSize; index++) + newHashArray[index] = -1; + // iterate through all elements and put them into the new + // hash array + for (int i = 0; i < fArraySize; i++) { + int32 index = fHashArray[i]; + while (index >= 0) { + // insert the element in the new array + Element &element = fElementVector->At(index); + int32 next = element.fNext; + uint32 hash = (element.Hash() % newSize); + element.fNext = newHashArray[hash]; + newHashArray[hash] = index; + // next element in old list + index = next; + } + } + // delete the old array and set the new one + free(fHashArray); + fHashArray = newHashArray; + fArraySize = newSize; + } else + result = false; + } + return result; +} + + +template +OpenHashElementArray::OpenHashElementArray(int32 initialSize) + : fSize(initialSize), + fNextFree(0), + fNextDeleted(-1) +{ + fData = (Element*)calloc((size_t)initialSize, sizeof(Element)); +} + +template +OpenHashElementArray::~OpenHashElementArray() +{ + free(fData); +} + +template +bool +OpenHashElementArray::InitCheck() const +{ + return fData; +} + +template +Element & +OpenHashElementArray::At(int32 index) +{ + ASSERT(index < fSize); + return fData[index]; +} + +template +const Element & +OpenHashElementArray::At(int32 index) const +{ + ASSERT(index < fSize); + return fData[index]; +} + +template +int32 +OpenHashElementArray::IndexOf(const Element &element) const +{ + int32 result = &element - fData; + if (result < 0 || result > fSize) + return -1; + + return result; +} + +template +int32 +OpenHashElementArray::Size() const +{ + return fSize; +} + + +template +Element * +OpenHashElementArray::Add(const Element &newElement) +{ + Element *element = Add(); + if (element) + element.Adopt(newElement); + return element; +} + +#if DEBUG +const int32 kGrowChunk = 10; +#else +const int32 kGrowChunk = 1024; +#endif + +template +Element * +OpenHashElementArray::Add() +{ + int32 index = fNextFree; + if (fNextDeleted >= 0) { + index = fNextDeleted; + fNextDeleted = At(index).fNext; + } else if (fNextFree >= fSize - 1) { + int32 newSize = fSize + kGrowChunk; +/* + Element *newData = (Element *)calloc((size_t)newSize , sizeof(Element)); + if (!newData) + return NULL; + memcpy(newData, fData, fSize * sizeof(Element)); + free(fData); +*/ + Element *newData = (Element*)realloc(fData, + (size_t)newSize * sizeof(Element)); + if (!newData) + return NULL; + + fData = newData; + fSize = newSize; + index = fNextFree; + fNextFree++; + } else + fNextFree++; + + new (&At(index)) Element; + // call placement new to initialize the element properly + ASSERT(At(index).fNext == -1); + + return &At(index); +} + +template +void +OpenHashElementArray::Remove(int32 index) +{ + // delete by chaining empty elements in a single linked + // list, reusing the next field + ASSERT(index < fSize); + At(index).~Element(); + // call the destructor explicitly to destroy the element + // properly + At(index).fNext = fNextDeleted; + fNextDeleted = index; +} + +//} // namespace BPrivate + +//using namespace BPrivate; + +#endif diff --git a/headers/private/userlandfs/shared/Referencable.h b/headers/private/userlandfs/shared/Referencable.h new file mode 100644 index 0000000000..42a5ed880d --- /dev/null +++ b/headers/private/userlandfs/shared/Referencable.h @@ -0,0 +1,118 @@ +// Referencable.h + +#ifndef USERLAND_FS_REFERENCABLE_H +#define USERLAND_FS_REFERENCABLE_H + +#include + +#include "ObjectTracker.h" + +namespace UserlandFSUtil { + +// Referencable +class Referencable ONLY_OBJECT_TRACKABLE_BASE_CLASS { +public: + Referencable( + bool deleteWhenUnreferenced = false); + virtual ~Referencable(); + + void AddReference(); + bool RemoveReference(); // returns true after last + + int32 CountReferences() const; + +protected: + vint32 fReferenceCount; + bool fDeleteWhenUnreferenced; +}; + +// Reference +template +class Reference { +public: + Reference() + : fObject(NULL) + { + } + + Reference(Type* object, bool alreadyHasReference = false) + : fObject(NULL) + { + SetTo(object, alreadyHasReference); + } + + Reference(const Reference& other) + : fObject(NULL) + { + SetTo(other.fObject); + } + + ~Reference() + { + Unset(); + } + + void SetTo(Type* object, bool alreadyHasReference = false) + { + Unset(); + fObject = object; + if (fObject && !alreadyHasReference) + fObject->AddReference(); + } + + void Unset() + { + if (fObject) { + fObject->RemoveReference(); + fObject = NULL; + } + } + + Type* Get() const + { + return fObject; + } + + Type* Detach() + { + Type* object = fObject; + fObject = NULL; + return object; + } + + Type& operator*() const + { + return *fObject; + } + + Type* operator->() const + { + return fObject; + } + + Reference& operator=(const Reference& other) + { + SetTo(other.fObject); + return *this; + } + + bool operator==(const Reference& other) const + { + return (fObject == other.fObject); + } + + bool operator!=(const Reference& other) const + { + return (fObject != other.fObject); + } + +private: + Type* fObject; +}; + +} // namespace UserlandFSUtil + +using UserlandFSUtil::Referencable; +using UserlandFSUtil::Reference; + +#endif // USERLAND_FS_REFERENCABLE_H diff --git a/headers/private/userlandfs/shared/SLList.h b/headers/private/userlandfs/shared/SLList.h new file mode 100644 index 0000000000..468e8e5a84 --- /dev/null +++ b/headers/private/userlandfs/shared/SLList.h @@ -0,0 +1,332 @@ +// SLList.h + +#ifndef SL_LIST_H +#define SL_LIST_H + +#include + +namespace UserlandFSUtil { + +// SLListLink +template +class SLListLink { +public: + SLListLink() : next(NULL) {} + ~SLListLink() {} + + Element *next; +}; + +// SLListLinkImpl +template +class SLListLinkImpl { +private: + typedef SLListLink Link; + +public: + SLListLinkImpl() : fSLListLink() {} + ~SLListLinkImpl() {} + + Link *GetSLListLink() { return &fSLListLink; } + const Link *GetSLListLink() const { return &fSLListLink; } + +private: + Link fSLListLink; +}; + +// SLListStandardGetLink +template +class SLListStandardGetLink { +private: + typedef SLListLink Link; + +public: + inline Link *operator()(Element *element) const + { + return element->GetSLListLink(); + } + + inline const Link *operator()(const Element *element) const + { + return element->GetSLListLink(); + } +}; + +// for convenience +#define SL_LIST_TEMPLATE_LIST template +#define SL_LIST_CLASS_NAME SLList + +// SLList +template > +class SLList { +private: + typedef SLList List; + typedef SLListLink Link; + +public: + class Iterator { + public: + Iterator(List *list) + : fList(list), + fPrevious(NULL), + fCurrent(NULL), + fNext(fList->GetFirst()) + { + } + + Iterator(const Iterator &other) + { + *this = other; + } + + bool HasNext() const + { + return fNext; + } + + Element *Next() + { + if (fCurrent) + fPrevious = fCurrent; + + fCurrent = fNext; + + if (fNext) + fNext = fList->GetNext(fNext); + + return fCurrent; + } + + Element *Remove() + { + Element *element = fCurrent; + if (fCurrent) { + fList->_Remove(fPrevious, fCurrent); + fCurrent = NULL; + } + return element; + } + + Iterator &operator=(const Iterator &other) + { + fList = other.fList; + fPrevious = other.fPrevious; + fCurrent = other.fCurrent; + fNext = other.fNext; + return *this; + } + + private: + List *fList; + Element *fPrevious; + Element *fCurrent; + Element *fNext; + }; + + class ConstIterator { + public: + ConstIterator(const List *list) + : fList(list), + fNext(list->GetFirst()) + { + } + + ConstIterator(const ConstIterator &other) + { + *this = other; + } + + bool HasNext() const + { + return fNext; + } + + Element *Next() + { + Element *element = fNext; + if (fNext) + fNext = fList->GetNext(fNext); + return element; + } + + ConstIterator &operator=(const ConstIterator &other) + { + fList = other.fList; + fNext = other.fNext; + return *this; + } + + private: + const List *fList; + Element *fNext; + }; + +public: + SLList() : fFirst(NULL), fLast(NULL) {} + SLList(const GetLink &getLink) + : fFirst(NULL), fLast(NULL), fGetLink(getLink) {} + ~SLList() {} + + inline bool IsEmpty() const { return (fFirst == NULL); } + + inline void Insert(Element *element, bool back = true); + inline void InsertAfter(Element *previous, Element *element); + inline void Remove(Element *element); + // O(n)! + + inline void MoveFrom(SL_LIST_CLASS_NAME *fromList); + + inline void RemoveAll(); + + inline Element *GetFirst() const { return fFirst; } + inline Element *GetLast() const { return fLast; } + + inline Element *GetHead() const { return fFirst; } + inline Element *GetTail() const { return fLast; } + + inline Element *GetNext(Element *element) const; + + inline int32 Size() const; + // O(n)! + + inline Iterator GetIterator() { return Iterator(this); } + inline ConstIterator GetIterator() const { return ConstIterator(this); } + +private: + friend class Iterator; + + inline void _Remove(Element *previous, Element *element); + +private: + Element *fFirst; + Element *fLast; + GetLink fGetLink; +}; + +} // namespace UserlandFSUtil + +using UserlandFSUtil::SLList; +using UserlandFSUtil::SLListLink; +using UserlandFSUtil::SLListLinkImpl; + + +// inline methods + +// Insert +SL_LIST_TEMPLATE_LIST +void +SL_LIST_CLASS_NAME::Insert(Element *element, bool back) +{ + InsertAfter((back ? fLast : NULL), element); +} + +// InsertAfter +SL_LIST_TEMPLATE_LIST +void +SL_LIST_CLASS_NAME::InsertAfter(Element *previous, Element *element) +{ + if (element) { + Link *elLink = fGetLink(element); + if (previous) { + // insert after previous element + Link *prevLink = fGetLink(previous); + elLink->next = prevLink->next; + prevLink->next = element; + } else { + // no previous element given: prepend + elLink->next = fFirst; + fFirst = element; + } + + // element may be new last element + if (fLast == previous) + fLast = element; + } +} + +// Remove +SL_LIST_TEMPLATE_LIST +void +SL_LIST_CLASS_NAME::Remove(Element *element) +{ + if (!element) + return; + + for (Iterator it = GetIterator(); it.HasNext();) { + if (element == it.Next()) { + it.Remove(); + return; + } + } +} + +// MoveFrom +SL_LIST_TEMPLATE_LIST +void +SL_LIST_CLASS_NAME::MoveFrom(SL_LIST_CLASS_NAME *fromList) +{ + if (fromList && fromList->fFirst) { + if (fFirst) { + fGetLink(fLast)->next = fromList->fFirst; + fLast = fromList->fLast; + } else { + fFirst = fromList->fFirst; + fLast = fromList->fLast; + } + fromList->fFirst = NULL; + fromList->fLast = NULL; + } +} + +// RemoveAll +SL_LIST_TEMPLATE_LIST +void +SL_LIST_CLASS_NAME::RemoveAll() +{ + Element *element = fFirst; + while (element) { + Link *elLink = fGetLink(element); + element = elLink->next; + elLink->next = NULL; + } + fFirst = NULL; + fLast = NULL; +} + +// GetNext +SL_LIST_TEMPLATE_LIST +Element * +SL_LIST_CLASS_NAME::GetNext(Element *element) const +{ + return (element ? fGetLink(element)->next : NULL); +} + +// _Remove +SL_LIST_TEMPLATE_LIST +void +SL_LIST_CLASS_NAME::_Remove(Element *previous, Element *element) +{ + Link *elLink = fGetLink(element); + if (previous) + fGetLink(previous)->next = elLink->next; + else + fFirst = elLink->next; + + if (element == fLast) + fLast = previous; + + elLink->next = NULL; +} + +// Size +SL_LIST_TEMPLATE_LIST +int32 +SL_LIST_CLASS_NAME::Size() const +{ + int32 count = 0; + for (Element* element = GetFirst(); element; element = GetNext(element)) + count++; + return count; +} + +#endif // SL_LIST_H diff --git a/headers/private/userlandfs/shared/String.h b/headers/private/userlandfs/shared/String.h new file mode 100644 index 0000000000..46bfcce78c --- /dev/null +++ b/headers/private/userlandfs/shared/String.h @@ -0,0 +1,70 @@ +// String.h + +#ifndef STRING_H +#define STRING_H + +#include + +#include + + +// string_hash +// +// from the Dragon Book: a slightly modified hashpjw() +static inline +uint32 +string_hash(const char *name) +{ + uint32 h = 0; + if (name) { + for (; *name; name++) { + uint32 g = h & 0xf0000000; + if (g) + h ^= g >> 24; + h = (h << 4) + *name; + } + } + return h; +} + +#ifdef __cplusplus + +namespace UserlandFSUtil { + +// String +class String { +public: + String(); + String(const String &string); + String(const char *string, int32 length = -1); + ~String(); + + bool SetTo(const char *string, int32 maxLength = -1); + void Unset(); + + void Truncate(int32 newLength); + + const char *GetString() const; + int32 GetLength() const { return fLength; } + + uint32 GetHashCode() const { return string_hash(GetString()); } + + String &operator=(const String &string); + bool operator==(const String &string) const; + bool operator!=(const String &string) const { return !(*this == string); } + +private: + bool _SetTo(const char *string, int32 length); + +private: + int32 fLength; + char *fString; +}; + +} // namespace UserlandFSUtil + +using UserlandFSUtil::String; + +#endif // __cplusplus + +#endif // STRING_H diff --git a/headers/private/userlandfs/shared/Vector.h b/headers/private/userlandfs/shared/Vector.h new file mode 100644 index 0000000000..d7f52a2b90 --- /dev/null +++ b/headers/private/userlandfs/shared/Vector.h @@ -0,0 +1,798 @@ +// Vector.h +// +// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#ifndef _VECTOR_H +#define _VECTOR_H + +#include +#include +#include + +#include + +template class VectorIterator; + +// for convenience +#define _VECTOR_TEMPLATE_LIST template +#define _VECTOR_CLASS_NAME Vector + +/*! + \class Vector + \brief A generic vector implementation. +*/ +template +class Vector { +public: + typedef VectorIterator Iterator; + typedef VectorIterator ConstIterator; + +private: + static const size_t kDefaultChunkSize = 10; + static const size_t kMaximalChunkSize = 1024 * 1024; + +public: + Vector(size_t chunkSize = kDefaultChunkSize); + ~Vector(); + + status_t PushFront(const Value &value); + status_t PushBack(const Value &value); + + void PopFront(); + void PopBack(); + + status_t Insert(const Value &value, int32 index); + status_t Insert(const Value &value, const Iterator &iterator); + + int32 Remove(const Value &value); + Iterator Erase(int32 index); + Iterator Erase(const Iterator &iterator); + + inline int32 Count() const; + inline bool IsEmpty() const; + void MakeEmpty(); + + inline Iterator Begin(); + inline ConstIterator Begin() const; + inline Iterator End(); + inline ConstIterator End() const; + inline Iterator Null(); + inline ConstIterator Null() const; + inline Iterator IteratorForIndex(int32 index); + inline ConstIterator IteratorForIndex(int32 index) const; + + inline const Value &ElementAt(int32 index) const; + inline Value &ElementAt(int32 index); + + int32 IndexOf(const Value &value, int32 start = 0) const; + Iterator Find(const Value &value); + Iterator Find(const Value &value, const Iterator &start); + ConstIterator Find(const Value &value) const; + ConstIterator Find(const Value &value, const ConstIterator &start) const; + + inline Value &operator[](int32 index); + inline const Value &operator[](int32 index) const; + + // debugging + int32 GetCapacity() const { return fCapacity; } + +private: + inline static void _MoveItems(Value *values, int32 offset, int32 count); + bool _Resize(size_t count); + inline int32 _IteratorIndex(const Iterator &iterator) const; + inline int32 _IteratorIndex(const ConstIterator &iterator) const; + +private: + size_t fCapacity; + size_t fChunkSize; + int32 fItemCount; + Value *fItems; +}; + + +// VectorIterator +template +class VectorIterator { +private: + typedef VectorIterator Iterator; + +public: + inline VectorIterator() + : fElement(NULL) + { + } + + inline VectorIterator(const Iterator &other) + : fElement(other.fElement) + { + } + + inline Iterator &operator++() + { + if (fElement) + ++fElement; + return *this; + } + + inline Iterator operator++(int) + { + Iterator it(*this); + ++*this; + return it; + } + + inline Iterator &operator--() + { + if (fElement) + --fElement; + return *this; + } + + inline Iterator operator--(int) + { + Iterator it(*this); + --*this; + return it; + } + + inline Iterator &operator=(const Iterator &other) + { + fElement = other.fElement; + return *this; + } + + + inline bool operator==(const Iterator &other) const + { + return (fElement == other.fElement); + } + + inline bool operator!=(const Iterator &other) const + { + return !(*this == other); + } + + inline Value &operator*() const + { + return *fElement; + } + + inline Value *operator->() const + { + return fElement; + } + + inline operator bool() const + { + return fElement; + } + +// private +public: + inline VectorIterator(Value *element) + : fElement(element) + { + } + + inline Value *Element() const + { + return fElement; + } + +protected: + Value *fElement; +}; + + +// Vector + +// constructor +/*! \brief Creates an empty vector. + \param chunkSize The granularity for the vector's capacity, i.e. the + minimal number of elements the capacity grows or shrinks when + necessary. +*/ +_VECTOR_TEMPLATE_LIST +_VECTOR_CLASS_NAME::Vector(size_t chunkSize) + : fCapacity(0), + fChunkSize(chunkSize), + fItemCount(0), + fItems(NULL) +{ + if (fChunkSize == 0 || fChunkSize > kMaximalChunkSize) + fChunkSize = kDefaultChunkSize; + _Resize(0); +} + +// destructor +/*! \brief Frees all resources associated with the object. + + The contained elements are destroyed. Note, that, if the element + type is a pointer type, only the pointer is destroyed, not the object + it points to. +*/ +_VECTOR_TEMPLATE_LIST +_VECTOR_CLASS_NAME::~Vector() +{ + MakeEmpty(); + free(fItems); +} + +// PushFront +/*! \brief Inserts a copy of the supplied value at the beginning of the + vector. + \param value The element to be inserted. + \return + - \c B_OK: Everything went fine. + - \c B_NO_MEMORY: Insufficient memory for this operation. +*/ +_VECTOR_TEMPLATE_LIST +status_t +_VECTOR_CLASS_NAME::PushFront(const Value &value) +{ + return Insert(value, 0); +} + +// PushBack +/*! \brief Inserts a copy of the supplied value at the end of the vector. + \param value The element to be inserted. + \return + - \c B_OK: Everything went fine. + - \c B_NO_MEMORY: Insufficient memory for this operation. +*/ +_VECTOR_TEMPLATE_LIST +status_t +_VECTOR_CLASS_NAME::PushBack(const Value &value) +{ + return Insert(value, fItemCount); +} + +// PopFront +/*! \brief Removes the first element of the vector. + + Invocation on an empty vector is harmless. +*/ +_VECTOR_TEMPLATE_LIST +void +_VECTOR_CLASS_NAME::PopFront() +{ + if (fItemCount > 0) + Erase(0); +} + +// PopBack +/*! \brief Removes the last element of the vector. + + Invocation on an empty vector is harmless. +*/ +_VECTOR_TEMPLATE_LIST +void +_VECTOR_CLASS_NAME::PopBack() +{ + if (fItemCount > 0) + Erase(fItemCount - 1); +} + +// _MoveItems +/*! \brief Moves elements within an array. + \param items The elements to be moved. + \param offset The index to which the elements shall be moved. May be + negative. + \param count The number of elements to be moved. +*/ +_VECTOR_TEMPLATE_LIST +inline +void +_VECTOR_CLASS_NAME::_MoveItems(Value* items, int32 offset, int32 count) +{ + if (count > 0 && offset != 0) + memmove(items + offset, items, count * sizeof(Value)); +} + +// Insert +/*! \brief Inserts a copy of the the supplied value at the given index. + \param value The value to be inserted. + \param index The index at which to insert the new element. It must + hold: 0 <= \a index <= Count(). + \return + - \c B_OK: Everything went fine. + - \c B_BAD_VALUE: \a index is out of range. + - \c B_NO_MEMORY: Insufficient memory for this operation. +*/ +_VECTOR_TEMPLATE_LIST +status_t +_VECTOR_CLASS_NAME::Insert(const Value &value, int32 index) +{ + if (index < 0 || index > fItemCount) + return B_BAD_VALUE; + if (!_Resize(fItemCount + 1)) + return B_NO_MEMORY; + _MoveItems(fItems + index, 1, fItemCount - index - 1); + new(fItems + index) Value(value); + return B_OK; +} + +// Insert +/*! \brief Inserts a copy of the the supplied value at the given position. + \param value The value to be inserted. + \param iterator An iterator specifying the position at which to insert + the new element. + \return + - \c B_OK: Everything went fine. + - \c B_BAD_VALUE: \a iterator is is invalid. + - \c B_NO_MEMORY: Insufficient memory for this operation. +*/ +_VECTOR_TEMPLATE_LIST +status_t +_VECTOR_CLASS_NAME::Insert(const Value &value, const Iterator &iterator) +{ + int32 index = _IteratorIndex(iterator); + if (index >= 0) + return Insert(value, index); + return B_BAD_VALUE; +} + +// Remove +/*! \brief Removes all elements of the supplied value. + \param value The value of the elements to be removed. + \return The number of removed occurrences. +*/ +_VECTOR_TEMPLATE_LIST +int32 +_VECTOR_CLASS_NAME::Remove(const Value &value) +{ + int32 count = 0; + for (int32 i = fItemCount - 1; i >= 0; i--) { + if (ElementAt(i) == value) { + Erase(i); + count++; + } + } + return count; +} + +// Erase +/*! \brief Removes the element at the given index. + \param index The position of the element to be removed. + \return An iterator referring to the element now being located at index + \a index (End(), if it was the last element that has been + removed), or Null(), if \a index was out of range. +*/ +_VECTOR_TEMPLATE_LIST +_VECTOR_CLASS_NAME::Iterator +_VECTOR_CLASS_NAME::Erase(int32 index) +{ + if (index >= 0 && index < fItemCount) { + fItems[index].~Value(); + _MoveItems(fItems + index + 1, -1, fItemCount - index - 1); + _Resize(fItemCount - 1); + return Iterator(fItems + index); + } + return Null(); +} + +// Erase +/*! \brief Removes the element at the given position. + \param iterator An iterator referring to the element to be removed. + \return An iterator referring to the element succeeding the removed + one (End(), if it was the last element that has been + removed), or Null(), if \a iterator was an invalid iterator + (in this case including End()). +*/ +_VECTOR_TEMPLATE_LIST +_VECTOR_CLASS_NAME::Iterator +_VECTOR_CLASS_NAME::Erase(const Iterator &iterator) +{ + int32 index = _IteratorIndex(iterator); + if (index >= 0 && index < fItemCount) + return Erase(index); + return Null(); +} + +// Count +/*! \brief Returns the number of elements the vector contains. + \return The number of elements the vector contains. +*/ +_VECTOR_TEMPLATE_LIST +inline +int32 +_VECTOR_CLASS_NAME::Count() const +{ + return fItemCount; +} + +// IsEmpty +/*! \brief Returns whether the vector is empty. + \return \c true, if the vector is empty, \c false otherwise. +*/ +_VECTOR_TEMPLATE_LIST +inline +bool +_VECTOR_CLASS_NAME::IsEmpty() const +{ + return (fItemCount == 0); +} + +// MakeEmpty +/*! \brief Removes all elements from the vector. +*/ +_VECTOR_TEMPLATE_LIST +void +_VECTOR_CLASS_NAME::MakeEmpty() +{ + for (int32 i = 0; i < fItemCount; i++) + fItems[i].~Value(); + _Resize(0); +} + +// Begin +/*! \brief Returns an iterator referring to the beginning of the vector. + + If the vector is not empty, Begin() refers to its first element, + otherwise it is equal to End() and must not be dereferenced! + + \return An iterator referring to the beginning of the vector. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::Iterator +_VECTOR_CLASS_NAME::Begin() +{ + return Iterator(fItems); +} + +// Begin +/*! \brief Returns an iterator referring to the beginning of the vector. + + If the vector is not empty, Begin() refers to its first element, + otherwise it is equal to End() and must not be dereferenced! + + \return An iterator referring to the beginning of the vector. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::ConstIterator +_VECTOR_CLASS_NAME::Begin() const +{ + return ConstIterator(fItems); +} + +// End +/*! \brief Returns an iterator referring to the end of the vector. + + The position identified by End() is the one succeeding the last + element, i.e. it must not be dereferenced! + + \return An iterator referring to the end of the vector. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::Iterator +_VECTOR_CLASS_NAME::End() +{ + return Iterator(fItems + fItemCount); +} + +// End +/*! \brief Returns an iterator referring to the end of the vector. + + The position identified by End() is the one succeeding the last + element, i.e. it must not be dereferenced! + + \return An iterator referring to the end of the vector. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::ConstIterator +_VECTOR_CLASS_NAME::End() const +{ + return ConstIterator(fItems + fItemCount); +} + +// Null +/*! \brief Returns an invalid iterator. + + Null() is used as a return value, if something went wrong. It must + neither be incremented or decremented nor dereferenced! + + \return An invalid iterator. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::Iterator +_VECTOR_CLASS_NAME::Null() +{ + return Iterator(NULL); +} + +// Null +/*! \brief Returns an invalid iterator. + + Null() is used as a return value, if something went wrong. It must + neither be incremented or decremented nor dereferenced! + + \return An invalid iterator. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::ConstIterator +_VECTOR_CLASS_NAME::Null() const +{ + return ConstIterator(NULL); +} + +// IteratorForIndex +/*! \brief Returns an iterator for a given index. + \return An iterator referring to the same element as \a index, or + End(), if \a index is out of range. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::Iterator +_VECTOR_CLASS_NAME::IteratorForIndex(int32 index) +{ + if (index >= 0 && index <= fItemCount) + return Iterator(fItems + index); + return End(); +} + +// IteratorForIndex +/*! \brief Returns an iterator for a given index. + \return An iterator referring to the same element as \a index, or + End(), if \a index is out of range. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::ConstIterator +_VECTOR_CLASS_NAME::IteratorForIndex(int32 index) const +{ + if (index >= 0 && index <= fItemCount) + return ConstIterator(fItems + index); + return End(); +} + +// ElementAt +/*! \brief Returns the element at a given index. + \param index The index identifying the element to be returned. + \return The element identified by the given index. +*/ +_VECTOR_TEMPLATE_LIST +inline +const Value & +_VECTOR_CLASS_NAME::ElementAt(int32 index) const +{ + if (index >= 0 && index < fItemCount) + return fItems[index]; + // Return the 0th element by default. Unless the allocation failed, there + // is always a 0th element -- uninitialized perhaps. + return fItems[0]; +} + +// ElementAt +/*! \brief Returns the element at a given index. + \param index The index identifying the element to be returned. + \return The element identified by the given index. +*/ +_VECTOR_TEMPLATE_LIST +inline +Value & +_VECTOR_CLASS_NAME::ElementAt(int32 index) +{ + if (index >= 0 && index < fItemCount) + return fItems[index]; + // Return the 0th element by default. Unless the allocation failed, there + // is always a 0th element -- uninitialized perhaps. + return fItems[0]; +} + +// IndexOf +/*! \brief Returns the index of the next element with the specified value. + \param value The value of the element to be found. + \param start The index at which to be started to search for the element. + \return The index of the found element, or \c -1, if no further element + with the given value could be found or \a index is out of range. +*/ +_VECTOR_TEMPLATE_LIST +int32 +_VECTOR_CLASS_NAME::IndexOf(const Value &value, int32 start) const +{ + if (start >= 0) { + for (int32 i = start; i < fItemCount; i++) { + if (fItems[i] == value) + return i; + } + } + return -1; +} + +// Find +/*! \brief Returns an iterator referring to the next element with the + specified value. + \param value The value of the element to be found. + \return An iterator referring to the found element, or End(), if no + further with the given value could be found. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::Iterator +_VECTOR_CLASS_NAME::Find(const Value &value) +{ + return Find(value, Begin()); +} + +// Find +/*! \brief Returns an iterator referring to the next element with the + specified value. + \param value The value of the element to be found. + \param start And iterator specifying where to start searching for the + element. + \return An iterator referring to the found element, or End(), if no + further with the given value could be found or \a start was + invalid. +*/ +_VECTOR_TEMPLATE_LIST +_VECTOR_CLASS_NAME::Iterator +_VECTOR_CLASS_NAME::Find(const Value &value, const Iterator &start) +{ + int32 index = IndexOf(value, _IteratorIndex(start)); + if (index >= 0) + return Iterator(fItems + index); + return End(); +} + +// Find +/*! \brief Returns an iterator referring to the of the next element with the + specified value. + \param value The value of the element to be found. + \return An iterator referring to the found element, or End(), if no + further with the given value could be found. +*/ +_VECTOR_TEMPLATE_LIST +inline +_VECTOR_CLASS_NAME::ConstIterator +_VECTOR_CLASS_NAME::Find(const Value &value) const +{ + return Find(value, Begin()); +} + +// Find +/*! \brief Returns an iterator referring to the of the next element with the + specified value. + \param value The value of the element to be found. + \param start And iterator specifying where to start searching for the + element. + \return An iterator referring to the found element, or End(), if no + further with the given value could be found or \a start was + invalid. +*/ +_VECTOR_TEMPLATE_LIST +_VECTOR_CLASS_NAME::ConstIterator +_VECTOR_CLASS_NAME::Find(const Value &value, const ConstIterator &start) const +{ + int32 index = IndexOf(value, _IteratorIndex(start)); + if (index >= 0) + return ConstIterator(fItems + index); + return End(); +} + +// [] +/*! \brief Semantically equivalent to ElementAt(). +*/ +_VECTOR_TEMPLATE_LIST +inline +Value & +_VECTOR_CLASS_NAME::operator[](int32 index) +{ + return ElementAt(index); +} + +// [] +/*! \brief Semantically equivalent to ElementAt(). +*/ +_VECTOR_TEMPLATE_LIST +inline +const Value & +_VECTOR_CLASS_NAME::operator[](int32 index) const +{ + return ElementAt(index); +} + +// _Resize +/*! \brief Resizes the vector. + + The internal element array will be grown or shrunk to the next multiple + of \a fChunkSize >= \a count, but no less than \a fChunkSize. + + Also adjusts \a fItemCount according to the supplied \a count, but does + not invoke a destructor or constructor on any element. + + \param count The number of element. + \return \c true, if everything went fine, \c false, if the memory + allocation failed. +*/ +_VECTOR_TEMPLATE_LIST +bool +_VECTOR_CLASS_NAME::_Resize(size_t count) +{ + bool result = true; + // calculate the new capacity + int32 newSize = count; + if (newSize <= 0) + newSize = 1; + newSize = ((newSize - 1) / fChunkSize + 1) * fChunkSize; + // resize if necessary + if ((size_t)newSize != fCapacity) { + Value* newItems = (Value*)realloc(fItems, newSize * sizeof(Value)); + if (newItems) { + fItems = newItems; + fCapacity = newSize; + } else + result = false; + } + if (result) + fItemCount = count; + return result; +} + +// _IteratorIndex +/*! \brief Returns index of the element the supplied iterator refers to. + \return The index of the element the supplied iterator refers to, or + \c -1, if the iterator is invalid (End() is considered valid + here, and Count() is returned). +*/ +_VECTOR_TEMPLATE_LIST +inline +int32 +_VECTOR_CLASS_NAME::_IteratorIndex(const Iterator &iterator) const +{ + if (iterator.Element()) { + int32 index = iterator.Element() - fItems; + if (index >= 0 && index <= fItemCount) + return index; + } + return -1; +} + +// _IteratorIndex +/*! \brief Returns index of the element the supplied iterator refers to. + \return The index of the element the supplied iterator refers to, or + \c -1, if the iterator is invalid (End() is considered valid + here, and Count() is returned). +*/ +_VECTOR_TEMPLATE_LIST +inline +int32 +_VECTOR_CLASS_NAME::_IteratorIndex(const ConstIterator &iterator) const +{ + if (iterator.Element()) { + int32 index = iterator.Element() - fItems; + if (index >= 0 && index <= fItemCount) + return index; + } + return -1; +} + +#endif // _VECTOR_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/FileSystem.cpp b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/FileSystem.cpp new file mode 100644 index 0000000000..0ef2442d34 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/FileSystem.cpp @@ -0,0 +1,359 @@ +// FileSystem.cpp + +#include "AutoLocker.h" +#include "Compatibility.h" +#include "Debug.h" +#include "FileSystem.h" +#include "HashMap.h" +#include "KernelRequestHandler.h" +#include "PortReleaser.h" +#include "RequestAllocator.h" +#include "RequestPort.h" +#include "Requests.h" +#include "Settings.h" +#include "SingleReplyRequestHandler.h" +#include "Volume.h" + +// The time after which the notification thread times out at the port and +// restarts the loop. Of interest only when the FS is deleted. It is the +// maximal time the destructor has to wait for the thread. +static const bigtime_t kNotificationRequestTimeout = 50000; // 50 ms + +// SelectSyncMap +struct FileSystem::SelectSyncMap + : public SynchronizedHashMap, int32*> { +}; + +// constructor +FileSystem::FileSystem(const char* name, RequestPort* initPort, status_t* error) + : LazyInitializable(), + Referencable(), + fVolumes(), + fVolumeLock(), + fName(name), + fInitPort(initPort), + fNotificationPort(NULL), + fNotificationThread(-1), + fPortPool(), + fSelectSyncs(NULL), + fSettings(NULL), + fUserlandServerTeam(-1), + fTerminating(false) +{ + if (error) + *error = (fName.GetLength() == 0 ? B_NO_MEMORY : B_OK); +} + +// destructor +FileSystem::~FileSystem() +{ + fTerminating = true; + // wait for the notification thread to terminate + if (fNotificationThread >= 0) { + int32 result; + wait_for_thread(fNotificationThread, &result); + } + // delete our data structures + if (fSelectSyncs) { + for (SelectSyncMap::Iterator it = fSelectSyncs->GetIterator(); + it.HasNext();) { + SelectSyncMap::Entry entry = it.Next(); + delete entry.value; + } + delete fSelectSyncs; + } + delete fSettings; +} + +// GetName +const char* +FileSystem::GetName() const +{ + return fName.GetString(); +} + +// GetPortPool +RequestPortPool* +FileSystem::GetPortPool() +{ + return &fPortPool; +} + +// Mount +status_t +FileSystem::Mount(nspace_id id, const char* device, ulong flags, + const char* parameters, int32 len, Volume** _volume) +{ + // check initialization and parameters + if (InitCheck() != B_OK) + return InitCheck(); + if (!_volume) + return B_BAD_VALUE; + // create volume + Volume* volume = new(nothrow) Volume(this, id); + if (!volume) + return B_NO_MEMORY; + // add volume to the volume list + fVolumeLock.Lock(); + status_t error = fVolumes.PushBack(volume); + fVolumeLock.Unlock(); + if (error != B_OK) + return error; + // mount volume + error = volume->Mount(device, flags, parameters, len); + if (error != B_OK) { + fVolumeLock.Lock(); + fVolumes.Remove(volume); + fVolumeLock.Unlock(); + volume->RemoveReference(); + return error; + } + *_volume = volume; + return error; +} + +// Initialize +status_t +FileSystem::Initialize(const char* deviceName, const char* parameters, + size_t len) +{ + // get a free port + RequestPort* port = fPortPool.AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(&fPortPool, port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + MountVolumeRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + error = allocator.AllocateString(request->device, deviceName); + if (error == B_OK) + error = allocator.AllocateData(request->parameters, parameters, len, 1); + if (error != B_OK) + return error; + // send the request + SingleReplyRequestHandler handler(MOUNT_VOLUME_REPLY); + InitializeVolumeReply* reply; + error = port->SendRequest(&allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// VolumeUnmounted +void +FileSystem::VolumeUnmounted(Volume* volume) +{ + fVolumeLock.Lock(); + fVolumes.Remove(volume); + fVolumeLock.Unlock(); +} + +// GetVolume +Volume* +FileSystem::GetVolume(nspace_id id) +{ + AutoLocker _(fVolumeLock); + for (Vector::Iterator it = fVolumes.Begin(); + it != fVolumes.End(); + it++) { + Volume* volume = *it; + if (volume->GetID() == id) { + volume->AddReference(); + return volume; + } + } + return NULL; +} + +// GetIOCtlInfo +const IOCtlInfo* +FileSystem::GetIOCtlInfo(int command) const +{ + return (fSettings ? fSettings->GetIOCtlInfo(command) : NULL); +} + +// AddSelectSyncEntry +status_t +FileSystem::AddSelectSyncEntry(selectsync* sync) +{ + AutoLocker _(fSelectSyncs); + int32* count = fSelectSyncs->Get(sync); + if (!count) { + count = new(nothrow) int32(0); + if (!count) + return B_NO_MEMORY; + status_t error = fSelectSyncs->Put(sync, count); + if (error != B_OK) { + delete count; + return error; + } + } + (*count)++; + return B_OK; +} + +// RemoveSelectSyncEntry +void +FileSystem::RemoveSelectSyncEntry(selectsync* sync) +{ + AutoLocker _(fSelectSyncs); + if (int32* count = fSelectSyncs->Get(sync)) { + if (--(*count) <= 0) { + fSelectSyncs->Remove(sync); + delete count; + } + } +} + +// KnowsSelectSyncEntry +bool +FileSystem::KnowsSelectSyncEntry(selectsync* sync) +{ + return fSelectSyncs->ContainsKey(sync); +} + +// IsUserlandServerThread +bool +FileSystem::IsUserlandServerThread() const +{ + thread_info info; + get_thread_info(find_thread(NULL), &info); + return (info.team == fUserlandServerTeam); +} + +// FirstTimeInit +status_t +FileSystem::FirstTimeInit() +{ + if (fName.GetLength() == 0) + RETURN_ERROR(B_NO_MEMORY); + PRINT(("FileSystem::FirstTimeInit(): %s\n", fName.GetString())); + // create the select sync entry map + fSelectSyncs = new(nothrow) SelectSyncMap; + if (!fSelectSyncs) + return B_NO_MEMORY; + // prepare the request + RequestAllocator allocator(fInitPort->GetPort()); + FSConnectRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + RETURN_ERROR(error); + error = allocator.AllocateString(request->fsName, fName.GetString()); + if (error != B_OK) + RETURN_ERROR(error); + // send the request + SingleReplyRequestHandler handler(FS_CONNECT_REPLY); + FSConnectReply* reply; + error = fInitPort->SendRequest(&allocator, &handler, (Request**)&reply); + if (error != B_OK) + RETURN_ERROR(error); + RequestReleaser requestReleaser(fInitPort, reply); + // process the reply + if (reply->error != B_OK) + RETURN_ERROR(reply->error); + // get the port infos + int32 count = reply->portInfoCount; + if (count < 2) + RETURN_ERROR(B_BAD_DATA); + if (reply->portInfos.GetSize() != count * (int32)sizeof(Port::Info)) + RETURN_ERROR(B_BAD_DATA); + Port::Info* infos = (Port::Info*)reply->portInfos.GetData(); + // create the request ports + // the notification port + fNotificationPort = new(nothrow) RequestPort(infos); + if (!fNotificationPort) + RETURN_ERROR(B_NO_MEMORY); + error = fNotificationPort->InitCheck(); + if (error != B_OK) + return error; + // the other request ports + for (int32 i = 1; i < count; i++) { + RequestPort* port = new(nothrow) RequestPort(infos + i); + if (!port) + RETURN_ERROR(B_NO_MEMORY); + error = port->InitCheck(); + if (error == B_OK) + error = fPortPool.AddPort(port); + if (error != B_OK) { + delete port; + RETURN_ERROR(error); + } + } + // get the userland team + port_info portInfo; + error = get_port_info(infos[0].owner_port, &portInfo); + if (error != B_OK) + RETURN_ERROR(error); + fUserlandServerTeam = portInfo.team; + // print some info about the userland team + D( + PRINT((" userland team is: %ld\n", fUserlandServerTeam)); + int32 cookie = 0; + thread_info threadInfo; + while (get_next_thread_info(fUserlandServerTeam, &cookie, &threadInfo) + == B_OK) { + PRINT((" userland thread: %ld: `%s'\n", threadInfo.thread, + threadInfo.name)); + } + ); + // load the settings + fSettings = new(nothrow) Settings; + if (fSettings) { + status_t settingsError = fSettings->SetTo(fName.GetString()); + if (settingsError != B_OK) { + PRINT(("Failed to load settings: %s\n", strerror(settingsError))); + delete fSettings; + fSettings = NULL; + } else + fSettings->Dump(); + } else + ERROR(("Failed to allocate settings.\n")); + // spawn the notification thread + #if USER + fNotificationThread = spawn_thread(_NotificationThreadEntry, + "UFS notification thread", B_NORMAL_PRIORITY, this); + #else + fNotificationThread = spawn_kernel_thread(_NotificationThreadEntry, + "UFS notification thread", B_NORMAL_PRIORITY, this); + #endif + if (fNotificationThread < 0) + RETURN_ERROR(fNotificationThread); + resume_thread(fNotificationThread); + RETURN_ERROR(error); +} + +// _NotificationThreadEntry +int32 +FileSystem::_NotificationThreadEntry(void* data) +{ + return ((FileSystem*)data)->_NotificationThread(); +} + +// _NotificationThread +int32 +FileSystem::_NotificationThread() +{ + // process the notification requests until the FS is deleted + while (!fTerminating) { + if (fNotificationPort->InitCheck() != B_OK) + return fNotificationPort->InitCheck(); + KernelRequestHandler handler(this, NO_REQUEST); + fNotificationPort->HandleRequests(&handler, NULL, + kNotificationRequestTimeout); + } + // We eat all remaining notification requests, so that they aren't + // presented to the file system, when it is mounted next time. + // TODO: We should probably use a special handler that sends an ack reply, + // but ignores the requests otherwise. + KernelRequestHandler handler(this, NO_REQUEST); + fNotificationPort->HandleRequests(&handler, NULL, 0); + return 0; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/FileSystem.h b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/FileSystem.h new file mode 100644 index 0000000000..47fe5db665 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/FileSystem.h @@ -0,0 +1,80 @@ +// FileSystem.h + +#ifndef USERLAND_FS_FILE_SYSTEM_H +#define USERLAND_FS_FILE_SYSTEM_H + +#include + +#include "LazyInitializable.h" +#include "Locker.h" +#include "Referencable.h" +#include "RequestPortPool.h" +#include "String.h" +#include "Vector.h" + +namespace UserlandFSUtil { + +class RequestPort; + +} + +using UserlandFSUtil::RequestPort; + +struct IOCtlInfo; +class Settings; +class Volume; + +class FileSystem : public LazyInitializable, public Referencable { +public: + FileSystem(const char* name, + RequestPort* initPort, + status_t* error); + ~FileSystem(); + + const char* GetName() const; + + RequestPortPool* GetPortPool(); + + status_t Mount(nspace_id id, const char* device, + ulong flags, const char* parameters, + int32 len, Volume** volume); + status_t Initialize(const char* deviceName, + const char* parameters, size_t len); + void VolumeUnmounted(Volume* volume); + + Volume* GetVolume(nspace_id id); + + const IOCtlInfo* GetIOCtlInfo(int command) const; + + status_t AddSelectSyncEntry(selectsync* sync); + void RemoveSelectSyncEntry(selectsync* sync); + bool KnowsSelectSyncEntry(selectsync* sync); + + bool IsUserlandServerThread() const; + +protected: + virtual status_t FirstTimeInit(); + +private: + static int32 _NotificationThreadEntry(void* data); + int32 _NotificationThread(); + +private: + friend class KernelDebug; + struct SelectSyncEntry; + struct SelectSyncMap; + + Vector fVolumes; + Locker fVolumeLock; + String fName; + RequestPort* fInitPort; + RequestPort* fNotificationPort; + thread_id fNotificationThread; + RequestPortPool fPortPool; + SelectSyncMap* fSelectSyncs; + Settings* fSettings; + team_id fUserlandServerTeam; + volatile bool fTerminating; +}; + +#endif // USERLAND_FS_FILE_SYSTEM_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/IOCtlInfo.h b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/IOCtlInfo.h new file mode 100644 index 0000000000..86b846b4c7 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/IOCtlInfo.h @@ -0,0 +1,16 @@ +// IOCtlInfo.h + +#ifndef USERLAND_FS_IOCTL_INFO_H +#define USERLAND_FS_IOCTL_INFO_H + +#include + +// IOCtlInfo +struct IOCtlInfo { + int command; + bool isBuffer; + int32 bufferSize; + int32 writeBufferSize; +}; + +#endif // USERLAND_FS_IOCTL_INFO_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Jamfile b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Jamfile new file mode 100644 index 0000000000..dba89734f4 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Jamfile @@ -0,0 +1,57 @@ +SubDir HAIKU_TOP src tests add-ons kernel file_systems userlandfs r5 src + kernel_add_on ; + +SetSubDirSupportedPlatforms r5 bone dano ; + +local userlandFSTop = [ FDirName $(HAIKU_TOP) src tests add-ons kernel + file_systems userlandfs r5 ] ; +local userlandFSIncludes = [ FDirName $(userlandFSTop) headers ] ; + +SubDirSysHdrs [ FDirName $(userlandFSIncludes) public ] ; +SubDirHdrs [ FDirName $(userlandFSIncludes) private ] ; +SubDirHdrs [ FDirName $(userlandFSIncludes) shared ] ; + +SEARCH_SOURCE += [ FDirName $(userlandFSTop) src private ] ; +SEARCH_SOURCE += [ FDirName $(userlandFSTop) src shared ] ; + +DEFINES += DEBUG_APP="\\\"userlandfs\\\"" ; + +local kernelC++ ; +if $(OSPLAT) = X86 { + kernelC++ += kernel-cpp.cpp ; + SubDirC++Flags -include [ FDirName $(SUBDIR) kernel-cpp.h ] ; +} + +KernelAddon userlandfs + : AreaSupport.cpp + Debug.cpp + DispatcherDefs.cpp + LazyInitializable.cpp + Locker.cpp + ObjectTracker.cpp + Port.cpp + Referencable.cpp + Request.cpp + RequestAllocator.cpp + RequestHandler.cpp + RequestPort.cpp + RequestPortPool.cpp + Requests.cpp + SingleReplyRequestHandler.cpp + String.cpp + userlandfs_ioctl.cpp + + FileSystem.cpp + kernel_interface.cpp + KernelDebug.cpp + KernelRequestHandler.cpp + Settings.cpp + UserlandFS.cpp + Volume.cpp + + $(kernelC++) + + : $(HAIKU_GCC_LIBGCC) + # TARGET_GCC_LIBGCC is not defined for TARGET_PLATFORM != haiku, + # but the compiler is the same in this case anyway. +; diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelDebug.cpp b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelDebug.cpp new file mode 100644 index 0000000000..ae443549ce --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelDebug.cpp @@ -0,0 +1,109 @@ +// KernelDebug.cpp + +#include + +#include "Debug.h" +#include "FileSystem.h" +#include "KernelDebug.h" +#include "RequestPort.h" +#include "RequestPortPool.h" +#include "UserlandFS.h" +#include "Volume.h" + +static vint32 sCommandsAdded = 0; + +// DebugUFS +int +KernelDebug::DebugUFS(int argc, char** argv) +{ + typedef HashMap KDebugFSMap; + UserlandFS* userlandFS = UserlandFS::GetUserlandFS(); + KDebugFSMap& fileSystems = userlandFS->fFileSystems->GetUnsynchronizedMap(); + for (KDebugFSMap::Iterator it = fileSystems.GetIterator(); + it.HasNext();) { + KDebugFSMap::Entry entry = it.Next(); + FileSystem* fs = entry.value; + kprintf("file system %p: %s\n", fs, fs->GetName()); + kprintf(" port pool %p\n", fs->GetPortPool()); + int32 volumeCount = fs->fVolumes.Count(); + for (int32 i = 0; i < volumeCount; i++) { + Volume* volume = fs->fVolumes.ElementAt(i); + kprintf(" volume %p: %ld\n", volume, volume->GetID()); + } + } + return 0; +} + +// DebugPortPool +int +KernelDebug::DebugPortPool(int argc, char** argv) +{ + if (argc < 2) { + kprintf("usage: ufs_portpool \n"); + return 0; + } + RequestPortPool *portPool = (RequestPortPool*)parse_expression(argv[1]); + kprintf("free ports:\n"); + for (int32 i = 0; i < portPool->fFreePorts; i++) { + kprintf(" port %p\n", portPool->fPorts[i].port); + } + kprintf("used ports:\n"); + for (int32 i = portPool->fFreePorts; i < portPool->fPortCount; i++) { + kprintf(" port %p, owner: %ld, count: %ld\n", portPool->fPorts[i].port, + portPool->fPorts[i].owner, portPool->fPorts[i].count); + } + return 0; +} + +// DebugPort +int +KernelDebug::DebugPort(int argc, char** argv) +{ + if (argc < 2) { + kprintf("usage: ufs_port \n"); + return 0; + } + RequestPort *port = (RequestPort*)parse_expression(argv[1]); + kprintf("port %p:\n", port); + kprintf(" status : %lx\n", port->fPort.fInitStatus); + kprintf(" is owner : %d\n", port->fPort.fOwner); + kprintf(" owner port: %ld\n", port->fPort.fInfo.owner_port); + kprintf(" client port: %ld\n", port->fPort.fInfo.client_port); + kprintf(" size: %ld\n", port->fPort.fInfo.size); + kprintf(" capacity: %ld\n", port->fPort.fCapacity); + kprintf(" message size: %ld\n", port->fPort.fMessageSize); + kprintf(" buffer: %p\n", port->fPort.fBuffer); + return 0; +} + +// #pragma mark - + +// AddDebuggerCommands +void +KernelDebug::AddDebuggerCommands() +{ + if (atomic_add(&sCommandsAdded, 1) > 0) + return; + PRINT(("KernelDebug::AddDebuggerCommands(): adding debugger commands\n")); + add_debugger_command("ufs", DebugUFS, "prints general info about " + "userland FS"); + add_debugger_command("ufs_portpool", DebugPortPool, + "ufs_portpool - prints info about a " + "userland FS port pool"); + add_debugger_command("ufs_port", DebugPort, + "ufs_port - prints info about a userland FS port"); +} + +// RemoveDebuggerCommands +void +KernelDebug::RemoveDebuggerCommands() +{ + if (atomic_add(&sCommandsAdded, -1) > 1) + return; + PRINT(("KernelDebug::RemoveDebuggerCommands(): removing debugger " + "commands\n")); + remove_debugger_command("ufs_port", DebugPort); + remove_debugger_command("ufs_portpool", DebugPortPool); + remove_debugger_command("ufs", DebugUFS); +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelDebug.h b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelDebug.h new file mode 100644 index 0000000000..f7d2525730 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelDebug.h @@ -0,0 +1,23 @@ +// KernelDebug.h + +#ifndef USERLAND_FS_KERNEL_DEBUG_H +#define USERLAND_FS_KERNEL_DEBUG_H + +class KernelDebug { +public: + static void AddDebuggerCommands(); + static void RemoveDebuggerCommands(); + +private: + static int DebugUFS(int argc, char** argv); + static int DebugPortPool(int argc, char** argv); + static int DebugPort(int argc, char** argv); +}; + +// no kernel debugger commands in userland +#if USER +inline void KernelDebug::AddDebuggerCommands() {} +inline void KernelDebug::RemoveDebuggerCommands() {} +#endif + +#endif // USERLAND_FS_KERNEL_DEBUG_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelRequestHandler.cpp b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelRequestHandler.cpp new file mode 100644 index 0000000000..9049a5b444 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelRequestHandler.cpp @@ -0,0 +1,355 @@ +// KernelRequestHandler.cpp + +#include "Compatibility.h" +#include "Debug.h" +#include "FileSystem.h" +#include "KernelRequestHandler.h" +#include "RequestPort.h" +#include "Requests.h" +#include "Volume.h" + +// VolumePutter +class VolumePutter { +public: + VolumePutter(Volume* volume) : fVolume(volume) {} + ~VolumePutter() + { + if (fVolume) + fVolume->RemoveReference(); + } + +private: + Volume *fVolume; +}; + +// constructor +KernelRequestHandler::KernelRequestHandler(Volume* volume, uint32 expectedReply) + : RequestHandler(), + fFileSystem(volume->GetFileSystem()), + fVolume(volume), + fExpectedReply(expectedReply) +{ +} + +// constructor +KernelRequestHandler::KernelRequestHandler(FileSystem* fileSystem, + uint32 expectedReply) + : RequestHandler(), + fFileSystem(fileSystem), + fVolume(NULL), + fExpectedReply(expectedReply) +{ +} + +// destructor +KernelRequestHandler::~KernelRequestHandler() +{ +} + +// HandleRequest +status_t +KernelRequestHandler::HandleRequest(Request* request) +{ + if (request->GetType() == fExpectedReply) { + fDone = true; + return B_OK; + } + switch (request->GetType()) { + // notifications + case NOTIFY_LISTENER_REQUEST: + return _HandleRequest((NotifyListenerRequest*)request); + case NOTIFY_SELECT_EVENT_REQUEST: + return _HandleRequest((NotifySelectEventRequest*)request); + case SEND_NOTIFICATION_REQUEST: + return _HandleRequest((SendNotificationRequest*)request); + // vnodes + case GET_VNODE_REQUEST: + return _HandleRequest((GetVNodeRequest*)request); + case PUT_VNODE_REQUEST: + return _HandleRequest((PutVNodeRequest*)request); + case NEW_VNODE_REQUEST: + return _HandleRequest((NewVNodeRequest*)request); + case REMOVE_VNODE_REQUEST: + return _HandleRequest((RemoveVNodeRequest*)request); + case UNREMOVE_VNODE_REQUEST: + return _HandleRequest((UnremoveVNodeRequest*)request); + case IS_VNODE_REMOVED_REQUEST: + return _HandleRequest((IsVNodeRemovedRequest*)request); + } +PRINT(("KernelRequestHandler::HandleRequest(): unexpected request: %lu\n", +request->GetType())); + return B_BAD_DATA; +} + +// #pragma mark - +// #pragma mark ----- notifications ----- + +// _HandleRequest +status_t +KernelRequestHandler::_HandleRequest(NotifyListenerRequest* request) +{ + // check and executed the request + status_t result = B_OK; + if (fVolume && request->nsid != fVolume->GetID()) + result = B_BAD_VALUE; + // check the name + char* name = (char*)request->name.GetData(); + int32 nameLen = request->name.GetSize(); + if (name && (nameLen <= 0 || strnlen(name, nameLen) < 1)) + name = NULL; + else if (name) + name[nameLen - 1] = '\0'; + if (!name) { + switch (request->operation) { + case B_ENTRY_CREATED: + case B_ENTRY_MOVED: + case B_ATTR_CHANGED: + ERROR(("notify_listener(): NULL name for opcode: %ld\n", + request->operation)); + result = B_BAD_VALUE; + break; + case B_ENTRY_REMOVED: + case B_STAT_CHANGED: + break; + } + } + // execute the request + if (result == B_OK) { + PRINT(("notify_listener(%ld, %ld, %Ld, %Ld, %Ld, `%s')\n", + request->operation, request->nsid, request->vnida, request->vnidb, + request->vnidc, name)); + result = notify_listener(request->operation, request->nsid, + request->vnida, request->vnidb, request->vnidc, name); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + NotifyListenerReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + return error; + reply->error = result; + // send the reply + return fPort->SendRequest(&allocator); +} + +// _HandleRequest +status_t +KernelRequestHandler::_HandleRequest(NotifySelectEventRequest* request) +{ + // check and executed the request + status_t result = B_OK; + if (fFileSystem->KnowsSelectSyncEntry(request->sync)) { + PRINT(("notify_select_event(%p, %lu)\n", request->sync, request->ref)); + notify_select_event(request->sync, request->ref); + } else + result = B_BAD_VALUE; + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + NotifySelectEventReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + return error; + reply->error = result; + // send the reply + return fPort->SendRequest(&allocator); +} + +// _HandleRequest +status_t +KernelRequestHandler::_HandleRequest(SendNotificationRequest* request) +{ + // check and executed the request + status_t result = B_OK; + if (fVolume && request->nsida != fVolume->GetID() + && request->nsidb != fVolume->GetID()) { + result = B_BAD_VALUE; + } + // check the name + char* name = (char*)request->name.GetData(); + int32 nameLen = request->name.GetSize(); + if (name && (nameLen <= 0 || strnlen(name, nameLen) < 1)) + name = NULL; + else if (name) + name[nameLen - 1] = '\0'; + if (!name) { + switch (request->operation) { + case B_ENTRY_CREATED: + case B_ENTRY_MOVED: + ERROR(("send_notification(): NULL name for opcode: %ld\n", + request->operation)); + result = B_BAD_VALUE; + break; + case B_ENTRY_REMOVED: + break; + } + } + // execute the request + if (result == B_OK) { + PRINT(("send_notification(%ld, %ld, %lu, %ld, %ld, %ld, %Ld, %Ld, %Ld, " + "`%s')\n", request->port, request->token, request->what, + request->operation, request->nsida, request->nsidb, request->vnida, + request->vnidb, request->vnidc, name)); + result = send_notification(request->port, request->token, request->what, + request->operation, request->nsida, request->nsidb, request->vnida, + request->vnidb, request->vnidc, name); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + SendNotificationReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + return error; + reply->error = result; + // send the reply + return fPort->SendRequest(&allocator); +} + +// #pragma mark - +// #pragma mark ----- vnodes ----- + +// _HandleRequest +status_t +KernelRequestHandler::_HandleRequest(GetVNodeRequest* request) +{ + // check and executed the request + Volume* volume = NULL; + status_t result = _GetVolume(request->nsid, &volume); + VolumePutter _(volume); + void* node; + if (result == B_OK) + result = volume->GetVNode(request->vnid, &node); + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + GetVNodeReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + return error; + reply->error = result; + reply->node = node; + // send the reply + return fPort->SendRequest(&allocator); +} + +// _HandleRequest +status_t +KernelRequestHandler::_HandleRequest(PutVNodeRequest* request) +{ + // check and executed the request + Volume* volume = NULL; + status_t result = _GetVolume(request->nsid, &volume); + VolumePutter _(volume); + if (result == B_OK) + result = volume->PutVNode(request->vnid); + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + PutVNodeReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + return error; + reply->error = result; + // send the reply + return fPort->SendRequest(&allocator); +} + +// _HandleRequest +status_t +KernelRequestHandler::_HandleRequest(NewVNodeRequest* request) +{ + // check and executed the request + Volume* volume = NULL; + status_t result = _GetVolume(request->nsid, &volume); + VolumePutter _(volume); + if (result == B_OK) + result = volume->NewVNode(request->vnid, request->node); + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + NewVNodeReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + return error; + reply->error = result; + // send the reply + return fPort->SendRequest(&allocator); +} + +// _HandleRequest +status_t +KernelRequestHandler::_HandleRequest(RemoveVNodeRequest* request) +{ + // check and executed the request + Volume* volume = NULL; + status_t result = _GetVolume(request->nsid, &volume); + VolumePutter _(volume); + if (result == B_OK) + result = volume->RemoveVNode(request->vnid); + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + RemoveVNodeReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + return error; + reply->error = result; + // send the reply + return fPort->SendRequest(&allocator); +} + +// _HandleRequest +status_t +KernelRequestHandler::_HandleRequest(UnremoveVNodeRequest* request) +{ + // check and executed the request + Volume* volume = NULL; + status_t result = _GetVolume(request->nsid, &volume); + VolumePutter _(volume); + if (result == B_OK) + result = volume->UnremoveVNode(request->vnid); + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + UnremoveVNodeReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + return error; + reply->error = result; + // send the reply + return fPort->SendRequest(&allocator); +} + +// _HandleRequest +status_t +KernelRequestHandler::_HandleRequest(IsVNodeRemovedRequest* request) +{ + // check and executed the request + Volume* volume = NULL; + status_t result = _GetVolume(request->nsid, &volume); + VolumePutter _(volume); + if (result == B_OK) + result = volume->IsVNodeRemoved(request->vnid); + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + IsVNodeRemovedReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + return error; + reply->error = (result < 0 ? result : B_OK); + reply->result = result; + // send the reply + return fPort->SendRequest(&allocator); +} + +// _GetVolume +status_t +KernelRequestHandler::_GetVolume(nspace_id id, Volume** volume) +{ + if (fVolume) { + if (fVolume->GetID() != id) { + *volume = NULL; + return B_BAD_VALUE; + } + fVolume->AddReference(); + *volume = fVolume; + return B_OK; + } + *volume = fFileSystem->GetVolume(id); + return (*volume ? B_OK : B_BAD_VALUE); +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelRequestHandler.h b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelRequestHandler.h new file mode 100644 index 0000000000..dd5ed99ed7 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelRequestHandler.h @@ -0,0 +1,70 @@ +// KernelRequestHandler.h + +#ifndef USERLAND_FS_KERNEL_REQUEST_HANDLER_H +#define USERLAND_FS_KERNEL_REQUEST_HANDLER_H + +#include + +#include "RequestHandler.h" + +namespace UserlandFSUtil { + +class GetVNodeRequest; +class IsVNodeRemovedRequest; +class NewVNodeRequest; +class NotifyListenerRequest; +class NotifySelectEventRequest; +class PutVNodeRequest; +class RemoveVNodeRequest; +class SendNotificationRequest; +class UnremoveVNodeRequest; + +} + +using UserlandFSUtil::GetVNodeRequest; +using UserlandFSUtil::IsVNodeRemovedRequest; +using UserlandFSUtil::NewVNodeRequest; +using UserlandFSUtil::NotifyListenerRequest; +using UserlandFSUtil::NotifySelectEventRequest; +using UserlandFSUtil::PutVNodeRequest; +using UserlandFSUtil::RemoveVNodeRequest; +using UserlandFSUtil::SendNotificationRequest; +using UserlandFSUtil::UnremoveVNodeRequest; +using UserlandFSUtil::GetVNodeRequest; + +class Volume; + +class KernelRequestHandler : public RequestHandler { +public: + KernelRequestHandler(Volume* volume, + uint32 expectedReply); + KernelRequestHandler(FileSystem* fileSystem, + uint32 expectedReply); + virtual ~KernelRequestHandler(); + + virtual status_t HandleRequest(Request* request); + +private: + // notifications + status_t _HandleRequest(NotifyListenerRequest* request); + status_t _HandleRequest( + NotifySelectEventRequest* request); + status_t _HandleRequest( + SendNotificationRequest* request); + // vnodes + status_t _HandleRequest(GetVNodeRequest* request); + status_t _HandleRequest(PutVNodeRequest* request); + status_t _HandleRequest(NewVNodeRequest* request); + status_t _HandleRequest(RemoveVNodeRequest* request); + status_t _HandleRequest(UnremoveVNodeRequest* request); + status_t _HandleRequest(IsVNodeRemovedRequest* request); + + status_t _GetVolume(nspace_id id, Volume** volume); + +private: + FileSystem* fFileSystem; + Volume* fVolume; + uint32 fExpectedReply; +}; + +#endif // USERLAND_FS_KERNEL_REQUEST_HANDLER_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/PortReleaser.h b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/PortReleaser.h new file mode 100644 index 0000000000..7cb9b1d746 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/PortReleaser.h @@ -0,0 +1,29 @@ +// PortReleaser.h + +#ifndef USERLAND_FS_PORT_RELEASER_H +#define USERLAND_FS_PORT_RELEASER_H + +#include "FileSystem.h" +#include "RequestPortPool.h" + +// PortReleaser +class PortReleaser { +public: + PortReleaser(RequestPortPool* portPool, RequestPort* port) + : fPortPool(portPool), + fPort(port) + { + } + + ~PortReleaser() + { + if (fPort && fPortPool) + fPortPool->ReleasePort(fPort); + } + +private: + RequestPortPool* fPortPool; + RequestPort* fPort; +}; + +#endif // USERLAND_FS_PORT_RELEASER_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/RequestPortPool.cpp b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/RequestPortPool.cpp new file mode 100644 index 0000000000..2bb434795e --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/RequestPortPool.cpp @@ -0,0 +1,130 @@ +// RequestPortPool.cpp + +#include "AutoLocker.h" +#include "Debug.h" +#include "RequestPort.h" +#include "RequestPortPool.h" + +typedef AutoLocker PoolLocker; + +// constructor +RequestPortPool::RequestPortPool() + : fPorts(NULL), + fPortCount(0), + fFreePorts(0), + fFreePortSemaphore(-1), + fDisconnected(false) +{ + fFreePortSemaphore = create_sem(0, "request port pool"); +} + +// destructor +RequestPortPool::~RequestPortPool() +{ + delete_sem(fFreePortSemaphore); + free(fPorts); +} + +// InitCheck +status_t +RequestPortPool::InitCheck() const +{ + if (fFreePortSemaphore < 0) + return fFreePortSemaphore; + return B_OK; +} + +// IsDisconnected +bool +RequestPortPool::IsDisconnected() const +{ + return fDisconnected; +} + +// AddPort +status_t +RequestPortPool::AddPort(RequestPort* port) +{ + if (!port) + return B_BAD_VALUE; + PoolLocker _(this); + // resize the port array + PortAcquirationInfo* ports = (PortAcquirationInfo*)realloc(fPorts, + (fPortCount + 1) * sizeof(PortAcquirationInfo)); + if (!ports) + return B_NO_MEMORY; + fPorts = ports; + // add the port as used port and let AcquirePort() free it + fPorts[fPortCount].port = port; + fPorts[fPortCount].owner = -1; + fPorts[fPortCount].count = 1; + fPortCount++; + ReleasePort(port); + return B_OK; +} + +// AcquirePort +RequestPort* +RequestPortPool::AcquirePort() +{ + // first check whether the thread does already own a port + thread_id thread = find_thread(NULL); + { + PoolLocker _(this); + if (fDisconnected) + return NULL; + for (int32 i = fFreePorts; i < fPortCount; i++) { + PortAcquirationInfo& info = fPorts[i]; + if (info.owner == thread) { + info.count++; + return info.port; + } + } + } + // the thread doesn't own a port yet, find a free one + status_t error = acquire_sem(fFreePortSemaphore); + if (error != B_OK) + return NULL; + PoolLocker _(this); + if (fDisconnected) + return NULL; + if (fFreePorts < 1) { + FATAL(("Inconsistent request port pool: We acquired the free port " + "semaphore, but there are no free ports.\n")); + return NULL; + } + PortAcquirationInfo& info = fPorts[--fFreePorts]; + info.owner = find_thread(NULL); + info.count = 1; + return info.port; +} + +// ReleasePort +void +RequestPortPool::ReleasePort(RequestPort* port) +{ + if (!port) + return; + PoolLocker _(this); + // find the port + for (int32 i = fFreePorts; i < fPortCount; i++) { + PortAcquirationInfo& info = fPorts[i]; + if (info.port == port) { + if (--info.count == 0) { + // swap with first used port + if (i != fFreePorts) { + fPorts[i] = fPorts[fFreePorts]; + fPorts[fFreePorts].port = port; + } + fFreePorts++; + release_sem(fFreePortSemaphore); + } + if (port->InitCheck() != B_OK) + fDisconnected = true; + return; + } + } + WARN(("RequestPortPool::ReleasePort(%p): port not found\n", port)); + // Not found! +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/RequestPortPool.h b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/RequestPortPool.h new file mode 100644 index 0000000000..26326605f1 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/RequestPortPool.h @@ -0,0 +1,48 @@ +// RequestPortPool.h + +#ifndef USERLAND_FS_REQUEST_PORT_POOL_H +#define USERLAND_FS_REQUEST_PORT_POOL_H + +#include + +#include "Locker.h" + +namespace UserlandFSUtil { + +class RequestPort; + +} + +using UserlandFSUtil::RequestPort; + +class RequestPortPool : public Locker { +public: + RequestPortPool(); + ~RequestPortPool(); + + status_t InitCheck() const; + + bool IsDisconnected() const; + + status_t AddPort(RequestPort* port); + + RequestPort* AcquirePort(); + void ReleasePort(RequestPort* port); + +private: + friend class KernelDebug; + + struct PortAcquirationInfo { + RequestPort* port; + thread_id owner; + int32 count; + }; + + PortAcquirationInfo* fPorts; + int32 fPortCount; + int32 fFreePorts; + sem_id fFreePortSemaphore; + volatile bool fDisconnected; +}; + +#endif // USERLAND_FS_REQUEST_PORT_POOL_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Settings.cpp b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Settings.cpp new file mode 100644 index 0000000000..a826edb05f --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Settings.cpp @@ -0,0 +1,254 @@ +// Settings.cpp + +#include + +#include + +#include "Debug.h" +#include "HashMap.h" +#include "IOCtlInfo.h" +#include "Settings.h" + +static const char *kFSName = "userlandfs"; + +// IOCtlInfoMap +struct Settings::IOCtlInfoMap : public HashMap, IOCtlInfo*> { +}; + + +// _FindNextParameter +template +static +const driver_parameter * +_FindNextParameter(const container_t *container, const char *name, + int32 &cookie) +{ + const driver_parameter *parameter = NULL; + if (container) { + for (; !parameter && cookie < container->parameter_count; cookie++) { + const driver_parameter ¶m = container->parameters[cookie]; + if (!strcmp(param.name, name)) + parameter = ¶m; + } + } + return parameter; +} + +// _GetParameterValue +template +static +const char * +_GetParameterValue(const container_t *container, const char *name, + const char *unknownValue, const char *noArgValue) +{ + if (container) { + for (int32 i = container->parameter_count - 1; i >= 0; i--) { + const driver_parameter ¶m = container->parameters[i]; + if (!strcmp(param.name, name)) { + if (param.value_count > 0) + return param.values[0]; + return noArgValue; + } + } + } + return unknownValue; +} + +// contains +static inline +bool +contains(const char **array, size_t size, const char *value) +{ + for (int32 i = 0; i < (int32)size; i++) { + if (!strcmp(array[i], value)) + return true; + } + return false; +} + +// _GetParameterValue +template +static +bool +_GetParameterValue(const container_t *container, const char *name, + bool unknownValue, bool noArgValue) +{ + // note: container may be NULL + const char unknown = 0; + const char noArg = 0; + const char *value = _GetParameterValue(container, name, &unknown, &noArg); + if (value == &unknown) + return unknownValue; + if (value == &noArg) + return noArgValue; + const char *trueStrings[] + = { "1", "true", "yes", "on", "enable", "enabled" }; + const char *falseStrings[] + = { "0", "false", "no", "off", "disable", "disabled" }; + if (contains(trueStrings, sizeof(trueStrings) / sizeof(const char*), + value)) { + return true; + } + if (contains(falseStrings, sizeof(falseStrings) / sizeof(const char*), + value)) { + return false; + } + return unknownValue; +} + +// _GetParameterValue +template +static +int +_GetParameterValue(const container_t *container, const char *name, + int unknownValue, int noArgValue) +{ + // note: container may be NULL + const char unknown = 0; + const char noArg = 0; + const char *value = _GetParameterValue(container, name, &unknown, &noArg); + if (value == &unknown) + return unknownValue; + if (value == &noArg) + return noArgValue; + return atoi(value); +} + +// _FindFSParameter +static +const driver_parameter * +_FindFSParameter(const driver_settings *settings, const char *name) +{ + if (settings) { + int32 cookie = 0; + while (const driver_parameter *parameter + = _FindNextParameter(settings, "file_system", cookie)) { +PRINT((" found file_system parameter\n")); +if (parameter->value_count > 0) +PRINT((" value: `%s'\n", parameter->values[0])); + if (parameter->value_count == 1 + && !strcmp(parameter->values[0], name)) { + return parameter; + } + } + } + return NULL; +} + +// constructor +Settings::Settings() + : fIOCtlInfos(NULL) +{ +} + +// destructor +Settings::~Settings() +{ + Unset(); +} + +// SetTo +status_t +Settings::SetTo(const char* fsName) +{ + if (!fsName) + RETURN_ERROR(B_BAD_VALUE); + // unset + Unset(); + // create the ioctl info map + fIOCtlInfos = new(nothrow) IOCtlInfoMap; + if (!fIOCtlInfos) + RETURN_ERROR(B_NO_MEMORY); + // load the driver settings and find the entry for the FS + void *settings = load_driver_settings(kFSName); + const driver_parameter *fsParameter = NULL; + const driver_settings *ds = get_driver_settings(settings); + if (!ds) + RETURN_ERROR(B_ENTRY_NOT_FOUND); + fsParameter = _FindFSParameter(ds, fsName); + // init the object and unload the settings + status_t error = B_OK; + if (fsParameter) + _Init(ds, fsParameter); + else + error = B_ENTRY_NOT_FOUND; + unload_driver_settings(settings); + return B_OK; +} + +// Unset +void +Settings::Unset() +{ + if (fIOCtlInfos) { + for (IOCtlInfoMap::Iterator it = fIOCtlInfos->GetIterator(); + it.HasNext();) { + IOCtlInfoMap::Entry entry = it.Next(); + delete entry.value; + } + delete fIOCtlInfos; + fIOCtlInfos = NULL; + } +} + +// GetIOCtlInfo +const IOCtlInfo* +Settings::GetIOCtlInfo(int command) const +{ + return (fIOCtlInfos ? fIOCtlInfos->Get(command) : NULL); +} + +// Dump +void +Settings::Dump() const +{ + PRINT(("Settings:\n")); + if (fIOCtlInfos) { + for (IOCtlInfoMap::Iterator it = fIOCtlInfos->GetIterator(); + it.HasNext();) { + IOCtlInfoMap::Entry entry = it.Next(); + IOCtlInfo* info = entry.value; + PRINT((" ioctl %d: buffer size: %ld, write buffer size: %ld\n", + info->command, info->bufferSize, info->writeBufferSize)); + } + } +} + +// _Init +status_t +Settings::_Init(const driver_settings *settings, + const driver_parameter *fsParams) +{ +PRINT(("Settings::_Init(%p, %p)\n", settings, fsParams)); + status_t error = B_OK; + int32 cookie = 0; + while (const driver_parameter *parameter + = _FindNextParameter(fsParams, "ioctl", cookie)) { + if (parameter->value_count == 1) { + int command = atoi(parameter->values[0]); + if (command > 0) { + IOCtlInfo* info = fIOCtlInfos->Remove(command); + if (!info) { + info = new(nothrow) IOCtlInfo; + if (!info) + RETURN_ERROR(B_NO_MEMORY); + } + info->command = command; + info->bufferSize + = _GetParameterValue(parameter, "buffer_size", 0, 0); + info->writeBufferSize + = _GetParameterValue(parameter, "write_buffer_size", 0, 0); + info->isBuffer = _GetParameterValue(parameter, "is_buffer", + false, false); + error = fIOCtlInfos->Put(command, info); + if (error != B_OK) { + delete info; + return error; + } + } + } + } +PRINT(("Settings::_Init() done: %s\n", strerror(error))); + return error; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Settings.h b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Settings.h new file mode 100644 index 0000000000..3e88e5695a --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Settings.h @@ -0,0 +1,35 @@ +// Settings.h + +#ifndef USERLAND_FS_SETTINGS_H +#define USERLAND_FS_SETTINGS_H + +#include + +struct driver_settings; +struct driver_parameter; +struct IOCtlInfo; + +// Settings +class Settings { +public: + Settings(); + ~Settings(); + + status_t SetTo(const char *fsName); + void Unset(); + + const IOCtlInfo* GetIOCtlInfo(int command) const; + + void Dump() const; + +private: + status_t _Init(const driver_settings *settings, + const driver_parameter *fsParams); + +private: + struct IOCtlInfoMap; + + IOCtlInfoMap* fIOCtlInfos; +}; + +#endif // USERLAND_FS_SETTINGS_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/UserlandFS.cpp b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/UserlandFS.cpp new file mode 100644 index 0000000000..c64ab1f5ec --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/UserlandFS.cpp @@ -0,0 +1,258 @@ +// UserlandFS.cpp + +#include + +#include "AutoLocker.h" +#include "Compatibility.h" +#include "Debug.h" +#include "DispatcherDefs.h" +#include "FileSystem.h" +#include "KernelDebug.h" +#include "RequestPort.h" +#include "Requests.h" +#include "UserlandFS.h" + +typedef AutoLocker FileSystemLocker; + +UserlandFS* volatile UserlandFS::sUserlandFS = NULL; +spinlock UserlandFS::sUserlandFSLock = 0; +vint32 UserlandFS::sMountedFileSystems = 0; + +// constructor +UserlandFS::UserlandFS() + : LazyInitializable(), + fPort(NULL), + fFileSystems(NULL), + fDebuggerCommandsAdded(false) +{ + // beware what you do here: the caller holds a spin lock +} + +// destructor +UserlandFS::~UserlandFS() +{ +PRINT(("UserlandFS::~UserlandFS()\n")) + if (fPort) { + // send a disconnect request + RequestAllocator allocator(fPort->GetPort()); + UFSDisconnectRequest* request; + if (AllocateRequest(allocator, &request) == B_OK) { + if (fPort->SendRequest(&allocator) != B_OK) + PRINT((" failed to send disconnect request\n")); + } else + PRINT((" failed to allocate disconnect request\n")); + delete fPort; + } else + PRINT((" no port\n")); + + delete fFileSystems; + if (fDebuggerCommandsAdded) + KernelDebug::RemoveDebuggerCommands(); +} + +// RegisterUserlandFS +status_t +UserlandFS::RegisterUserlandFS(UserlandFS** _userlandFS) +{ + // first check, if there's already an instance + bool create = false; + + cpu_status cpuStatus = disable_interrupts(); + acquire_spinlock(&sUserlandFSLock); + + if (sUserlandFS) + sMountedFileSystems++; + else + create = true; + + release_spinlock(&sUserlandFSLock); + restore_interrupts(cpuStatus); + + // if there's not, create a new + status_t error = B_OK; + if (create) { + // first create an instance + // Note, that we can't even construct a LazyInitializable with a + // spinlock being held, since it allocates a semaphore, which may + // allocate memory, which will acquire a semaphore. + UserlandFS* userlandFS = new(nothrow) UserlandFS; + if (userlandFS) { + // now set the instance unless someone else beat us to it + bool deleteInstance = false; + + cpu_status cpuStatus = disable_interrupts(); + acquire_spinlock(&sUserlandFSLock); + + sMountedFileSystems++; + if (sUserlandFS) + deleteInstance = true; + else + sUserlandFS = userlandFS; + + release_spinlock(&sUserlandFSLock); + restore_interrupts(cpuStatus); + + // delete the new instance, if there was one already + if (deleteInstance) + delete userlandFS; + } else + error = B_NO_MEMORY; + } + if (error != B_OK) + return error; + + // init the thing, if necessary + error = sUserlandFS->Access(); + if (error == B_OK) + *_userlandFS = sUserlandFS; + else + UnregisterUserlandFS(); + return error; +} + +// UnregisterUserlandFS +void +UserlandFS::UnregisterUserlandFS() +{ + cpu_status cpuStatus = disable_interrupts(); + acquire_spinlock(&sUserlandFSLock); + + --sMountedFileSystems; + UserlandFS* userlandFS = NULL; + if (sMountedFileSystems == 0 && sUserlandFS) { + userlandFS = sUserlandFS; + sUserlandFS = NULL; + } + + release_spinlock(&sUserlandFSLock); + restore_interrupts(cpuStatus); + + // delete, if the last FS has been unmounted + if (userlandFS) { + userlandFS->~UserlandFS(); + delete[] (uint8*)userlandFS; + } +} + +// GetUserlandFS +UserlandFS* +UserlandFS::GetUserlandFS() +{ + return sUserlandFS; +} + +// RegisterFileSystem +status_t +UserlandFS::RegisterFileSystem(const char* name, FileSystem** _fileSystem) +{ + // check initialization and parameters + if (InitCheck() != B_OK) + return InitCheck(); + if (!name || !_fileSystem) + return B_BAD_VALUE; + // check, if we do already know this file system, and create it, if not + FileSystem* fileSystem; + { + FileSystemLocker _(fFileSystems); + fileSystem = fFileSystems->Get(name); + if (fileSystem) { + fileSystem->AddReference(); + } else { + status_t error; + fileSystem = new(nothrow) FileSystem(name, fPort, &error); + if (!fileSystem) + return B_NO_MEMORY; + if (error == B_OK) + error = fFileSystems->Put(name, fileSystem); + if (error != B_OK) { + delete fileSystem; + return error; + } + } + } + // prepare the file system + status_t error = fileSystem->Access(); + if (error != B_OK) { + UnregisterFileSystem(fileSystem); + return error; + } + *_fileSystem = fileSystem; + return error; +} + +// UnregisterFileSystem +status_t +UserlandFS::UnregisterFileSystem(FileSystem* fileSystem) +{ + if (!fileSystem) + return B_BAD_VALUE; + // find the FS and decrement its reference counter + bool deleteFS = false; + { + FileSystemLocker _(fFileSystems); + fileSystem = fFileSystems->Get(fileSystem->GetName()); + if (!fileSystem) + return B_BAD_VALUE; + deleteFS = fileSystem->RemoveReference(); + if (deleteFS) + fFileSystems->Remove(fileSystem->GetName()); + } + // delete the FS, if the last reference has been removed + if (deleteFS) + delete fileSystem; + return B_OK; +} + +// CountFileSystems +int32 +UserlandFS::CountFileSystems() const +{ + return fFileSystems->Size(); +} + +// FirstTimeInit +status_t +UserlandFS::FirstTimeInit() +{ + // add debugger commands + KernelDebug::AddDebuggerCommands(); + fDebuggerCommandsAdded = true; + // create file system map + fFileSystems = new(nothrow) FileSystemMap; + if (!fFileSystems) + RETURN_ERROR(B_NO_MEMORY); + status_t error = fFileSystems->InitCheck(); + if (error != B_OK) + RETURN_ERROR(error); + // find the dispatcher ports + port_id port = find_port(kUserlandFSDispatcherPortName); + if (port < 0) + RETURN_ERROR(B_ERROR); + port_id replyPort = find_port(kUserlandFSDispatcherReplyPortName); + if (replyPort < 0) + RETURN_ERROR(B_ERROR); + // create a reply port + // send a connection request + error = write_port(port, UFS_DISPATCHER_CONNECT, NULL, 0); + if (error != B_OK) + RETURN_ERROR(error); + // receive the reply + int32 replyCode; + Port::Info portInfo; + ssize_t bytesRead = read_port(replyPort, &replyCode, &portInfo, + sizeof(Port::Info)); + if (bytesRead < 0) + RETURN_ERROR(bytesRead); + if (replyCode != UFS_DISPATCHER_CONNECT_ACK) + RETURN_ERROR(B_BAD_DATA); + if (bytesRead != sizeof(Port::Info)) + RETURN_ERROR(B_BAD_DATA); + // create a request port + fPort = new(nothrow) RequestPort(&portInfo); + if (!fPort) + RETURN_ERROR(B_NO_MEMORY); + if ((error = fPort->InitCheck()) != B_OK) + RETURN_ERROR(error); + RETURN_ERROR(error); +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/UserlandFS.h b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/UserlandFS.h new file mode 100644 index 0000000000..5982376747 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/UserlandFS.h @@ -0,0 +1,54 @@ +// UserlandFS.h + +#ifndef USERLAND_FS_H +#define USERLAND_FS_H + +#include + +#include "HashMap.h" +#include "LazyInitializable.h" +#include "String.h" + +namespace UserlandFSUtil { + +class RequestPort; + +} + +using UserlandFSUtil::RequestPort; + +class FileSystem; + +class UserlandFS : public LazyInitializable { +private: + UserlandFS(); + ~UserlandFS(); + +public: + static status_t RegisterUserlandFS(UserlandFS** userlandFS); + static void UnregisterUserlandFS(); + static UserlandFS* GetUserlandFS(); + + status_t RegisterFileSystem(const char* name, + FileSystem** fileSystem); + status_t UnregisterFileSystem(FileSystem* fileSystem); + + int32 CountFileSystems() const; + +protected: + virtual status_t FirstTimeInit(); + +private: + friend class KernelDebug; + typedef SynchronizedHashMap FileSystemMap; + + static UserlandFS* volatile sUserlandFS; + static spinlock sUserlandFSLock; + static vint32 sMountedFileSystems; + + RequestPort* fPort; + FileSystemMap* fFileSystems; + bool fDebuggerCommandsAdded; +}; + +#endif // USERLAND_FS_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Volume.cpp b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Volume.cpp new file mode 100644 index 0000000000..c741db9932 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Volume.cpp @@ -0,0 +1,2800 @@ +// Volume.cpp + +#include +#include + +#include "AutoLocker.h" +#include "Compatibility.h" +#include "Debug.h" +#include "FileSystem.h" +#include "HashMap.h" +#include "IOCtlInfo.h" +#include "KernelRequestHandler.h" +#include "PortReleaser.h" +#include "RequestAllocator.h" +#include "RequestPort.h" +#include "Requests.h" +#include "userlandfs_ioctl.h" +#include "Volume.h" + +// If a thread of the userland server enters userland FS kernel code and +// is sending a request, this is the time after which it shall time out +// waiting for a reply. +static const bigtime_t kUserlandServerlandPortTimeout = 10000000; // 10s + +// MountVNodeMap +struct Volume::MountVNodeMap : public HashMap, void*> { +}; + +// VNodeCountMap +struct Volume::VNodeCountMap + : public SynchronizedHashMap, int32*> { +}; + +// AutoIncrementer +class Volume::AutoIncrementer { +public: + AutoIncrementer(vint32* variable) + : fVariable(variable) + { + if (fVariable) + atomic_add(fVariable, 1); + } + + ~AutoIncrementer() + { + if (fVariable) + atomic_add(fVariable, -1); + } + + void Keep() + { + fVariable = NULL; + } + +private: + vint32* fVariable; +}; + +// constructor +Volume::Volume(FileSystem* fileSystem, nspace_id id) + : Referencable(true), + fFileSystem(fileSystem), + fID(id), + fUserlandVolume(NULL), + fRootID(0), + fRootNode(NULL), + fMountVNodes(NULL), + fOpenFiles(0), + fOpenDirectories(0), + fOpenAttributeDirectories(0), + fOpenIndexDirectories(0), + fOpenQueries(0), + fVNodeCountMap(NULL), + fVNodeCountingEnabled(false) +{ +} + +// destructor +Volume::~Volume() +{ +} + +// GetFileSystem +FileSystem* +Volume::GetFileSystem() const +{ + return fFileSystem; +} + +// GetID +nspace_id +Volume::GetID() const +{ + return fID; +} + +// GetUserlandVolume +void* +Volume::GetUserlandVolume() const +{ + return fUserlandVolume; +} + +// GetRootID +vnode_id +Volume::GetRootID() const +{ + return fRootID; +} + +// IsMounting +bool +Volume::IsMounting() const +{ + return fMountVNodes; +} + + +// #pragma mark - +// #pragma mark ----- client methods ----- + +// GetVNode +status_t +Volume::GetVNode(vnode_id vnid, void** node) +{ +PRINT(("get_vnode(%ld, %Ld)\n", fID, vnid)); + if (IsMounting() && !fMountVNodes->ContainsKey(vnid)) { + ERROR(("Volume::GetVNode(): get_vnode() invoked for unknown vnode " + "while mounting!\n")); + } + status_t error = get_vnode(fID, vnid, node); + if (error == B_OK) + _IncrementVNodeCount(vnid); + return error; +} + +// PutVNode +status_t +Volume::PutVNode(vnode_id vnid) +{ +PRINT(("put_vnode(%ld, %Ld)\n", fID, vnid)); + status_t error = put_vnode(fID, vnid); + if (error == B_OK) + _DecrementVNodeCount(vnid); + return error; +} + +// NewVNode +status_t +Volume::NewVNode(vnode_id vnid, void* node) +{ +PRINT(("new_vnode(%ld, %Ld)\n", fID, vnid)); + status_t error = new_vnode(fID, vnid, node); + if (error == B_OK) { + if (IsMounting()) { + error = fMountVNodes->Put(vnid, node); + if (error != B_OK) { + ERROR(("Volume::NewVNode(): Failed to add vnode to mount " + "vnode map!\n")); + put_vnode(fID, vnid); + return error; + } + } + _IncrementVNodeCount(vnid); + } + return error; +} + +// RemoveVNode +status_t +Volume::RemoveVNode(vnode_id vnid) +{ +PRINT(("remove_vnode(%ld, %Ld)\n", fID, vnid)); + return remove_vnode(fID, vnid); +} + +// UnremoveVNode +status_t +Volume::UnremoveVNode(vnode_id vnid) +{ +PRINT(("unremove_vnode(%ld, %Ld)\n", fID, vnid)); + return unremove_vnode(fID, vnid); +} + +// IsVNodeRemoved +status_t +Volume::IsVNodeRemoved(vnode_id vnid) +{ +PRINT(("is_vnode_removed(%ld, %Ld)\n", fID, vnid)); + return is_vnode_removed(fID, vnid); +} + +// #pragma mark - +// #pragma mark ----- FS ----- + +// Mount +status_t +Volume::Mount(const char* device, ulong flags, const char* parameters, + int32 len) +{ + // Create a map that holds vnode_id->void* mappings of all vnodes + // created while mounting. We need it to get the root node. + MountVNodeMap vnodeMap; + status_t error = vnodeMap.InitCheck(); + if (error != B_OK) + RETURN_ERROR(error); + fMountVNodes = &vnodeMap; + error = _Mount(device, flags, parameters, len); + fMountVNodes = NULL; + if (error == B_OK) { + // fetch the root node, so that we can serve Walk() requests on it, + // after the connection to the userland server is gone + if (!vnodeMap.ContainsKey(fRootID)) { + // The root node was not added while mounting. That's a serious + // problem -- not only because we don't have it, but also because + // the VFS requires new_vnode() to be invoked for the root node. + ERROR(("Volume::Mount(): new_vnode() was not called for root node! " + "Unmounting...\n")); + Unmount(); + return B_ERROR; + } + fRootNode = vnodeMap.Get(fRootID); + } + return error; +} + +// Unmount +status_t +Volume::Unmount() +{ + status_t error = _Unmount(); + // free the memory associated with the vnode count map + if (fVNodeCountMap) { + AutoLocker _(fVNodeCountMap); + fVNodeCountingEnabled = false; + for (VNodeCountMap::Iterator it = fVNodeCountMap->GetIterator(); + it.HasNext();) { + VNodeCountMap::Entry entry = it.Next(); + delete entry.value; + } + delete fVNodeCountMap; + fVNodeCountMap = NULL; + } + fFileSystem->VolumeUnmounted(this); + return error; +} + +// Sync +status_t +Volume::Sync() +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + SyncVolumeRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + // send the request + KernelRequestHandler handler(this, SYNC_VOLUME_REPLY); + SyncVolumeReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// ReadFSStat +status_t +Volume::ReadFSStat(fs_info* info) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + ReadFSStatRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + // send the request + KernelRequestHandler handler(this, READ_FS_STAT_REPLY); + ReadFSStatReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + *info = reply->info; + return error; +} + +// WriteFSStat +status_t +Volume::WriteFSStat(struct fs_info *info, long mask) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + WriteFSStatRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->info = *info; + request->mask = mask; + // send the request + KernelRequestHandler handler(this, WRITE_FS_STAT_REPLY); + WriteFSStatReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// #pragma mark - +// #pragma mark ----- vnodes ----- + +// ReadVNode +status_t +Volume::ReadVNode(vnode_id vnid, char reenter, void** node) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + ReadVNodeRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->vnid = vnid; + request->reenter = reenter; + // send the request + KernelRequestHandler handler(this, READ_VNODE_REPLY); + ReadVNodeReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + *node = reply->node; + return error; +} + +// WriteVNode +status_t +Volume::WriteVNode(void* node, char reenter) +{ + status_t error = _WriteVNode(node, reenter); + if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { + // This isn't really necessary, since the VFS basically ignores the + // return value -- at least OBOS. The fshell panic()s; didn't check + // BeOS. It doesn't harm to appear to behave nicely. :-) + WARN(("Volume::WriteVNode(): connection lost, forcing write vnode\n")); + return B_OK; + } + return error; +} + +// RemoveVNode +status_t +Volume::RemoveVNode(void* node, char reenter) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + FSRemoveVNodeRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->reenter = reenter; + // send the request + KernelRequestHandler handler(this, FS_REMOVE_VNODE_REPLY); + FSRemoveVNodeReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// #pragma mark - +// #pragma mark ----- nodes ----- + +// FSync +status_t +Volume::FSync(void* node) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + FSyncRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + // send the request + KernelRequestHandler handler(this, FSYNC_REPLY); + FSyncReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// ReadStat +status_t +Volume::ReadStat(void* node, struct stat* st) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + ReadStatRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + // send the request + KernelRequestHandler handler(this, READ_STAT_REPLY); + ReadStatReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + *st = reply->st; + return error; +} + +// WriteStat +status_t +Volume::WriteStat(void* node, struct stat* st, long mask) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + WriteStatRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->st = *st; + request->mask = mask; + // send the request + KernelRequestHandler handler(this, WRITE_STAT_REPLY); + WriteStatReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// Access +status_t +Volume::Access(void* node, int mode) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + AccessRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->mode = mode; + // send the request + KernelRequestHandler handler(this, ACCESS_REPLY); + AccessReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// #pragma mark - +// #pragma mark ----- files ----- + +// Create +status_t +Volume::Create(void* dir, const char* name, int openMode, int mode, + vnode_id* vnid, void** cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + CreateRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = dir; + error = allocator.AllocateString(request->name, name); + request->openMode = openMode; + request->mode = mode; + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, CREATE_REPLY); + CreateReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + *vnid = reply->vnid; + *cookie = reply->fileCookie; + if (error == B_OK) + _DecrementVNodeCount(*vnid); + return error; +} + +// Open +status_t +Volume::Open(void* node, int openMode, void** cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + AutoIncrementer incrementer(&fOpenFiles); + // prepare the request + RequestAllocator allocator(port->GetPort()); + OpenRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->openMode = openMode; + // send the request + KernelRequestHandler handler(this, OPEN_REPLY); + OpenReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + incrementer.Keep(); + *cookie = reply->fileCookie; + return error; +} + +// Close +status_t +Volume::Close(void* node, void* cookie) +{ + status_t error = _Close(node, cookie); + if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { + // This isn't really necessary, as the return value is irrelevant to + // the VFS. OBOS ignores it completely. The fsshell returns it to the + // userland, but considers the node closed anyway. + WARN(("Volume::Close(): connection lost, forcing close\n")); + return B_OK; + } + return error; +} + +// FreeCookie +status_t +Volume::FreeCookie(void* node, void* cookie) +{ + status_t error = _FreeCookie(node, cookie); + bool disconnected = false; + if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { + // This isn't really necessary, as the return value is irrelevant to + // the VFS. It's completely ignored by OBOS as well as by the fsshell. + WARN(("Volume::FreeCookie(): connection lost, forcing free cookie\n")); + error = B_OK; + disconnected = true; + } + int32 openFiles = atomic_add(&fOpenFiles, -1); + if (openFiles <= 1 && disconnected) + _PutAllPendingVNodes(); + return error; +} + +// Read +status_t +Volume::Read(void* node, void* cookie, off_t pos, void* buffer, + size_t bufferSize, size_t* bytesRead) +{ + *bytesRead = 0; + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + ReadRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->fileCookie = cookie; + request->pos = pos; + request->size = bufferSize; + // send the request + KernelRequestHandler handler(this, READ_REPLY); + ReadReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + void* readBuffer = reply->buffer.GetData(); + if (reply->bytesRead > (uint32)reply->buffer.GetSize() + || reply->bytesRead > bufferSize) { + return B_BAD_DATA; + } + if (reply->bytesRead > 0) + memcpy(buffer, readBuffer, reply->bytesRead); + *bytesRead = reply->bytesRead; + _SendReceiptAck(port); + return error; +} + +// Write +status_t +Volume::Write(void* node, void* cookie, off_t pos, const void* buffer, + size_t size, size_t* bytesWritten) +{ + *bytesWritten = 0; + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + WriteRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->fileCookie = cookie; + request->pos = pos; + error = allocator.AllocateData(request->buffer, buffer, size, 1); + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, WRITE_REPLY); + WriteReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + *bytesWritten = reply->bytesWritten; + return error; +} + +// IOCtl +status_t +Volume::IOCtl(void* node, void* cookie, int command, void *buffer, + size_t len) +{ + // check the command and its parameters + bool isBuffer = false; + int32 bufferSize = 0; + int32 writeSize = 0; + switch (command) { + case IOCTL_FILE_UNCACHED_IO: + buffer = NULL; + break; + case IOCTL_CREATE_TIME: + case IOCTL_MODIFIED_TIME: + isBuffer = 0; + bufferSize = 0; + writeSize = sizeof(bigtime_t); + break; + case USERLANDFS_IOCTL: + area_id area; + area_info info; + PRINT(("Volume::IOCtl(): USERLANDFS_IOCTL\n")); + if ((area = area_for(buffer)) >= 0) { + if (get_area_info(area, &info) == B_OK) { + if ((uint8*)buffer - (uint8*)info.address + + sizeof(userlandfs_ioctl) <= info.size) { + if (strncmp(((userlandfs_ioctl*)buffer)->magic, + kUserlandFSIOCtlMagic, + USERLAND_IOCTL_MAGIC_LENGTH) == 0) { + return _InternalIOCtl((userlandfs_ioctl*)buffer, + bufferSize); + } else + PRINT(("Volume::IOCtl(): bad magic\n")); + } else + PRINT(("Volume::IOCtl(): bad buffer size\n")); + } else + PRINT(("Volume::IOCtl(): failed to get area info\n")); + } else + PRINT(("Volume::IOCtl(): bad area\n")); + // fall through... + default: + { + // We don't know the command. Check whether the FileSystem knows + // about it. + const IOCtlInfo* info = fFileSystem->GetIOCtlInfo(command); + if (!info) { + PRINT(("Volume::IOCtl(): unknown command\n")); + return B_BAD_VALUE; + } + isBuffer = info->isBuffer; + bufferSize = info->bufferSize; + writeSize = info->writeBufferSize; + // If the buffer shall indeed specify a buffer, check it. + if (info->isBuffer) { + if (!buffer) { + PRINT(("Volume::IOCtl(): buffer is NULL\n")); + return B_BAD_VALUE; + } + area_id area = area_for(buffer); + if (area < 0) { + PRINT(("Volume::IOCtl(): bad area\n")); + return B_BAD_VALUE; + } + area_info info; + if (get_area_info(area, &info) != B_OK) { + PRINT(("Volume::IOCtl(): failed to get area info\n")); + return B_BAD_VALUE; + } + int32 areaSize = info.size - ((uint8*)buffer + - (uint8*)info.address); + if (bufferSize > areaSize || writeSize > areaSize) { + PRINT(("Volume::IOCtl(): bad buffer size\n")); + return B_BAD_VALUE; + } + if (writeSize > 0 && !(info.protection & B_WRITE_AREA)) { + PRINT(("Volume::IOCtl(): buffer not writable\n")); + return B_BAD_VALUE; + } + } + break; + } + } + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + IOCtlRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->fileCookie = cookie; + request->command = command; + request->bufferParameter = buffer; + request->isBuffer = isBuffer; + request->lenParameter = len; + request->writeSize = writeSize; + if (isBuffer && bufferSize > 0) { + error = allocator.AllocateData(request->buffer, buffer, bufferSize, 8); + if (error != B_OK) + return error; + } + // send the request + KernelRequestHandler handler(this, IOCTL_REPLY); + IOCtlReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + // Copy back the buffer even if the result is not B_OK. The protocol + // is defined by the FS developer and may include writing data into + // the buffer in some error cases. + if (isBuffer && writeSize > 0 && reply->buffer.GetData()) { + if (writeSize > reply->buffer.GetSize()) + writeSize = reply->buffer.GetSize(); + memcpy(buffer, reply->buffer.GetData(), writeSize); + _SendReceiptAck(port); + } + return reply->ioctlError; +} + +// SetFlags +status_t +Volume::SetFlags(void* node, void* cookie, int flags) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + SetFlagsRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->fileCookie = cookie; + request->flags = flags; + // send the request + KernelRequestHandler handler(this, SET_FLAGS_REPLY); + SetFlagsReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// Select +status_t +Volume::Select(void* node, void* cookie, uint8 event, uint32 ref, + selectsync* sync) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + SelectRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->fileCookie = cookie; + request->event = event; + request->ref = ref; + request->sync = sync; + // add a selectsync entry + error = fFileSystem->AddSelectSyncEntry(sync); + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, SELECT_REPLY); + SelectReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) { + fFileSystem->RemoveSelectSyncEntry(sync); + return error; + } + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) { + fFileSystem->RemoveSelectSyncEntry(sync); + return reply->error; + } + return error; +} + +// Deselect +status_t +Volume::Deselect(void* node, void* cookie, uint8 event, selectsync* sync) +{ + struct SyncRemover { + SyncRemover(FileSystem* fs, selectsync* sync) + : fs(fs), sync(sync) {} + ~SyncRemover() { fs->RemoveSelectSyncEntry(sync); } + + FileSystem* fs; + selectsync* sync; + } syncRemover(fFileSystem, sync); + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + DeselectRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->fileCookie = cookie; + request->event = event; + request->sync = sync; + // send the request + KernelRequestHandler handler(this, DESELECT_REPLY); + DeselectReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// #pragma mark - +// #pragma mark ----- hard links / symlinks ----- + +// Link +status_t +Volume::Link(void* dir, const char* name, void* node) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + LinkRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = dir; + error = allocator.AllocateString(request->name, name); + request->target = node; + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, LINK_REPLY); + LinkReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// Unlink +status_t +Volume::Unlink(void* dir, const char* name) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + UnlinkRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = dir; + error = allocator.AllocateString(request->name, name); + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, UNLINK_REPLY); + UnlinkReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// Symlink +status_t +Volume::Symlink(void* dir, const char* name, const char* target) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + SymlinkRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = dir; + error = allocator.AllocateString(request->name, name); + if (error == B_OK) + error = allocator.AllocateString(request->target, target); + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, SYMLINK_REPLY); + SymlinkReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// ReadLink +status_t +Volume::ReadLink(void* node, char* buffer, size_t bufferSize, size_t* bytesRead) +{ + *bytesRead = 0; + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + ReadLinkRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->size = bufferSize; + // send the request + KernelRequestHandler handler(this, READ_LINK_REPLY); + ReadLinkReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + void* readBuffer = reply->buffer.GetData(); + if (reply->bytesRead > (uint32)reply->buffer.GetSize() + || reply->bytesRead > bufferSize) { + return B_BAD_DATA; + } + if (reply->bytesRead > 0) + memcpy(buffer, readBuffer, reply->bytesRead); + *bytesRead = reply->bytesRead; + _SendReceiptAck(port); + return error; +} + +// Rename +status_t +Volume::Rename(void* oldDir, const char* oldName, void* newDir, + const char* newName) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + RenameRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->oldDir = oldDir; + request->newDir = newDir; + error = allocator.AllocateString(request->oldName, oldName); + if (error == B_OK) + error = allocator.AllocateString(request->newName, newName); + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, RENAME_REPLY); + RenameReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// #pragma mark - +// #pragma mark ----- directories ----- + +// MkDir +status_t +Volume::MkDir(void* dir, const char* name, int mode) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + MkDirRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = dir; + error = allocator.AllocateString(request->name, name); + request->mode = mode; + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, MKDIR_REPLY); + MkDirReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// RmDir +status_t +Volume::RmDir(void* dir, const char* name) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + RmDirRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = dir; + error = allocator.AllocateString(request->name, name); + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, RMDIR_REPLY); + RmDirReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// OpenDir +status_t +Volume::OpenDir(void* node, void** cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + AutoIncrementer incrementer(&fOpenDirectories); + // prepare the request + RequestAllocator allocator(port->GetPort()); + OpenDirRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + // send the request + KernelRequestHandler handler(this, OPEN_DIR_REPLY); + OpenDirReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + incrementer.Keep(); + *cookie = reply->dirCookie; + return error; +} + +// CloseDir +status_t +Volume::CloseDir(void* node, void* cookie) +{ + status_t error = _CloseDir(node, cookie); + if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { + // This isn't really necessary, as the return value is irrelevant to + // the VFS. OBOS ignores it completely. The fsshell returns it to the + // userland, but considers the node closed anyway. + WARN(("Volume::CloseDir(): connection lost, forcing close dir\n")); + return B_OK; + } + return error; +} + +// FreeDirCookie +status_t +Volume::FreeDirCookie(void* node, void* cookie) +{ + status_t error = _FreeDirCookie(node, cookie); + bool disconnected = false; + if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { + // This isn't really necessary, as the return value is irrelevant to + // the VFS. It's completely ignored by OBOS as well as by the fsshell. + WARN(("Volume::FreeDirCookie(): connection lost, forcing free dir " + "cookie\n")); + error = B_OK; + disconnected = true; + } + int32 openDirs = atomic_add(&fOpenDirectories, -1); + if (openDirs <= 1 && disconnected) + _PutAllPendingVNodes(); + return error; +} + +// ReadDir +status_t +Volume::ReadDir(void* node, void* cookie, void* buffer, size_t bufferSize, + int32 count, int32* countRead) +{ + *countRead = 0; + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + ReadDirRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->dirCookie = cookie; + request->bufferSize = bufferSize; + request->count = count; + // send the request + KernelRequestHandler handler(this, READ_DIR_REPLY); + ReadDirReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + if (reply->count < 0 || reply->count > count) + return B_BAD_DATA; + if ((int32)bufferSize < reply->buffer.GetSize()) + return B_BAD_DATA; +PRINT(("Volume::ReadDir(): buffer returned: %ld bytes\n", +reply->buffer.GetSize())); + *countRead = reply->count; + if (*countRead > 0) { + // copy the buffer -- limit the number of bytes to copy + int32 maxBytes = *countRead + * (sizeof(struct dirent) + B_FILE_NAME_LENGTH); + int32 copyBytes = reply->buffer.GetSize(); + if (copyBytes > maxBytes) + copyBytes = maxBytes; + memcpy(buffer, reply->buffer.GetData(), copyBytes); + } + _SendReceiptAck(port); + return error; +} + +// RewindDir +status_t +Volume::RewindDir(void* node, void* cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + RewindDirRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->dirCookie = cookie; + // send the request + KernelRequestHandler handler(this, REWIND_DIR_REPLY); + RewindDirReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// Walk +status_t +Volume::Walk(void* dir, const char* entryName, char** resolvedPath, + vnode_id* vnid) +{ + // When the connection to the userland server is lost, we serve + // walk(fRootNode, `.') requests manually to allow clean unmounting. + status_t error = _Walk(dir, entryName, resolvedPath, vnid); + if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected() + && dir == fRootNode && strcmp(entryName, ".") == 0) { + WARN(("Volume::Walk(): connection lost, emulating walk `.'\n")); + void* entryNode; + if (GetVNode(fRootID, &entryNode) != B_OK) + RETURN_ERROR(B_BAD_VALUE); + *vnid = fRootID; + _DecrementVNodeCount(*vnid); + return B_OK; + } + return error; +} + +// #pragma mark - +// #pragma mark ----- attributes ----- + +// OpenAttrDir +status_t +Volume::OpenAttrDir(void* node, void** cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + AutoIncrementer incrementer(&fOpenAttributeDirectories); + // prepare the request + RequestAllocator allocator(port->GetPort()); + OpenAttrDirRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + // send the request + KernelRequestHandler handler(this, OPEN_ATTR_DIR_REPLY); + OpenAttrDirReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + incrementer.Keep(); + *cookie = reply->attrDirCookie; + return error; +} + +// CloseAttrDir +status_t +Volume::CloseAttrDir(void* node, void* cookie) +{ + status_t error = _CloseAttrDir(node, cookie); + if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { + // This isn't really necessary, as the return value is irrelevant to + // the VFS. OBOS ignores it completely. The fsshell returns it to the + // userland, but considers the node closed anyway. + WARN(("Volume::CloseAttrDir(): connection lost, forcing close attr " + "dir\n")); + return B_OK; + } + return error; +} + +// FreeAttrDirCookie +status_t +Volume::FreeAttrDirCookie(void* node, void* cookie) +{ + status_t error = _FreeAttrDirCookie(node, cookie); + bool disconnected = false; + if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { + // This isn't really necessary, as the return value is irrelevant to + // the VFS. It's completely ignored by OBOS as well as by the fsshell. + WARN(("Volume::FreeAttrDirCookie(): connection lost, forcing free attr " + "dir cookie\n")); + error = B_OK; + disconnected = true; + } + int32 openAttrDirs = atomic_add(&fOpenAttributeDirectories, -1); + if (openAttrDirs <= 1 && disconnected) + _PutAllPendingVNodes(); + return error; +} + +// ReadAttrDir +status_t +Volume::ReadAttrDir(void* node, void* cookie, void* buffer, size_t bufferSize, + int32 count, int32* countRead) +{ + *countRead = 0; + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + ReadAttrDirRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->attrDirCookie = cookie; + request->bufferSize = bufferSize; + request->count = count; + // send the request + KernelRequestHandler handler(this, READ_ATTR_DIR_REPLY); + ReadAttrDirReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + if (reply->count < 0 || reply->count > count) + return B_BAD_DATA; + if ((int32)bufferSize < reply->buffer.GetSize()) + return B_BAD_DATA; + *countRead = reply->count; + if (*countRead > 0) { + // copy the buffer -- limit the number of bytes to copy + int32 maxBytes = *countRead + * (sizeof(struct dirent) + B_ATTR_NAME_LENGTH); + int32 copyBytes = reply->buffer.GetSize(); + if (copyBytes > maxBytes) + copyBytes = maxBytes; + memcpy(buffer, reply->buffer.GetData(), copyBytes); + } + _SendReceiptAck(port); + return error; +} + +// RewindAttrDir +status_t +Volume::RewindAttrDir(void* node, void* cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + RewindAttrDirRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->attrDirCookie = cookie; + // send the request + KernelRequestHandler handler(this, REWIND_ATTR_DIR_REPLY); + RewindAttrDirReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// ReadAttr +status_t +Volume::ReadAttr(void* node, const char* name, int type, off_t pos, + void* buffer, size_t bufferSize, size_t* bytesRead) +{ + *bytesRead = 0; + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + ReadAttrRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + error = allocator.AllocateString(request->name, name); + request->type = type; + request->pos = pos; + request->size = bufferSize; + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, READ_ATTR_REPLY); + ReadAttrReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + void* readBuffer = reply->buffer.GetData(); + if (reply->bytesRead > (uint32)reply->buffer.GetSize() + || reply->bytesRead > bufferSize) { + return B_BAD_DATA; + } + if (reply->bytesRead > 0) + memcpy(buffer, readBuffer, reply->bytesRead); + *bytesRead = reply->bytesRead; + _SendReceiptAck(port); + return error; +} + +// WriteAttr +status_t +Volume::WriteAttr(void* node, const char* name, int type, off_t pos, + const void* buffer, size_t bufferSize, size_t* bytesWritten) +{ + *bytesWritten = 0; + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + WriteAttrRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + error = allocator.AllocateString(request->name, name); + request->type = type; + request->pos = pos; + if (error == B_OK) + error = allocator.AllocateData(request->buffer, buffer, bufferSize, 1); + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, WRITE_ATTR_REPLY); + WriteAttrReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + *bytesWritten = reply->bytesWritten; + return error; +} + +// RemoveAttr +status_t +Volume::RemoveAttr(void* node, const char* name) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + RemoveAttrRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + error = allocator.AllocateString(request->name, name); + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, REMOVE_ATTR_REPLY); + RemoveAttrReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// RenameAttr +status_t +Volume::RenameAttr(void* node, const char* oldName, const char* newName) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + RenameAttrRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + error = allocator.AllocateString(request->oldName, oldName); + if (error == B_OK) + error = allocator.AllocateString(request->newName, newName); + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, RENAME_ATTR_REPLY); + RenameAttrReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// StatAttr +status_t +Volume::StatAttr(void* node, const char* name, struct attr_info* attrInfo) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + StatAttrRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + error = allocator.AllocateString(request->name, name); + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, STAT_ATTR_REPLY); + StatAttrReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + *attrInfo = reply->info; + return error; +} + +// #pragma mark - +// #pragma mark ----- indices ----- + +// OpenIndexDir +status_t +Volume::OpenIndexDir(void** cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + AutoIncrementer incrementer(&fOpenIndexDirectories); + // prepare the request + RequestAllocator allocator(port->GetPort()); + OpenIndexDirRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + // send the request + KernelRequestHandler handler(this, OPEN_INDEX_DIR_REPLY); + OpenIndexDirReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + incrementer.Keep(); + *cookie = reply->indexDirCookie; + return error; +} + +// CloseIndexDir +status_t +Volume::CloseIndexDir(void* cookie) +{ + status_t error = _CloseIndexDir(cookie); + if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { + // This isn't really necessary, as the return value is irrelevant to + // the VFS. OBOS ignores it completely. The fsshell returns it to the + // userland, but considers the node closed anyway. + WARN(("Volume::CloseIndexDir(): connection lost, forcing close " + "index dir\n")); + return B_OK; + } + return error; +} + +// FreeIndexDirCookie +status_t +Volume::FreeIndexDirCookie(void* cookie) +{ + status_t error = _FreeIndexDirCookie(cookie); + bool disconnected = false; + if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { + // This isn't really necessary, as the return value is irrelevant to + // the VFS. It's completely ignored by OBOS as well as by the fsshell. + WARN(("Volume::FreeIndexDirCookie(): connection lost, forcing free " + "index dir cookie\n")); + error = B_OK; + disconnected = true; + } + int32 openIndexDirs = atomic_add(&fOpenIndexDirectories, -1); + if (openIndexDirs <= 1 && disconnected) + _PutAllPendingVNodes(); + return error; +} + +// ReadIndexDir +status_t +Volume::ReadIndexDir(void* cookie, void* buffer, size_t bufferSize, int32 count, + int32* countRead) +{ + *countRead = 0; + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + ReadIndexDirRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->indexDirCookie = cookie; + request->bufferSize = bufferSize; + request->count = count; + // send the request + KernelRequestHandler handler(this, READ_INDEX_DIR_REPLY); + ReadIndexDirReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + if (reply->count < 0 || reply->count > count) + return B_BAD_DATA; + if ((int32)bufferSize < reply->buffer.GetSize()) + return B_BAD_DATA; + *countRead = reply->count; + if (*countRead > 0) { + // copy the buffer -- limit the number of bytes to copy + int32 maxBytes = *countRead + * (sizeof(struct dirent) + B_FILE_NAME_LENGTH); + int32 copyBytes = reply->buffer.GetSize(); + if (copyBytes > maxBytes) + copyBytes = maxBytes; + memcpy(buffer, reply->buffer.GetData(), copyBytes); + } + _SendReceiptAck(port); + return error; +} + +// RewindIndexDir +status_t +Volume::RewindIndexDir(void* cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + RewindIndexDirRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->indexDirCookie = cookie; + // send the request + KernelRequestHandler handler(this, REWIND_INDEX_DIR_REPLY); + RewindIndexDirReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// CreateIndex +status_t +Volume::CreateIndex(const char* name, int type, int flags) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + CreateIndexRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + error = allocator.AllocateString(request->name, name); + request->type = type; + request->flags = flags; + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, CREATE_INDEX_REPLY); + CreateIndexReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// RemoveIndex +status_t +Volume::RemoveIndex(const char* name) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + RemoveIndexRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + error = allocator.AllocateString(request->name, name); + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, REMOVE_INDEX_REPLY); + RemoveIndexReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// RenameIndex +status_t +Volume::RenameIndex(const char* oldName, const char* newName) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + RenameIndexRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + error = allocator.AllocateString(request->oldName, oldName); + if (error == B_OK) + error = allocator.AllocateString(request->newName, newName); + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, RENAME_INDEX_REPLY); + RenameIndexReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// StatIndex +status_t +Volume::StatIndex(const char *name, struct index_info* indexInfo) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + StatIndexRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + error = allocator.AllocateString(request->name, name); + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, STAT_INDEX_REPLY); + StatIndexReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + *indexInfo = reply->info; + return error; +} + +// #pragma mark - +// #pragma mark ----- queries ----- + +// OpenQuery +status_t +Volume::OpenQuery(const char* queryString, ulong flags, port_id targetPort, + long token, void** cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + AutoIncrementer incrementer(&fOpenQueries); + // prepare the request + RequestAllocator allocator(port->GetPort()); + OpenQueryRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + error = allocator.AllocateString(request->queryString, queryString); + if (error != B_OK) + return error; + request->flags = flags; + request->port = targetPort; + request->token = token; + // send the request + KernelRequestHandler handler(this, OPEN_QUERY_REPLY); + OpenQueryReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + incrementer.Keep(); + *cookie = reply->queryCookie; + return error; +} + +// CloseQuery +status_t +Volume::CloseQuery(void* cookie) +{ + status_t error = _CloseQuery(cookie); + if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { + // This isn't really necessary, as the return value is irrelevant to + // the VFS. OBOS ignores it completely. The fsshell returns it to the + // userland, but considers the node closed anyway. + WARN(("Volume::CloseQuery(): connection lost, forcing close query\n")); + return B_OK; + } + return error; +} + +// FreeQueryCookie +status_t +Volume::FreeQueryCookie(void* cookie) +{ + status_t error = _FreeQueryCookie(cookie); + bool disconnected = false; + if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { + // This isn't really necessary, as the return value is irrelevant to + // the VFS. It's completely ignored by OBOS as well as by the fsshell. + WARN(("Volume::FreeQueryCookie(): connection lost, forcing free " + "query cookie\n")); + error = B_OK; + disconnected = true; + } + int32 openQueries = atomic_add(&fOpenQueries, -1); + if (openQueries <= 1 && disconnected) + _PutAllPendingVNodes(); + return error; +} + +// ReadQuery +status_t +Volume::ReadQuery(void* cookie, void* buffer, size_t bufferSize, int32 count, + int32* countRead) +{ + *countRead = 0; + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + ReadQueryRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->queryCookie = cookie; + request->bufferSize = bufferSize; + request->count = count; + // send the request + KernelRequestHandler handler(this, READ_QUERY_REPLY); + ReadQueryReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + if (reply->count < 0 || reply->count > count) + return B_BAD_DATA; + if ((int32)bufferSize < reply->buffer.GetSize()) + return B_BAD_DATA; + *countRead = reply->count; + if (*countRead > 0) { + // copy the buffer -- limit the number of bytes to copy + int32 maxBytes = *countRead + * (sizeof(struct dirent) + B_FILE_NAME_LENGTH); + int32 copyBytes = reply->buffer.GetSize(); + if (copyBytes > maxBytes) + copyBytes = maxBytes; + memcpy(buffer, reply->buffer.GetData(), copyBytes); + } + _SendReceiptAck(port); + return error; +} + +// #pragma mark - +// #pragma mark ----- private implementations ----- + +// _Mount +status_t +Volume::_Mount(const char* device, ulong flags, const char* parameters, + int32 len) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + + // get the current working directory + char cwd[B_PATH_NAME_LENGTH]; + if (!getcwd(cwd, sizeof(cwd))) + return errno; + + // prepare the request + RequestAllocator allocator(port->GetPort()); + MountVolumeRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->nsid = fID; + error = allocator.AllocateString(request->cwd, cwd); + if (error == B_OK) + error = allocator.AllocateString(request->device, device); + request->flags = flags; + if (error == B_OK) + error = allocator.AllocateData(request->parameters, parameters, len, 1); + if (error != B_OK) + return error; + + // send the request + KernelRequestHandler handler(this, MOUNT_VOLUME_REPLY); + MountVolumeReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + + // process the reply + if (reply->error != B_OK) + return reply->error; + fRootID = reply->rootID; + fUserlandVolume = reply->volume; + + // enable vnode counting + fVNodeCountMap = new(nothrow) VNodeCountMap; + if (fVNodeCountMap) + fVNodeCountingEnabled = true; + else + ERROR(("Failed to allocate vnode count map.")); + return error; +} + +// _Unmount +status_t +Volume::_Unmount() +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + UnmountVolumeRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + // send the request + KernelRequestHandler handler(this, UNMOUNT_VOLUME_REPLY); + UnmountVolumeReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// _WriteVNode +status_t +Volume::_WriteVNode(void* node, char reenter) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + WriteVNodeRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->reenter = reenter; + // send the request + KernelRequestHandler handler(this, WRITE_VNODE_REPLY); + WriteVNodeReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// _Close +status_t +Volume::_Close(void* node, void* cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + CloseRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->fileCookie = cookie; + // send the request + KernelRequestHandler handler(this, CLOSE_REPLY); + CloseReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// _FreeCookie +status_t +Volume::_FreeCookie(void* node, void* cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + FreeCookieRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->fileCookie = cookie; + // send the request + KernelRequestHandler handler(this, FREE_COOKIE_REPLY); + FreeCookieReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// _CloseDir +status_t +Volume::_CloseDir(void* node, void* cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + CloseDirRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->dirCookie = cookie; + // send the request + KernelRequestHandler handler(this, CLOSE_DIR_REPLY); + CloseDirReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// _FreeDirCookie +status_t +Volume::_FreeDirCookie(void* node, void* cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + FreeDirCookieRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->dirCookie = cookie; + // send the request + KernelRequestHandler handler(this, FREE_DIR_COOKIE_REPLY); + FreeDirCookieReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// _Walk +status_t +Volume::_Walk(void* dir, const char* entryName, char** resolvedPath, + vnode_id* vnid) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + WalkRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = dir; + error = allocator.AllocateString(request->entryName, entryName); + request->traverseLink = resolvedPath; + if (error != B_OK) + return error; + // send the request + KernelRequestHandler handler(this, WALK_REPLY); + WalkReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + *vnid = reply->vnid; + if (resolvedPath) { + const char* readPath = (const char*)reply->resolvedPath.GetData(); + if (readPath) { + int32 len = strnlen(readPath, reply->resolvedPath.GetSize()); + *resolvedPath = (char*)malloc(len + 1); + if (*resolvedPath) { + memcpy(*resolvedPath, readPath, len); + (*resolvedPath)[len] = '\0'; + } else + error = B_NO_MEMORY; + _SendReceiptAck(port); + } else + _DecrementVNodeCount(*vnid); + } else + _DecrementVNodeCount(*vnid); + return error; +} + +// _CloseAttrDir +status_t +Volume::_CloseAttrDir(void* node, void* cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + CloseAttrDirRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->attrDirCookie = cookie; + // send the request + KernelRequestHandler handler(this, CLOSE_ATTR_DIR_REPLY); + CloseAttrDirReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// _FreeAttrDirCookie +status_t +Volume::_FreeAttrDirCookie(void* node, void* cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + FreeAttrDirCookieRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->node = node; + request->attrDirCookie = cookie; + // send the request + KernelRequestHandler handler(this, FREE_ATTR_DIR_COOKIE_REPLY); + FreeAttrDirCookieReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// _CloseIndexDir +status_t +Volume::_CloseIndexDir(void* cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + CloseIndexDirRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->indexDirCookie = cookie; + // send the request + KernelRequestHandler handler(this, CLOSE_INDEX_DIR_REPLY); + CloseIndexDirReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// _FreeIndexDirCookie +status_t +Volume::_FreeIndexDirCookie(void* cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + FreeIndexDirCookieRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->indexDirCookie = cookie; + // send the request + KernelRequestHandler handler(this, FREE_INDEX_DIR_COOKIE_REPLY); + FreeIndexDirCookieReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// _CloseQuery +status_t +Volume::_CloseQuery(void* cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + CloseQueryRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->queryCookie = cookie; + // send the request + KernelRequestHandler handler(this, CLOSE_QUERY_REPLY); + CloseQueryReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// _FreeQueryCookie +status_t +Volume::_FreeQueryCookie(void* cookie) +{ + // get a free port + RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); + if (!port) + return B_ERROR; + PortReleaser _(fFileSystem->GetPortPool(), port); + // prepare the request + RequestAllocator allocator(port->GetPort()); + FreeQueryCookieRequest* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->volume = fUserlandVolume; + request->queryCookie = cookie; + // send the request + KernelRequestHandler handler(this, FREE_QUERY_COOKIE_REPLY); + FreeQueryCookieReply* reply; + error = _SendRequest(port, &allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// _SendRequest +status_t +Volume::_SendRequest(RequestPort* port, RequestAllocator* allocator, + RequestHandler* handler, Request** reply) +{ + if (!fFileSystem->IsUserlandServerThread()) + return port->SendRequest(allocator, handler, reply); + // Here it gets dangerous: a thread of the userland server team being here + // calls for trouble. We try receiving the request with a timeout, and + // close the port -- which will disconnect the whole FS. + status_t error = port->SendRequest(allocator, handler, reply, + kUserlandServerlandPortTimeout); + if (error == B_TIMED_OUT || error == B_WOULD_BLOCK) + port->Close(); + return error; +} + +// _SendReceiptAck +status_t +Volume::_SendReceiptAck(RequestPort* port) +{ + RequestAllocator allocator(port->GetPort()); + ReceiptAckReply* request; + status_t error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + return port->SendRequest(&allocator); +} + +// _IncrementVNodeCount +void +Volume::_IncrementVNodeCount(vnode_id vnid) +{ + if (!fVNodeCountingEnabled) + return; + AutoLocker _(fVNodeCountMap); + if (!fVNodeCountingEnabled) // someone may have changed it + return; + // get the counter + int32* count = fVNodeCountMap->Get(vnid); + if (!count) { + // vnode not known yet: create and add a new counter + count = new(nothrow) int32(0); + if (!count) { + ERROR(("Volume::_IncrementVNodeCount(): Failed to allocate " + "counter. Disabling vnode counting.\n")); + fVNodeCountingEnabled = false; + return; + } + if (fVNodeCountMap->Put(vnid, count) != B_OK) { + ERROR(("Volume::_IncrementVNodeCount(): Failed to add counter. " + "Disabling vnode counting.\n")); + delete count; + fVNodeCountingEnabled = false; + return; + } + } + // increment the counter + (*count)++; +//PRINT(("_IncrementVNodeCount(%Ld): count: %ld, fVNodeCountMap size: %ld\n", vnid, *count, fVNodeCountMap->Size())); +} + +// _DecrementVNodeCount +void +Volume::_DecrementVNodeCount(vnode_id vnid) +{ + if (!fVNodeCountingEnabled) + return; + AutoLocker _(fVNodeCountMap); + if (!fVNodeCountingEnabled) // someone may have changed it + return; + int32* count = fVNodeCountMap->Get(vnid); + if (!count) { + // that should never happen + ERROR(("Volume::_DecrementVNodeCount(): Failed to get counter. " + "Disabling vnode counting.\n")); + fVNodeCountingEnabled = false; + return; + } + (*count)--; +//int32 tmpCount = *count; + if (*count == 0) + fVNodeCountMap->Remove(vnid); +//PRINT(("_DecrementVNodeCount(%Ld): count: %ld, fVNodeCountMap size: %ld\n", vnid, tmpCount, fVNodeCountMap->Size())); +} + +// _InternalIOCtl +status_t +Volume::_InternalIOCtl(userlandfs_ioctl* buffer, int32 bufferSize) +{ + if (buffer->version != USERLAND_IOCTL_CURRENT_VERSION) + return B_BAD_VALUE; + status_t result = B_OK; + switch (buffer->command) { + case USERLAND_IOCTL_PUT_ALL_PENDING_VNODES: + result = _PutAllPendingVNodes(); + break; + default: + return B_BAD_VALUE; + } + buffer->error = result; + return B_OK; +} + +// _PutAllPendingVNodes +status_t +Volume::_PutAllPendingVNodes() +{ +PRINT(("Volume::_PutAllPendingVNodes()\n")); + if (!fFileSystem->GetPortPool()->IsDisconnected()) { + PRINT(("Volume::_PutAllPendingVNodes() failed: still connected\n")); + return USERLAND_IOCTL_STILL_CONNECTED; + } + if (!fVNodeCountingEnabled) { + PRINT(("Volume::_PutAllPendingVNodes() failed: vnode counting " + "disabled\n")); + return USERLAND_IOCTL_VNODE_COUNTING_DISABLED; + } + { + AutoLocker _(fVNodeCountMap); + if (!fVNodeCountingEnabled) {// someone may have changed it + PRINT(("Volume::_PutAllPendingVNodes() failed: vnode counting " + "disabled\n")); + return USERLAND_IOCTL_VNODE_COUNTING_DISABLED; + } + // Check whether there are open entities at the moment. + if (fOpenFiles > 0) { + PRINT(("Volume::_PutAllPendingVNodes() failed: open files\n")); + return USERLAND_IOCTL_OPEN_FILES; + } + if (fOpenDirectories > 0) { + PRINT(("Volume::_PutAllPendingVNodes() failed: open dirs\n")); + return USERLAND_IOCTL_OPEN_DIRECTORIES; + } + if (fOpenAttributeDirectories > 0) { + PRINT(("Volume::_PutAllPendingVNodes() failed: open attr dirs\n")); + return USERLAND_IOCTL_OPEN_ATTRIBUTE_DIRECTORIES; + } + if (fOpenIndexDirectories > 0) { + PRINT(("Volume::_PutAllPendingVNodes() failed: open index dirs\n")); + return USERLAND_IOCTL_OPEN_INDEX_DIRECTORIES; + } + if (fOpenQueries > 0) { + PRINT(("Volume::_PutAllPendingVNodes() failed: open queries\n")); + return USERLAND_IOCTL_OPEN_QUERIES; + } + // No open entities. Since the port pool is disconnected, no new + // entities can be opened. Disable node counting and put all pending + // vnodes. + fVNodeCountingEnabled = false; + } + int32 putVNodeCount = 0; + for (VNodeCountMap::Iterator it = fVNodeCountMap->GetIterator(); + it.HasNext();) { + VNodeCountMap::Entry entry = it.Next(); + int32 count = *entry.value; + for (int32 i = 0; i < count; i++) { + PutVNode(entry.key.value); + putVNodeCount++; + } + } + PRINT(("Volume::_PutAllPendingVNodes() successful: Put %ld vnodes\n", + putVNodeCount)); + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Volume.h b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Volume.h new file mode 100644 index 0000000000..459c218796 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Volume.h @@ -0,0 +1,207 @@ +// Volume.h + +#ifndef USERLAND_FS_VOLUME_H +#define USERLAND_FS_VOLUME_H + +#include + +#include "Referencable.h" + +namespace UserlandFSUtil { + +class Request; +class RequestAllocator; +class RequestHandler; + +} + +using UserlandFSUtil::Request; +using UserlandFSUtil::RequestAllocator; +using UserlandFSUtil::RequestHandler; + +class FileSystem; +struct userlandfs_ioctl; + +class Volume : public Referencable { +public: + Volume(FileSystem* fileSystem, nspace_id id); + ~Volume(); + + FileSystem* GetFileSystem() const; + nspace_id GetID() const; + + void* GetUserlandVolume() const; + vnode_id GetRootID() const; + bool IsMounting() const; + + // client methods + status_t GetVNode(vnode_id vnid, void** node); + status_t PutVNode(vnode_id vnid); + status_t NewVNode(vnode_id vnid, void* node); + status_t RemoveVNode(vnode_id vnid); + status_t UnremoveVNode(vnode_id vnid); + status_t IsVNodeRemoved(vnode_id vnid); + + // FS + status_t Mount(const char* device, ulong flags, + const char* parameters, int32 len); + status_t Unmount(); + status_t Sync(); + status_t ReadFSStat(fs_info* info); + status_t WriteFSStat(struct fs_info* info, long mask); + + // vnodes + status_t ReadVNode(vnode_id vnid, char reenter, + void** node); + status_t WriteVNode(void* node, char reenter); + status_t RemoveVNode(void* node, char reenter); + + // nodes + status_t FSync(void* node); + status_t ReadStat(void* node, struct stat* st); + status_t WriteStat(void* node, struct stat *st, + long mask); + status_t Access(void* node, int mode); + + // files + status_t Create(void* dir, const char* name, + int openMode, int mode, vnode_id* vnid, + void** cookie); + status_t Open(void* node, int openMode, void** cookie); + status_t Close(void* node, void* cookie); + status_t FreeCookie(void* node, void* cookie); + status_t Read(void* node, void* cookie, off_t pos, + void* buffer, size_t bufferSize, + size_t* bytesRead); + status_t Write(void* node, void* cookie, off_t pos, + const void* buffer, size_t bufferSize, + size_t* bytesWritten); + status_t IOCtl(void* node, void* cookie, int command, + void *buffer, size_t size); + status_t SetFlags(void* node, void* cookie, int flags); + status_t Select(void* node, void* cookie, uint8 event, + uint32 ref, selectsync* sync); + status_t Deselect(void* node, void* cookie, uint8 event, + selectsync* sync); + + // hard links / symlinks + status_t Link(void* dir, const char* name, void* node); + status_t Unlink(void* dir, const char* name); + status_t Symlink(void* dir, const char* name, + const char* target); + status_t ReadLink(void* node, char* buffer, + size_t bufferSize, size_t* bytesRead); + status_t Rename(void* oldDir, const char* oldName, + void* newDir, const char* newName); + + // directories + status_t MkDir(void* dir, const char* name, int mode); + status_t RmDir(void* dir, const char* name); + status_t OpenDir(void* node, void** cookie); + status_t CloseDir(void* node, void* cookie); + status_t FreeDirCookie(void* node, void* cookie); + status_t ReadDir(void* node, void* cookie, + void* buffer, size_t bufferSize, + int32 count, int32* countRead); + status_t RewindDir(void* node, void* cookie); + status_t Walk(void* dir, const char* entryName, + char** resolvedPath, vnode_id* vnid); + + // attributes + status_t OpenAttrDir(void* node, void** cookie); + status_t CloseAttrDir(void* node, void* cookie); + status_t FreeAttrDirCookie(void* node, void* cookie); + status_t ReadAttrDir(void* node, void* cookie, + void* buffer, size_t bufferSize, + int32 count, int32* countRead); + status_t RewindAttrDir(void* node, void* cookie); + status_t ReadAttr(void* node, const char* name, + int type, off_t pos, void* buffer, + size_t bufferSize, size_t* bytesRead); + status_t WriteAttr(void* node, const char* name, + int type, off_t pos, const void* buffer, + size_t bufferSize, size_t* bytesWritten); + status_t RemoveAttr(void* node, const char* name); + status_t RenameAttr(void* node, const char* oldName, + const char* newName); + status_t StatAttr(void* node, const char* name, + struct attr_info* attrInfo); + + // indices + status_t OpenIndexDir(void** cookie); + status_t CloseIndexDir(void* cookie); + status_t FreeIndexDirCookie(void* cookie); + status_t ReadIndexDir(void* cookie, void* buffer, + size_t bufferSize, int32 count, + int32* countRead); + status_t RewindIndexDir(void* cookie); + status_t CreateIndex(const char* name, int type, + int flags); + status_t RemoveIndex(const char* name); + status_t RenameIndex(const char* oldName, + const char* newName); + status_t StatIndex(const char *name, + struct index_info* indexInfo); + + // queries + status_t OpenQuery(const char* queryString, + ulong flags, port_id port, long token, + void** cookie); + status_t CloseQuery(void* cookie); + status_t FreeQueryCookie(void* cookie); + status_t ReadQuery(void* cookie, void* buffer, + size_t bufferSize, int32 count, + int32* countRead); + +private: + status_t _Mount(const char* device, ulong flags, + const char* parameters, int32 len); + status_t _Unmount(); + status_t _WriteVNode(void* node, char reenter); + status_t _Close(void* node, void* cookie); + status_t _FreeCookie(void* node, void* cookie); + status_t _CloseDir(void* node, void* cookie); + status_t _FreeDirCookie(void* node, void* cookie); + status_t _Walk(void* dir, const char* entryName, + char** resolvedPath, vnode_id* vnid); + status_t _CloseAttrDir(void* node, void* cookie); + status_t _FreeAttrDirCookie(void* node, void* cookie); + status_t _CloseIndexDir(void* cookie); + status_t _FreeIndexDirCookie(void* cookie); + status_t _CloseQuery(void* cookie); + status_t _FreeQueryCookie(void* cookie); + + status_t _SendRequest(RequestPort* port, + RequestAllocator* allocator, + RequestHandler* handler, Request** reply); + status_t _SendReceiptAck(RequestPort* port); + + void _IncrementVNodeCount(vnode_id vnid); + void _DecrementVNodeCount(vnode_id vnid); + + status_t _InternalIOCtl(userlandfs_ioctl* buffer, + int32 bufferSize); + + status_t _PutAllPendingVNodes(); + +private: + struct MountVNodeMap; + struct VNodeCountMap; + class AutoIncrementer; + + FileSystem* fFileSystem; + nspace_id fID; + void* fUserlandVolume; + vnode_id fRootID; + void* fRootNode; + MountVNodeMap* fMountVNodes; + vint32 fOpenFiles; + vint32 fOpenDirectories; + vint32 fOpenAttributeDirectories; + vint32 fOpenIndexDirectories; + vint32 fOpenQueries; + VNodeCountMap* fVNodeCountMap; + volatile bool fVNodeCountingEnabled; +}; + +#endif // USERLAND_FS_VOLUME_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/kernel-cpp.cpp b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/kernel-cpp.cpp new file mode 100644 index 0000000000..5e88c21b74 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/kernel-cpp.cpp @@ -0,0 +1,16 @@ +/* cpp - C++ in the kernel +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + + +#include "kernel-cpp.h" + +#include +#include + +FILE * stderr = NULL; + +extern "C" int fprintf(FILE *f, const char *format, ...) { return 0; } +extern "C" void abort() { panic("abort() called!"); } diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/kernel-cpp.h b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/kernel-cpp.h new file mode 100644 index 0000000000..c776a85b27 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/kernel-cpp.h @@ -0,0 +1,48 @@ +#ifndef KERNEL_CPP_H +#define KERNEL_CPP_H +/* cpp - C++ in the kernel +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + +#ifdef __cplusplus + +#include +#include + + +// Oh no! C++ in the kernel! Are you nuts? +// +// - no exceptions +// - (almost) no virtuals (well, the Query code now uses them) +// - it's basically only the C++ syntax, and type checking +// - since one tend to encapsulate everything in classes, it has a slightly +// higher memory overhead +// - nicer code +// - easier to maintain + + +inline void *operator new(size_t size, const nothrow_t&) throw() +{ + return malloc(size); +} + +inline void *operator new[](size_t size, const nothrow_t&) throw() +{ + return malloc(size); +} + +inline void operator delete(void *ptr) +{ + free(ptr); +} + +inline void operator delete[](void *ptr) +{ + free(ptr); +} + +#endif // __cplusplus + +#endif /* KERNEL_CPP_H */ diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/kernel_interface.cpp b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/kernel_interface.cpp new file mode 100644 index 0000000000..c94f29fddf --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/kernel_interface.cpp @@ -0,0 +1,1140 @@ +// kernel_interface.cpp + +#include +#include + +#include "Debug.h" +#include "FileSystem.h" +#include "String.h" +#include "UserlandFS.h" +#include "Volume.h" + +#if USER + +// disable_interrupts +static inline +cpu_status +disable_interrupts() +{ + return 0; +} + +// restore_interrupts +static inline +void +restore_interrupts(cpu_status status) +{ +} + +#endif // USER + +// #pragma mark - +// #pragma mark ----- prototypes ----- + +extern "C" { + +// fs +static int userlandfs_mount(nspace_id nsid, const char *device, ulong flags, + void *parameters, size_t len, void **data, vnode_id *rootID); +static int userlandfs_unmount(void *ns); +static int userlandfs_initialize(const char *deviceName, void *parameters, + size_t len); +static int userlandfs_sync(void *ns); +static int userlandfs_read_fs_stat(void *ns, struct fs_info *info); +static int userlandfs_write_fs_stat(void *ns, struct fs_info *info, long mask); + +// vnodes +static int userlandfs_read_vnode(void *ns, vnode_id vnid, char reenter, + void **node); +static int userlandfs_write_vnode(void *ns, void *node, char reenter); +static int userlandfs_remove_vnode(void *ns, void *node, char reenter); + +// nodes +static int userlandfs_fsync(void *ns, void *node); +static int userlandfs_read_stat(void *ns, void *node, struct stat *st); +static int userlandfs_write_stat(void *ns, void *node, struct stat *st, + long mask); +static int userlandfs_access(void *ns, void *node, int mode); + +// files +static int userlandfs_create(void *ns, void *dir, const char *name, + int openMode, int mode, vnode_id *vnid, void **cookie); +static int userlandfs_open(void *ns, void *node, int openMode, void **cookie); +static int userlandfs_close(void *ns, void *node, void *cookie); +static int userlandfs_free_cookie(void *ns, void *node, void *cookie); +static int userlandfs_read(void *ns, void *node, void *cookie, off_t pos, + void *buffer, size_t *bufferSize); +static int userlandfs_write(void *ns, void *node, void *cookie, off_t pos, + const void *buffer, size_t *bufferSize); +static int userlandfs_ioctl(void *ns, void *node, void *cookie, int cmd, + void *buffer, size_t bufferSize); +static int userlandfs_setflags(void *ns, void *node, void *cookie, int flags); +static int userlandfs_select(void *ns, void *node, void *cookie, uint8 event, + uint32 ref, selectsync *sync); +static int userlandfs_deselect(void *ns, void *node, void *cookie, uint8 event, + selectsync *sync); + +// hard links / symlinks +static int userlandfs_link(void *ns, void *dir, const char *name, void *node); +static int userlandfs_unlink(void *ns, void *dir, const char *name); +static int userlandfs_symlink(void *ns, void *dir, const char *name, + const char *path); +static int userlandfs_read_link(void *ns, void *node, char *buffer, + size_t *bufferSize); +static int userlandfs_rename(void *ns, void *oldDir, const char *oldName, + void *newDir, const char *newName); + +// directories +static int userlandfs_mkdir(void *ns, void *dir, const char *name, int mode); +static int userlandfs_rmdir(void *ns, void *dir, const char *name); +static int userlandfs_open_dir(void *ns, void *node, void **cookie); +static int userlandfs_close_dir(void *ns, void *node, void *cookie); +static int userlandfs_free_dir_cookie(void *ns, void *node, void *cookie); +static int userlandfs_read_dir(void *ns, void *node, void *cookie, + long *count, struct dirent *buffer, size_t bufferSize); +static int userlandfs_rewind_dir(void *ns, void *node, void *cookie); +static int userlandfs_walk(void *ns, void *dir, const char *entryName, + char **resolvedPath, vnode_id *vnid); + +// attributes +static int userlandfs_open_attrdir(void *ns, void *node, void **cookie); +static int userlandfs_close_attrdir(void *ns, void *node, void *cookie); +static int userlandfs_free_attrdir_cookie(void *ns, void *node, void *cookie); +static int userlandfs_read_attrdir(void *ns, void *node, void *cookie, + long *count, struct dirent *buffer, size_t bufferSize); +static int userlandfs_read_attr(void *ns, void *node, const char *name, + int type, void *buffer, size_t *bufferSize, off_t pos); +static int userlandfs_rewind_attrdir(void *ns, void *node, void *cookie); +static int userlandfs_write_attr(void *ns, void *node, const char *name, + int type, const void *buffer, size_t *bufferSize, off_t pos); +static int userlandfs_remove_attr(void *ns, void *node, const char *name); +static int userlandfs_rename_attr(void *ns, void *node, const char *oldName, + const char *newName); +static int userlandfs_stat_attr(void *ns, void *node, const char *name, + struct attr_info *attrInfo); + +// indices +static int userlandfs_open_indexdir(void *ns, void **cookie); +static int userlandfs_close_indexdir(void *ns, void *cookie); +static int userlandfs_free_indexdir_cookie(void *ns, void *node, void *cookie); +static int userlandfs_read_indexdir(void *ns, void *cookie, long *count, + struct dirent *buffer, size_t bufferSize); +static int userlandfs_rewind_indexdir(void *ns, void *cookie); +static int userlandfs_create_index(void *ns, const char *name, int type, int flags); +static int userlandfs_remove_index(void *ns, const char *name); +static int userlandfs_rename_index(void *ns, const char *oldName, + const char *newName); +static int userlandfs_stat_index(void *ns, const char *name, + struct index_info *indexInfo); + +// queries +static int userlandfs_open_query(void *ns, const char *queryString, ulong flags, + port_id port, long token, void **cookie); +static int userlandfs_close_query(void *ns, void *cookie); +static int userlandfs_free_query_cookie(void *ns, void *node, void *cookie); +static int userlandfs_read_query(void *ns, void *cookie, long *count, + struct dirent *buffer, size_t bufferSize); + +} // extern "C" + +/* vnode_ops struct. Fill this in to tell the kernel how to call + functions in your driver. +*/ +vnode_ops fs_entry = { + &userlandfs_read_vnode, // read_vnode + &userlandfs_write_vnode, // write_vnode + &userlandfs_remove_vnode, // remove_vnode + NULL, // secure_vnode (not needed) + &userlandfs_walk, // walk + &userlandfs_access, // access + &userlandfs_create, // create + &userlandfs_mkdir, // mkdir + &userlandfs_symlink, // symlink + &userlandfs_link, // link + &userlandfs_rename, // rename + &userlandfs_unlink, // unlink + &userlandfs_rmdir, // rmdir + &userlandfs_read_link, // readlink + &userlandfs_open_dir, // opendir + &userlandfs_close_dir, // closedir + &userlandfs_free_dir_cookie, // free_dircookie + &userlandfs_rewind_dir, // rewinddir + &userlandfs_read_dir, // readdir + &userlandfs_open, // open file + &userlandfs_close, // close file + &userlandfs_free_cookie, // free cookie + &userlandfs_read, // read file + &userlandfs_write, // write file + NULL, // readv + NULL, // writev + &userlandfs_ioctl, // ioctl + &userlandfs_setflags, // setflags file + &userlandfs_read_stat, // read stat + &userlandfs_write_stat, // write stat + &userlandfs_fsync, // fsync + &userlandfs_initialize, // initialize + &userlandfs_mount, // mount + &userlandfs_unmount, // unmount + &userlandfs_sync, // sync + &userlandfs_read_fs_stat, // read fs stat + &userlandfs_write_fs_stat, // write fs stat + &userlandfs_select, // select + &userlandfs_deselect, // deselect + + &userlandfs_open_indexdir, // open index dir + &userlandfs_close_indexdir, // close index dir + &userlandfs_free_indexdir_cookie, // free index dir cookie + &userlandfs_rewind_indexdir, // rewind index dir + &userlandfs_read_indexdir, // read index dir + &userlandfs_create_index, // create index + &userlandfs_remove_index, // remove index + &userlandfs_rename_index, // rename index + &userlandfs_stat_index, // stat index + + &userlandfs_open_attrdir, // open attr dir + &userlandfs_close_attrdir, // close attr dir + &userlandfs_free_attrdir_cookie, // free attr dir cookie + &userlandfs_rewind_attrdir, // rewind attr dir + &userlandfs_read_attrdir, // read attr dir + &userlandfs_write_attr, // write attr + &userlandfs_read_attr, // read attr + &userlandfs_remove_attr, // remove attr + &userlandfs_rename_attr, // rename attr + &userlandfs_stat_attr, // stat attr + + &userlandfs_open_query, // open query + &userlandfs_close_query, // close query + &userlandfs_free_query_cookie, // free query cookie + &userlandfs_read_query, // read query +}; + +int32 api_version = B_CUR_FS_API_VERSION; + +// #pragma mark - +// #pragma mark ----- fs ----- + +// parse_parameters +static +status_t +parse_parameters(const void *_parameters, int32 _len, String &fsName, + const char **fsParameters, int32 *fsParameterLength) +{ + // check parameters + if (!_parameters || _len <= 0) + return B_BAD_VALUE; + const char* parameters = (const char*)_parameters; + int32 len = strnlen(parameters, _len); + // skip leading white space + for (; len > 0; parameters++, len--) { + if (*parameters != ' ' && *parameters != '\t' && *parameters != '\n') + break; + } + if (len == 0) + return B_BAD_VALUE; + // get the file system name + int32 fsNameLen = len; + for (int32 i = 0; i < len; i++) { + if (parameters[i] == ' ' || parameters[i] == '\t' + || parameters[i] == '\n') { + fsNameLen = i; + break; + } + } + fsName.SetTo(parameters, fsNameLen); + if (fsName.GetLength() == 0) { + exit_debugging(); + return B_NO_MEMORY; + } + parameters += fsNameLen; + len -= fsNameLen; + // skip leading white space of the FS parameters + for (; len > 0; parameters++, len--) { + if (*parameters != ' ' && *parameters != '\t' && *parameters != '\n') + break; + } + *fsParameters = parameters; + *fsParameterLength = len; + return B_OK; +} + +// userlandfs_mount +static +int +userlandfs_mount(nspace_id nsid, const char *device, ulong flags, + void *parameters, size_t len, void **data, vnode_id *rootID) +{ + status_t error = B_OK; + init_debugging(); + // get the parameters + String fsName; + const char* fsParameters; + int32 fsParameterLength; + error = parse_parameters(parameters, len, fsName, &fsParameters, + &fsParameterLength); + if (error != B_OK) { + exit_debugging(); + return error; + } + // make sure there is a UserlandFS we can work with + UserlandFS* userlandFS = NULL; + error = UserlandFS::RegisterUserlandFS(&userlandFS); + if (error != B_OK) { + exit_debugging(); + return error; + } + // get the file system + FileSystem* fileSystem = NULL; + error = userlandFS->RegisterFileSystem(fsName.GetString(), &fileSystem); + if (error != B_OK) { + UserlandFS::UnregisterUserlandFS(); + exit_debugging(); + return error; + } + // mount the volume + Volume* volume = NULL; + error = fileSystem->Mount(nsid, device, flags, fsParameters, + fsParameterLength, &volume); + if (error != B_OK) { + userlandFS->UnregisterFileSystem(fileSystem); + UserlandFS::UnregisterUserlandFS(); + exit_debugging(); + return error; + } + *data = volume; + *rootID = volume->GetRootID(); + return error; +} + +// userlandfs_unmount +static +int +userlandfs_unmount(void *ns) +{ + Volume* volume = (Volume*)ns; + FileSystem* fileSystem = volume->GetFileSystem(); + status_t error = volume->Unmount(); + // The error code the FS's unmount hook returns is completely irrelevant to + // the VFS. It considers the volume unmounted in any case. + volume->RemoveReference(); + UserlandFS::GetUserlandFS()->UnregisterFileSystem(fileSystem); + UserlandFS::UnregisterUserlandFS(); + exit_debugging(); + return error; +} + +// userlandfs_initialize +static +int +userlandfs_initialize(const char *deviceName, void *parameters, + size_t len) +{ + init_debugging(); + // get the parameters + String fsName; + const char* fsParameters; + int32 fsParameterLength; + status_t error = parse_parameters(parameters, len, fsName, &fsParameters, + &fsParameterLength); + // make sure there is a UserlandFS we can work with + UserlandFS* userlandFS = NULL; + error = UserlandFS::RegisterUserlandFS(&userlandFS); + if (error != B_OK) { + exit_debugging(); + return error; + } + // get the file system + FileSystem* fileSystem = NULL; + if (error == B_OK) + error = userlandFS->RegisterFileSystem(fsName.GetString(), &fileSystem); + // initialize the volume + if (error == B_OK) { + error = fileSystem->Initialize(deviceName, fsParameters, + fsParameterLength); + } + // cleanup + if (fileSystem) + userlandFS->UnregisterFileSystem(fileSystem); + UserlandFS::UnregisterUserlandFS(); + exit_debugging(); + return error; +} + +// userlandfs_sync +static +int +userlandfs_sync(void *ns) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_sync(%p)\n", ns)); + status_t error = volume->Sync(); + PRINT(("userlandfs_sync() done: %lx \n", error)); + return error; +} + +// userlandfs_read_fs_stat +static +int +userlandfs_read_fs_stat(void *ns, struct fs_info *info) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_read_fs_stat(%p, %p)\n", ns, info)); + status_t error = volume->ReadFSStat(info); + PRINT(("userlandfs_read_fs_stat() done: %lx \n", error)); + return error; +} + +// userlandfs_write_fs_stat +static +int +userlandfs_write_fs_stat(void *ns, struct fs_info *info, long mask) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_write_fs_stat(%p, %p, %ld)\n", ns, info, mask)); + status_t error = volume->WriteFSStat(info, mask); + PRINT(("userlandfs_write_fs_stat() done: %lx \n", error)); + return error; +} + +// #pragma mark - +// #pragma mark ----- vnodes ----- + +// userlandfs_read_vnode +static +int +userlandfs_read_vnode(void *ns, vnode_id vnid, char reenter, void **node) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_read_vnode(%p, %Ld, %d, %p)\n", ns, vnid, reenter, + node)); + status_t error = volume->ReadVNode(vnid, reenter, node); + PRINT(("userlandfs_read_vnode() done: (%lx, %p)\n", error, *node)); + return error; +} + +// userlandfs_write_vnode +static +int +userlandfs_write_vnode(void *ns, void *node, char reenter) +{ + Volume* volume = (Volume*)ns; +// DANGER: If dbg_printf() is used, this thread will enter another FS and +// even perform a write operation. The is dangerous here, since this hook +// may be called out of the other FSs, since, for instance a put_vnode() +// called from another FS may cause the VFS layer to free vnodes and thus +// invoke this hook. +// PRINT(("userlandfs_write_vnode(%p, %p, %d)\n", ns, node, reenter)); + status_t error = volume->WriteVNode(node, reenter); +// PRINT(("userlandfs_write_vnode() done: %lx\n", error)); + return error; +} + +// userlandfs_remove_vnode +static +int +userlandfs_remove_vnode(void *ns, void *node, char reenter) +{ + Volume* volume = (Volume*)ns; +// DANGER: See userlandfs_write_vnode(). +// PRINT(("userlandfs_remove_vnode(%p, %p, %d)\n", ns, node, reenter)); + status_t error = volume->RemoveVNode(node, reenter); +// PRINT(("userlandfs_remove_vnode() done: %lx\n", error)); + return error; +} + +// #pragma mark - +// #pragma mark ----- nodes ----- + +// userlandfs_fsync +static +int +userlandfs_fsync(void *ns, void *node) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_fsync(%p, %p)\n", ns, node)); + status_t error = volume->FSync(node); + PRINT(("userlandfs_fsync() done: %lx\n", error)); + return error; +} + +// userlandfs_read_stat +static +int +userlandfs_read_stat(void *ns, void *node, struct stat *st) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_read_stat(%p, %p, %p)\n", ns, node, st)); + status_t error = volume->ReadStat(node, st); + PRINT(("userlandfs_read_stat() done: %lx\n", error)); + return error; +} + +// userlandfs_write_stat +static +int +userlandfs_write_stat(void *ns, void *node, struct stat *st, long mask) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_write_stat(%p, %p, %p, %ld)\n", ns, node, st, mask)); + status_t error = volume->WriteStat(node, st, mask); + PRINT(("userlandfs_write_stat() done: %lx\n", error)); + return error; +} + +// userlandfs_access +static +int +userlandfs_access(void *ns, void *node, int mode) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_access(%p, %p, %d)\n", ns, node, mode)); + status_t error = volume->Access(node, mode); + PRINT(("userlandfs_access() done: %lx\n", error)); + return error; +} + +// #pragma mark - +// #pragma mark ----- files ----- + +// userlandfs_create +static +int +userlandfs_create(void *ns, void *dir, const char *name, int openMode, int mode, + vnode_id *vnid, void **cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_create(%p, %p, `%s', %d, %d, %p, %p)\n", ns, dir, + name, openMode, mode, vnid, cookie)); + status_t error = volume->Create(dir, name, openMode, mode, vnid, cookie); + PRINT(("userlandfs_create() done: (%lx, %Ld, %p)\n", error, *vnid, + *cookie)); + return error; +} + +// userlandfs_open +static +int +userlandfs_open(void *ns, void *node, int openMode, void **cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_open(%p, %p, %d)\n", ns, node, openMode)); + status_t error = volume->Open(node, openMode, cookie); + PRINT(("userlandfs_open() done: (%lx, %p)\n", error, *cookie)); + return error; +} + +// userlandfs_close +static +int +userlandfs_close(void *ns, void *node, void *cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_close(%p, %p, %p)\n", ns, node, cookie)); + status_t error = volume->Close(node, cookie); + PRINT(("userlandfs_close() done: %lx\n", error)); + return error; +} + +// userlandfs_free_cookie +static +int +userlandfs_free_cookie(void *ns, void *node, void *cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_free_cookie(%p, %p, %p)\n", ns, node, cookie)); + status_t error = volume->FreeCookie(node, cookie); + PRINT(("userlandfs_free_cookie() done: %lx\n", error)); + return error; +} + +// userlandfs_read +static +int +userlandfs_read(void *ns, void *node, void *cookie, off_t pos, void *buffer, + size_t *bufferSize) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_read(%p, %p, %p, %Ld, %p, %lu)\n", ns, node, cookie, pos, + buffer, *bufferSize)); + status_t error = volume->Read(node, cookie, pos, buffer, *bufferSize, + bufferSize); + PRINT(("userlandfs_read() done: (%lx, %lu)\n", error, *bufferSize)); + return error; +} + +// userlandfs_write +static +int +userlandfs_write(void *ns, void *node, void *cookie, off_t pos, + const void *buffer, size_t *bufferSize) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_write(%p, %p, %p, %Ld, %p, %lu)\n", ns, node, cookie, pos, + buffer, *bufferSize)); + status_t error = volume->Write(node, cookie, pos, buffer, *bufferSize, + bufferSize); + PRINT(("userlandfs_write() done: (%lx, %lu)\n", error, *bufferSize)); + return error; +} + +// userlandfs_ioctl +static +int +userlandfs_ioctl(void *ns, void *node, void *cookie, int cmd, void *buffer, + size_t bufferSize) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_ioctl(%p, %p, %p, %d, %p, %lu)\n", ns, node, cookie, cmd, + buffer, bufferSize)); + status_t error = volume->IOCtl(node, cookie, cmd, buffer, bufferSize); + PRINT(("userlandfs_ioctl() done: (%lx)\n", error)); + return error; +} + +// userlandfs_setflags +static +int +userlandfs_setflags(void *ns, void *node, void *cookie, int flags) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_setflags(%p, %p, %p, %d)\n", ns, node, cookie, flags)); + status_t error = volume->SetFlags(node, cookie, flags); + PRINT(("userlandfs_setflags() done: (%lx)\n", error)); + return error; +} + +// userlandfs_select +static +int +userlandfs_select(void *ns, void *node, void *cookie, uint8 event, uint32 ref, + selectsync *sync) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_select(%p, %p, %p, %hhd, %lu, %p)\n", ns, node, cookie, + event, ref, sync)); + status_t error = volume->Select(node, cookie, event, ref, sync); + PRINT(("userlandfs_select() done: (%lx)\n", error)); + return error; +} + +// userlandfs_deselect +static +int +userlandfs_deselect(void *ns, void *node, void *cookie, uint8 event, + selectsync *sync) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_deselect(%p, %p, %p, %hhd, %p)\n", ns, node, cookie, + event, sync)); + status_t error = volume->Deselect(node, cookie, event, sync); + PRINT(("userlandfs_deselect() done: (%lx)\n", error)); + return error; +} + +// #pragma mark - +// #pragma mark ----- hard links / symlinks ----- + +// userlandfs_link +static +int +userlandfs_link(void *ns, void *dir, const char *name, void *node) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_link(%p, %p, `%s', %p)\n", ns, dir, name, node)); + status_t error = volume->Link(dir, name, node); + PRINT(("userlandfs_link() done: (%lx)\n", error)); + return error; +} + +// userlandfs_unlink +static +int +userlandfs_unlink(void *ns, void *dir, const char *name) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_unlink(%p, %p, `%s')\n", ns, dir, name)); + status_t error = volume->Unlink(dir, name); + PRINT(("userlandfs_unlink() done: (%lx)\n", error)); + return error; +} + +// userlandfs_symlink +static +int +userlandfs_symlink(void *ns, void *dir, const char *name, const char *path) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_symlink(%p, %p, `%s', `%s')\n", ns, dir, name, path)); + status_t error = volume->Symlink(dir, name, path); + PRINT(("userlandfs_symlink() done: (%lx)\n", error)); + return error; +} + +// userlandfs_read_link +static +int +userlandfs_read_link(void *ns, void *node, char *buffer, size_t *bufferSize) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_read_link(%p, %p, %p, %lu)\n", ns, node, buffer, + *bufferSize)); + status_t error = volume->ReadLink(node, buffer, *bufferSize, bufferSize); + PRINT(("userlandfs_read_link() done: (%lx, %lu)\n", error, *bufferSize)); + return error; +} + +// userlandfs_rename +static +int +userlandfs_rename(void *ns, void *oldDir, const char *oldName, void *newDir, + const char *newName) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_rename(%p, %p, `%s', %p, `%s')\n", ns, oldDir, oldName, + newDir, newName)); + status_t error = volume->Rename(oldDir, oldName, newDir, newName); + PRINT(("userlandfs_rename() done: (%lx)\n", error)); + return error; +} + +// #pragma mark - +// #pragma mark ----- directories ----- + +// userlandfs_mkdir +static +int +userlandfs_mkdir(void *ns, void *dir, const char *name, int mode) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_mkdir(%p, %p, `%s', %d)\n", ns, dir, name, mode)); + status_t error = volume->MkDir(dir, name, mode); + PRINT(("userlandfs_mkdir() done: (%lx)\n", error)); + return error; +} + +// userlandfs_rmdir +static +int +userlandfs_rmdir(void *ns, void *dir, const char *name) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_rmdir(%p, %p, `%s')\n", ns, dir, name)); + status_t error = volume->RmDir(dir, name); + PRINT(("userlandfs_rmdir() done: (%lx)\n", error)); + return error; +} + +// userlandfs_open_dir +static +int +userlandfs_open_dir(void *ns, void *node, void **cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_open_dir(%p, %p)\n", ns, node)); + status_t error = volume->OpenDir(node, cookie); + PRINT(("userlandfs_open_dir() done: (%lx, %p)\n", error, *cookie)); + return error; +} + +// userlandfs_close_dir +static +int +userlandfs_close_dir(void *ns, void *node, void *cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_close_dir(%p, %p, %p)\n", ns, node, cookie)); + status_t error = volume->CloseDir(node, cookie); + PRINT(("userlandfs_close_dir() done: %lx\n", error)); + return error; +} + +// userlandfs_free_dir_cookie +static +int +userlandfs_free_dir_cookie(void *ns, void *node, void *cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_free_dir_cookie(%p, %p, %p)\n", ns, node, cookie)); + status_t error = volume->FreeDirCookie(node, cookie); + PRINT(("userlandfs_free_dir_cookie() done: %lx \n", error)); + return error; +} + +// userlandfs_read_dir +static +int +userlandfs_read_dir(void *ns, void *node, void *cookie, long *count, + struct dirent *buffer, size_t bufferSize) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_read_dir(%p, %p, %p, %ld, %p, %lu)\n", ns, node, cookie, + *count, buffer, bufferSize)); + status_t error = volume->ReadDir(node, cookie, buffer, bufferSize, *count, + count); + PRINT(("userlandfs_read_dir() done: (%lx, %ld)\n", error, *count)); + #if DEBUG + dirent* entry = buffer; + for (int32 i = 0; i < *count; i++) { + // R5's kernel vsprintf() doesn't seem to know `%.s', so + // we need to work around. + char name[B_FILE_NAME_LENGTH]; + int nameLen = strnlen(entry->d_name, B_FILE_NAME_LENGTH - 1); + strncpy(name, entry->d_name, nameLen); + name[nameLen] = '\0'; + PRINT((" entry: d_dev: %ld, d_pdev: %ld, d_ino: %Ld, d_pino: %Ld, " + "d_reclen: %hu, d_name: `%s'\n", + entry->d_dev, entry->d_pdev, entry->d_ino, entry->d_pino, + entry->d_reclen, name)); + entry = (dirent*)((char*)entry + entry->d_reclen); + } + #endif + return error; +} + +// userlandfs_rewind_dir +static +int +userlandfs_rewind_dir(void *ns, void *node, void *cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_rewind_dir(%p, %p, %p)\n", ns, node, cookie)); + status_t error = volume->RewindDir(node, cookie); + PRINT(("userlandfs_rewind_dir() done: %lx\n", error)); + return error; +} + +// userlandfs_walk +static +int +userlandfs_walk(void *ns, void *dir, const char *entryName, + char **resolvedPath, vnode_id *vnid) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_walk(%p, %p, `%s', %p, %p)\n", ns, dir, + entryName, resolvedPath, vnid)); + status_t error = volume->Walk(dir, entryName, resolvedPath, vnid); + PRINT(("userlandfs_walk() done: (%lx, `%s', %Ld)\n", error, + (resolvedPath ? *resolvedPath : NULL), *vnid)); + return error; +} + +// #pragma mark - +// #pragma mark ----- attributes ----- + +// userlandfs_open_attrdir +static +int +userlandfs_open_attrdir(void *ns, void *node, void **cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_open_attrdir(%p, %p)\n", ns, node)); + status_t error = volume->OpenAttrDir(node, cookie); + PRINT(("userlandfs_open_attrdir() done: (%lx, %p)\n", error, *cookie)); + return error; +} + +// userlandfs_close_attrdir +static +int +userlandfs_close_attrdir(void *ns, void *node, void *cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_close_attrdir(%p, %p, %p)\n", ns, node, cookie)); + status_t error = volume->CloseAttrDir(node, cookie); + PRINT(("userlandfs_close_attrdir() done: (%lx)\n", error)); + return error; +} + +// userlandfs_free_attrdir_cookie +static +int +userlandfs_free_attrdir_cookie(void *ns, void *node, void *cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_free_attrdir_cookie(%p, %p, %p)\n", ns, node, cookie)); + status_t error = volume->FreeAttrDirCookie(node, cookie); + PRINT(("userlandfs_free_attrdir_cookie() done: (%lx)\n", error)); + return error; +} + +// userlandfs_read_attrdir +static +int +userlandfs_read_attrdir(void *ns, void *node, void *cookie, long *count, + struct dirent *buffer, size_t bufferSize) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_read_attrdir(%p, %p, %p, %ld, %p, %lu)\n", ns, node, + cookie, *count, buffer, bufferSize)); + status_t error = volume->ReadAttrDir(node, cookie, buffer, bufferSize, + *count, count); + PRINT(("userlandfs_read_attrdir() done: (%lx, %ld)\n", error, *count)); + return error; +} + +// userlandfs_rewind_attrdir +static +int +userlandfs_rewind_attrdir(void *ns, void *node, void *cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_rewind_attrdir(%p, %p, %p)\n", ns, node, cookie)); + status_t error = volume->RewindAttrDir(node, cookie); + PRINT(("userlandfs_rewind_attrdir() done: (%lx)\n", error)); + return error; +} + +// userlandfs_read_attr +static +int +userlandfs_read_attr(void *ns, void *node, const char *name, int type, + void *buffer, size_t *bufferSize, off_t pos) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_read_attr(%p, %p, `%s', %d, %p, %lu, %Ld)\n", ns, node, + name, type, buffer, *bufferSize, pos)); + status_t error = volume->ReadAttr(node, name, type, pos, buffer, + *bufferSize, bufferSize); + PRINT(("userlandfs_read_attr() done: (%lx, %ld)\n", error, *bufferSize)); + return error; +} + +// userlandfs_write_attr +static +int +userlandfs_write_attr(void *ns, void *node, const char *name, int type, + const void *buffer, size_t *bufferSize, off_t pos) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_write_attr(%p, %p, `%s', %d, %p, %lu, %Ld)\n", ns, node, + name, type, buffer, *bufferSize, pos)); + status_t error = volume->WriteAttr(node, name, type, pos, buffer, + *bufferSize, bufferSize); + PRINT(("userlandfs_write_attr() done: (%lx, %ld)\n", error, *bufferSize)); + return error; +} + +// userlandfs_remove_attr +static +int +userlandfs_remove_attr(void *ns, void *node, const char *name) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_remove_attr(%p, %p, `%s')\n", ns, node, name)); + status_t error = volume->RemoveAttr(node, name); + PRINT(("userlandfs_remove_attr() done: (%lx)\n", error)); + return error; +} + +// userlandfs_rename_attr +static +int +userlandfs_rename_attr(void *ns, void *node, const char *oldName, + const char *newName) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_rename_attr(%p, %p, `%s', `%s')\n", ns, node, oldName, + newName)); + status_t error = volume->RenameAttr(node, oldName, newName); + PRINT(("userlandfs_rename_attr() done: (%lx)\n", error)); + return error; +} + +// userlandfs_stat_attr +static +int +userlandfs_stat_attr(void *ns, void *node, const char *name, + struct attr_info *attrInfo) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_stat_attr(%p, %p, `%s', %p)\n", ns, node, name, + attrInfo)); + status_t error = volume->StatAttr(node, name, attrInfo); + PRINT(("userlandfs_stat_attr() done: (%lx)\n", error)); + return error; +} + +// #pragma mark - +// #pragma mark ----- indices ----- + +// userlandfs_open_indexdir +static +int +userlandfs_open_indexdir(void *ns, void **cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_open_indexdir(%p, %p)\n", ns, cookie)); + status_t error = volume->OpenIndexDir(cookie); + PRINT(("userlandfs_open_indexdir() done: (%lx, %p)\n", error, *cookie)); + return error; +} + +// userlandfs_close_indexdir +static +int +userlandfs_close_indexdir(void *ns, void *cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_close_indexdir(%p, %p)\n", ns, cookie)); + status_t error = volume->CloseIndexDir(cookie); + PRINT(("userlandfs_close_indexdir() done: (%lx)\n", error)); + return error; +} + +// userlandfs_free_indexdir_cookie +static +int +userlandfs_free_indexdir_cookie(void *ns, void *node, void *cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_free_indexdir_cookie(%p, %p)\n", ns, cookie)); + status_t error = volume->FreeIndexDirCookie(cookie); + PRINT(("userlandfs_free_indexdir_cookie() done: (%lx)\n", error)); + return error; +} + +// userlandfs_read_indexdir +static +int +userlandfs_read_indexdir(void *ns, void *cookie, long *count, + struct dirent *buffer, size_t bufferSize) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_read_indexdir(%p, %p, %ld, %p, %lu)\n", ns, cookie, + *count, buffer, bufferSize)); + status_t error = volume->ReadIndexDir(cookie, buffer, bufferSize, + *count, count); + PRINT(("userlandfs_read_indexdir() done: (%lx, %ld)\n", error, *count)); + return error; +} + +// userlandfs_rewind_indexdir +static +int +userlandfs_rewind_indexdir(void *ns, void *cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_rewind_indexdir(%p, %p)\n", ns, cookie)); + status_t error = volume->RewindIndexDir(cookie); + PRINT(("userlandfs_rewind_indexdir() done: (%lx)\n", error)); + return error; +} + +// userlandfs_create_index +static +int +userlandfs_create_index(void *ns, const char *name, int type, int flags) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_create_index(%p, `%s', %d, %d)\n", ns, name, type, + flags)); + status_t error = volume->CreateIndex(name, type, flags); + PRINT(("userlandfs_create_index() done: (%lx)\n", error)); + return error; +} + +// userlandfs_remove_index +static +int +userlandfs_remove_index(void *ns, const char *name) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_remove_index(%p, `%s')\n", ns, name)); + status_t error = volume->RemoveIndex(name); + PRINT(("userlandfs_remove_index() done: (%lx)\n", error)); + return error; +} + +// userlandfs_rename_index +static +int +userlandfs_rename_index(void *ns, const char *oldName, const char *newName) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_rename_index(%p, `%s', `%s')\n", ns, oldName, newName)); + status_t error = volume->RenameIndex(oldName, newName); + PRINT(("userlandfs_rename_index() done: (%lx)\n", error)); + return error; +} + +// userlandfs_stat_index +static +int +userlandfs_stat_index(void *ns, const char *name, struct index_info *indexInfo) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_stat_index(%p, `%s', %p)\n", ns, name, indexInfo)); + status_t error = volume->StatIndex(name, indexInfo); + PRINT(("userlandfs_stat_index() done: (%lx)\n", error)); + return error; +} + +// #pragma mark - +// #pragma mark ----- queries ----- + +// userlandfs_open_query +static +int +userlandfs_open_query(void *ns, const char *queryString, ulong flags, + port_id port, long token, void **cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_open_query(%p, `%s', %lu, %ld, %ld, %p)\n", ns, + queryString, flags, port, token, cookie)); + status_t error = volume->OpenQuery(queryString, flags, port, token, cookie); + PRINT(("userlandfs_open_query() done: (%lx, %p)\n", error, *cookie)); + return error; +} + +// userlandfs_close_query +static +int +userlandfs_close_query(void *ns, void *cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_close_query(%p, %p)\n", ns, cookie)); + status_t error = volume->CloseQuery(cookie); + PRINT(("userlandfs_close_query() done: (%lx)\n", error)); + return error; +} + +// userlandfs_free_query_cookie +static +int +userlandfs_free_query_cookie(void *ns, void *node, void *cookie) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_free_query_cookie(%p, %p)\n", ns, cookie)); + status_t error = volume->FreeQueryCookie(cookie); + PRINT(("userlandfs_free_query_cookie() done: (%lx)\n", error)); + return error; +} + +// userlandfs_read_query +static +int +userlandfs_read_query(void *ns, void *cookie, long *count, + struct dirent *buffer, size_t bufferSize) +{ + Volume* volume = (Volume*)ns; + PRINT(("userlandfs_read_query(%p, %p, %ld, %p, %lu)\n", ns, cookie, + *count, buffer, bufferSize)); + status_t error = volume->ReadQuery(cookie, buffer, bufferSize, + *count, count); + PRINT(("userlandfs_read_query() done: (%lx, %ld)\n", error, *count)); + #if DEBUG + if (*count > 0) { + // R5's kernel vsprintf() doesn't seem to know `%.s', so + // we need to work around. + char name[B_FILE_NAME_LENGTH]; + int nameLen = strnlen(buffer->d_name, B_FILE_NAME_LENGTH - 1); + strncpy(name, buffer->d_name, nameLen); + name[nameLen] = '\0'; + PRINT((" entry: d_dev: %ld, d_pdev: %ld, d_ino: %Ld, d_pino: %Ld, " + "d_reclen: %hu, d_name: `%s'\n", + buffer->d_dev, buffer->d_pdev, buffer->d_ino, buffer->d_pino, + buffer->d_reclen, name)); + } + #endif + return error; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/private/AreaSupport.cpp b/src/add-ons/kernel/file_systems/userlandfs/private/AreaSupport.cpp new file mode 100644 index 0000000000..5854206909 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/private/AreaSupport.cpp @@ -0,0 +1,34 @@ +// AreaSupport.cpp + +#include "AreaSupport.h" + +// get_area_for_address +status_t +UserlandFSUtil::get_area_for_address(void* address, int32 size, area_id* area, + int32* offset, void** areaBaseAddress) +{ + // check parameters + if (!area || !offset || size < 0) + return B_BAD_VALUE; + // catch NULL address case + if (!address) { + *area = -1; + *offset = 0; + return B_OK; + } + // get area and in-area offset + *area = area_for(address); + if (*area < 0) + return *area; + area_info areaInfo; + status_t error = get_area_info(*area, &areaInfo); + if (error != B_OK) + return error; + // check the size + *offset = (uint8*)address - (uint8*)areaInfo.address; + if (*offset + size > (int32)areaInfo.size) + return B_BAD_VALUE; + if (areaBaseAddress) + *areaBaseAddress = areaInfo.address; + return B_OK; +} diff --git a/src/add-ons/kernel/file_systems/userlandfs/private/DispatcherDefs.cpp b/src/add-ons/kernel/file_systems/userlandfs/private/DispatcherDefs.cpp new file mode 100644 index 0000000000..efeb096c27 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/private/DispatcherDefs.cpp @@ -0,0 +1,6 @@ +// DispatcherDefs.cpp + +#include "DispatcherDefs.h" + +const char* kUserlandFSDispatcherPortName = "userland fs dispatcher"; +const char* kUserlandFSDispatcherReplyPortName = "userland fs dispatcher reply"; diff --git a/src/add-ons/kernel/file_systems/userlandfs/private/Port.cpp b/src/add-ons/kernel/file_systems/userlandfs/private/Port.cpp new file mode 100644 index 0000000000..0dd6136b34 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/private/Port.cpp @@ -0,0 +1,203 @@ +// Port.cpp + +#include + +#include "AreaSupport.h" +#include "Compatibility.h" +#include "Port.h" + +// minimal and maximal port size +static const int32 kMinPortSize = 1024; // 1 kB +static const int32 kMaxPortSize = 64 * 1024; // 64 kB + +// constructor +Port::Port(int32 size) + : fBuffer(NULL), + fCapacity(0), + fMessageSize(0), + fInitStatus(B_NO_INIT), + fOwner(true) +{ + // adjust size to be within the sane bounds + if (size < kMinPortSize) + size = kMinPortSize; + else if (size > kMaxPortSize) + size = kMaxPortSize; + // allocate the buffer + fBuffer = new(nothrow) uint8[size]; + if (!fBuffer) { + fInitStatus = B_NO_MEMORY; + return; + } + // create the owner port + fInfo.owner_port = create_port(1, "port owner port"); + if (fInfo.owner_port < 0) { + fInitStatus = fInfo.owner_port; + return; + } + // create the client port + fInfo.client_port = create_port(1, "port client port"); + if (fInfo.client_port < 0) { + fInitStatus = fInfo.client_port; + return; + } + fInfo.size = size; + fCapacity = size; + fInitStatus = B_OK; +} + +// constructor +Port::Port(const Info* info) + : fBuffer(NULL), + fCapacity(0), + fMessageSize(0), + fInitStatus(B_NO_INIT), + fOwner(false) +{ + // check parameters + if (!info || info->owner_port < 0 || info->client_port < 0 + || info->size < kMinPortSize || info->size > kMaxPortSize) { + return; + } + // allocate the buffer + fBuffer = new(nothrow) uint8[info->size]; + if (!fBuffer) { + fInitStatus = B_NO_MEMORY; + return; + } + // init the info + fInfo.owner_port = info->owner_port; + fInfo.client_port = info->client_port; + fInfo.size = info->size; + // init the other members + fCapacity = info->size; + fInitStatus = B_OK; +} + +// destructor +Port::~Port() +{ + Close(); + delete[] fBuffer; +} + +// Close +void +Port::Close() +{ + if (fInitStatus != B_OK) + return; + fInitStatus = B_NO_INIT; + // delete the ports only if we are the owner + if (fOwner) { + if (fInfo.owner_port >= 0) + delete_port(fInfo.owner_port); + if (fInfo.client_port >= 0) + delete_port(fInfo.client_port); + } + fInfo.owner_port = -1; + fInfo.client_port = -1; +} + +// InitCheck +status_t +Port::InitCheck() const +{ + return fInitStatus; +} + +// GetInfo +const Port::Info* +Port::GetInfo() const +{ + return &fInfo; +} + +// GetBuffer +void* +Port::GetBuffer() const +{ + return fBuffer; +} + +// GetCapacity +int32 +Port::GetCapacity() const +{ + return fCapacity; +} + +// GetMessage +void* +Port::GetMessage() const +{ + return (fInitStatus == B_OK && fMessageSize > 0 ? fBuffer : NULL); +} + +// GetMessageSize +int32 +Port::GetMessageSize() const +{ + return (fInitStatus == B_OK ? fMessageSize : 0); +} + +// Send +status_t +Port::Send(int32 size) +{ + if (fInitStatus != B_OK) + return fInitStatus; + if (size <= 0 || size > fCapacity) + return B_BAD_VALUE; + fMessageSize = 0; + port_id port = (fOwner ? fInfo.client_port : fInfo.owner_port); + status_t error; + do { + error = write_port(port, 0, fBuffer, size); + } while (error == B_INTERRUPTED); + return (fInitStatus = error); +} + +// SendAndReceive +status_t +Port::SendAndReceive(int32 size) +{ + status_t error = Send(size); + if (error != B_OK) + return error; + return Receive(); +} + +// Receive +status_t +Port::Receive(bigtime_t timeout) +{ + if (fInitStatus != B_OK) + return fInitStatus; + port_id port = (fOwner ? fInfo.owner_port : fInfo.client_port); + status_t error = B_OK; + do { + int32 code; + ssize_t bytesRead; + if (timeout >= 0) { + bytesRead = read_port_etc(port, &code, fBuffer, fCapacity, + B_RELATIVE_TIMEOUT, timeout); + } else + bytesRead = read_port(port, &code, fBuffer, fCapacity); + if (bytesRead < 0) + error = bytesRead; + else + fMessageSize = bytesRead; + } while (error == B_INTERRUPTED); + if (error == B_TIMED_OUT || error == B_WOULD_BLOCK) { + return error; + } + if (error != B_OK) + return (fInitStatus = error); + if (fMessageSize <= 0 || fMessageSize > fCapacity) { + fMessageSize = 0; + return B_BAD_DATA; + } + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/private/Request.cpp b/src/add-ons/kernel/file_systems/userlandfs/private/Request.cpp new file mode 100644 index 0000000000..e11e079f6f --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/private/Request.cpp @@ -0,0 +1,54 @@ +// Request.cpp + +#include "Request.h" + +// Address + +// constructor +Address::Address() + : fSize(0) +{ + fUnrelocated.area = -1; + fUnrelocated.offset = 0; +} + +// SetTo +void +Address::SetTo(area_id area, int32 offset, int32 size) +{ + fUnrelocated.area = area; + fUnrelocated.offset = offset; + fSize = size; +} + + +// Request + +// constructor +Request::Request(uint32 type) + : fType(type) +{ +} + +// GetType +uint32 +Request::GetType() const +{ + return fType; +} + +// Check +status_t +Request::Check() const +{ + return B_OK; +} + +// GetAddressInfos +status_t +Request::GetAddressInfos(AddressInfo* infos, int32* count) +{ + *count = 0; + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/private/RequestAllocator.cpp b/src/add-ons/kernel/file_systems/userlandfs/private/RequestAllocator.cpp new file mode 100644 index 0000000000..8fab1726b8 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/private/RequestAllocator.cpp @@ -0,0 +1,289 @@ +// RequestAllocator.cpp + +#include + +#include "AreaSupport.h" +#include "Compatibility.h" +#include "Debug.h" +#include "Port.h" +#include "RequestAllocator.h" + +// constructor +RequestAllocator::RequestAllocator(Port* port) + : fError(B_NO_INIT), + fPort(NULL), + fRequest(NULL), + fRequestSize(0), + fAllocatedAreaCount(0), + fDeferredInitInfoCount(0), + fRequestInPortBuffer(false) +{ + Init(port); +} + +// destructor +RequestAllocator::~RequestAllocator() +{ + Uninit(); +} + +// Init +status_t +RequestAllocator::Init(Port* port) +{ + Uninit(); + if (port) { + fPort = port; + fError = fPort->InitCheck(); + } + return fError; +} + +// Uninit +void +RequestAllocator::Uninit() +{ + if (!fRequestInPortBuffer) + delete[] (uint8*)fRequest; + for (int32 i = 0; i < fAllocatedAreaCount; i++) + delete_area(fAllocatedAreas[i]); + fAllocatedAreaCount = 0; + for (int32 i = 0; i < fDeferredInitInfoCount; i++) { + if (fDeferredInitInfos[i].inPortBuffer) + delete[] fDeferredInitInfos[i].data; + } + fDeferredInitInfoCount = 0; + fError = B_NO_INIT; + fPort = NULL; + fRequest = NULL; + fRequestSize = 0; +} + +// Error +status_t +RequestAllocator::Error() const +{ + return fError; +} + +// FinishDeferredInit +void +RequestAllocator::FinishDeferredInit() +{ + if (fError != B_OK) + return; + for (int32 i = 0; i < fDeferredInitInfoCount; i++) { + DeferredInitInfo& info = fDeferredInitInfos[i]; + if (info.inPortBuffer) { + if (info.size > 0) + memcpy((uint8*)fRequest + info.offset, info.data, info.size); + delete[] info.data; + } + PRINT(("RequestAllocator::FinishDeferredInit(): area: %ld, " + "offset: %ld, size: %ld\n", info.area, info.offset, info.size)); + info.target->SetTo(info.area, info.offset, info.size); + } + fDeferredInitInfoCount = 0; +} + +// AllocateRequest +status_t +RequestAllocator::AllocateRequest(int32 size) +{ + if (fError != B_OK) + RETURN_ERROR(fError); + if (size < (int32)sizeof(Request) || size > fPort->GetCapacity()) + RETURN_ERROR(fError = B_BAD_VALUE); + fRequest = (Request*)fPort->GetBuffer(); + fRequestSize = size; + fRequestInPortBuffer = true; + return B_OK; +} + +// ReadRequest +status_t +RequestAllocator::ReadRequest() +{ + if (fError != B_OK) + RETURN_ERROR(fError); + if (fPort->GetMessageSize() < (int32)sizeof(Request)) + RETURN_ERROR(fError = B_BAD_DATA); + // clone the request + fRequest = (Request*)new(nothrow) uint8[fPort->GetMessageSize()]; + if (!fRequest) + RETURN_ERROR(fError = B_NO_MEMORY); + memcpy(fRequest, fPort->GetMessage(), fPort->GetMessageSize()); + fRequestSize = fPort->GetMessageSize(); + fRequestInPortBuffer = false; + // relocate the request + fError = relocate_request(fRequest, fRequestSize, fAllocatedAreas, + &fAllocatedAreaCount); + RETURN_ERROR(fError); +} + +// GetRequest +Request* +RequestAllocator::GetRequest() const +{ + return fRequest; +} + +// GetRequestSize +int32 +RequestAllocator::GetRequestSize() const +{ + return fRequestSize; +} + +// AllocateAddress +status_t +RequestAllocator::AllocateAddress(Address& address, int32 size, int32 align, + void** data, bool deferredInit) +{ + if (fError != B_OK) + return fError; + if (!fRequest) + RETURN_ERROR(B_NO_INIT); + if (size < 0) + RETURN_ERROR(B_BAD_VALUE); + if (fDeferredInitInfoCount >= MAX_REQUEST_ADDRESS_COUNT) + RETURN_ERROR(B_BAD_VALUE); + // fix the alignment -- valid is 1, 2, 4, 8 + if (align <= 0 || size == 0 || (align & 0x1)) + align = 1; + else if (align & 0x2) + align = 2; + else if (align & 0x4) + align = 4; + else + align = 8; + // check address location + // Currently we only support relocation of addresses inside the + // port buffer. + int32 addressOffset = (uint8*)&address - (uint8*)fRequest; + if (addressOffset < (int32)sizeof(Request) + || addressOffset + (int32)sizeof(Address) > fRequestSize) { + RETURN_ERROR(B_BAD_VALUE); + } + // get the next free aligned offset in the port buffer + int32 offset = (fRequestSize + align - 1) / align * align; + // allocate the data + if (offset + size <= fPort->GetCapacity()) { + // there's enough free space in the port buffer + fRequestSize = offset + size; + if (deferredInit) { + DeferredInitInfo& info + = fDeferredInitInfos[fDeferredInitInfoCount]; + if (size > 0) { + info.data = new(nothrow) uint8[size]; + if (!info.data) + RETURN_ERROR(B_NO_MEMORY); + } else + info.data = NULL; + info.area = -1; + info.offset = offset; + info.size = size; + info.inPortBuffer = true; + info.target = &address; + *data = info.data; + fDeferredInitInfoCount++; + } else { + *data = (uint8*)fRequest + offset; + address.SetTo(-1, offset, size); + } + } else { + // not enough room in the port's buffer: we need to allocate an area + if (fAllocatedAreaCount >= MAX_REQUEST_ADDRESS_COUNT) + RETURN_ERROR(B_ERROR); + int32 areaSize = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE; + area_id area = create_area("request data", data, +#ifdef _KERNEL_MODE + B_ANY_KERNEL_ADDRESS, +#else + B_ANY_ADDRESS, +#endif + areaSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); + if (area < 0) + RETURN_ERROR(area); + fAllocatedAreas[fAllocatedAreaCount++] = area; + if (deferredInit) { + DeferredInitInfo& info + = fDeferredInitInfos[fDeferredInitInfoCount]; + info.data = NULL; + info.area = area; + info.offset = 0; + info.size = size; + info.inPortBuffer = false; + info.target = &address; + fDeferredInitInfoCount++; +PRINT((" RequestAllocator::AllocateAddress(): deferred allocated area: " +"%ld, size: %ld (%ld), data: %p\n", area, size, areaSize, *data)); + } else + address.SetTo(area, 0, size); + } + return B_OK; +} + +// AllocateData +status_t +RequestAllocator::AllocateData(Address& address, const void* data, int32 size, + int32 align, bool deferredInit) +{ + void* destination; + status_t error = AllocateAddress(address, size, align, &destination, + deferredInit); + if (error != B_OK) + return error; + if (size > 0) + memcpy(destination, data, size); + return error; +} + +// AllocateString +status_t +RequestAllocator::AllocateString(Address& address, const char* data, + bool deferredInit) +{ + int32 size = (data ? strlen(data) + 1 : 0); + return AllocateData(address, data, size, 1, deferredInit); +} + +// SetAddress +/*status_t +RequestAllocator::SetAddress(Address& address, void* data, int32 size) +{ + if (fError != B_OK) + return fError; + if (!fRequest) + return (fError = B_NO_INIT); + // check address location + // Currently we only support relocation of addresses inside the + // port buffer. + int32 addressOffset = (uint8*)&address - (uint8*)fRequest; + if (addressOffset < (int32)sizeof(Request) + || addressOffset + (int32)sizeof(Address) > fRequestSize) { + return (fError = B_BAD_VALUE); + } + // if data does itself lie within the port buffer, we store only the + // request relative offset + int32 inRequestOffset = (uint8*)data - (uint8*)fRequest; + if (!data) { + address.SetTo(-1, 0, 0); + } else if (inRequestOffset >= (int32)sizeof(Request) + && inRequestOffset <= fRequestSize) { + if (inRequestOffset + size > fRequestSize) + return (fError = B_BAD_VALUE); + address.SetTo(-1, inRequestOffset, size); + } else { + // get the area and in-area offset for the address + area_id area; + int32 offset; + fError = get_area_for_address(data, size, &area, &offset); + if (fError != B_OK) + return fError; + // set the address + address.SetTo(area, offset, size); + } + return fError; +}*/ + diff --git a/src/add-ons/kernel/file_systems/userlandfs/private/RequestHandler.cpp b/src/add-ons/kernel/file_systems/userlandfs/private/RequestHandler.cpp new file mode 100644 index 0000000000..89b7438922 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/private/RequestHandler.cpp @@ -0,0 +1,30 @@ +// RequestHandler.cpp + +#include "RequestHandler.h" + +// constructor +RequestHandler::RequestHandler() + : fPort(NULL), + fDone(false) +{ +} + +// destructor +RequestHandler::~RequestHandler() +{ +} + +// SetPort +void +RequestHandler::SetPort(RequestPort* port) +{ + fPort = port; +} + +// IsDone +bool +RequestHandler::IsDone() const +{ + return fDone; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/private/RequestPort.cpp b/src/add-ons/kernel/file_systems/userlandfs/private/RequestPort.cpp new file mode 100644 index 0000000000..ed6c55c2b3 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/private/RequestPort.cpp @@ -0,0 +1,204 @@ +// RequestPort.cpp + +#include + +#include "AutoDeleter.h" +#include "Debug.h" +#include "Request.h" +#include "RequestHandler.h" +#include "RequestPort.h" + +// TODO: Limit the stacking of requests? + +// AllocatorNode +struct RequestPort::AllocatorNode { + AllocatorNode(Port* port) : allocator(port), previous(NULL) {} + + RequestAllocator allocator; + AllocatorNode* previous; +}; + + +// constructor +RequestPort::RequestPort(int32 size) + : fPort(size), + fCurrentAllocatorNode(NULL) +{ +} + +// constructor +RequestPort::RequestPort(const Port::Info* info) + : fPort(info), + fCurrentAllocatorNode(NULL) +{ +} + +// destructor +RequestPort::~RequestPort() +{ + while (fCurrentAllocatorNode) + _PopAllocator(); +} + +// Close +void +RequestPort::Close() +{ + fPort.Close(); +} + +// InitCheck +status_t +RequestPort::InitCheck() const +{ + return fPort.InitCheck(); +} + +// GetPort +Port* +RequestPort::GetPort() +{ + return &fPort; +} + +// GetPortInfo +const Port::Info* +RequestPort::GetPortInfo() const +{ + return fPort.GetInfo(); +} + +// SendRequest +status_t +RequestPort::SendRequest(RequestAllocator* allocator) +{ + // check initialization and parameters + if (InitCheck() != B_OK) + RETURN_ERROR(InitCheck()); + if (!allocator || allocator->GetRequest() != fPort.GetBuffer() + || allocator->GetRequestSize() < (int32)sizeof(Request) + || allocator->GetRequestSize() > fPort.GetCapacity()) { + RETURN_ERROR(B_BAD_VALUE); + } + allocator->FinishDeferredInit(); +//PRINT(("RequestPort::SendRequest(%lu)\n", allocator->GetRequest()->GetType())); +#if USER && !KERNEL_EMU + if (!is_userland_request(allocator->GetRequest()->GetType())) { + ERROR(("RequestPort::SendRequest(%lu): request is not a userland " + "request\n", allocator->GetRequest()->GetType())); + debugger("Request is not a userland request."); + } +#else + if (!is_kernel_request(allocator->GetRequest()->GetType())) { + ERROR(("RequestPort::SendRequest(%lu): request is not a userland " + "request\n", allocator->GetRequest()->GetType())); + debugger("Request is not a userland request."); + } +#endif + RETURN_ERROR(fPort.Send(allocator->GetRequestSize())); +} + +// SendRequest +status_t +RequestPort::SendRequest(RequestAllocator* allocator, + RequestHandler* handler, Request** reply, bigtime_t timeout) +{ + status_t error = SendRequest(allocator); + if (error != B_OK) + return error; + return HandleRequests(handler, reply, timeout); +} + +// ReceiveRequest +// +// The caller is responsible for calling ReleaseRequest() with the request. +status_t +RequestPort::ReceiveRequest(Request** request, bigtime_t timeout) +{ + // check initialization and parameters + if (InitCheck() != B_OK) + RETURN_ERROR(InitCheck()); + if (!request) + RETURN_ERROR(B_BAD_VALUE); + // allocate a request allocator + AllocatorNode* node = new(nothrow) AllocatorNode(&fPort); + if (!node) + RETURN_ERROR(B_NO_MEMORY); + ObjectDeleter deleter(node); + // receive the message + status_t error = fPort.Receive(timeout); + if (error != B_OK) { + if (error != B_TIMED_OUT && error != B_WOULD_BLOCK) + RETURN_ERROR(error); + return error; + } + // allocate the request + error = node->allocator.ReadRequest(); + if (error != B_OK) + RETURN_ERROR(error); + // everything went fine: push the allocator + *request = node->allocator.GetRequest(); + node->previous = fCurrentAllocatorNode; + fCurrentAllocatorNode = node; + deleter.Detach(); +//PRINT(("RequestPort::RequestReceived(%lu)\n", (*request)->GetType())); + return B_OK; +} + +// HandleRequests +// +// If request is not NULL, the caller is responsible for calling +// ReleaseRequest() with the request. If it is NULL, the request will already +// be gone, when the method returns. +status_t +RequestPort::HandleRequests(RequestHandler* handler, Request** request, + bigtime_t timeout) +{ + // check initialization and parameters + if (InitCheck() != B_OK) + RETURN_ERROR(InitCheck()); + if (!handler) + RETURN_ERROR(B_BAD_VALUE); + handler->SetPort(this); + Request* currentRequest = NULL; + do { + if (currentRequest) + ReleaseRequest(currentRequest); + status_t error = ReceiveRequest(¤tRequest, timeout); + if (error != B_OK) + return error; + // handle the request + error = handler->HandleRequest(currentRequest); + if (error != B_OK) { + ReleaseRequest(currentRequest); + RETURN_ERROR(error); + } + } while (!handler->IsDone()); + if (request) + *request = currentRequest; + else + ReleaseRequest(currentRequest); + return B_OK; +} + +// ReleaseRequest +void +RequestPort::ReleaseRequest(Request* request) +{ + if (request && fCurrentAllocatorNode + && request == fCurrentAllocatorNode->allocator.GetRequest()) { + _PopAllocator(); + } +} + +// _PopAllocator +void +RequestPort::_PopAllocator() +{ + if (fCurrentAllocatorNode) { + AllocatorNode* node = fCurrentAllocatorNode->previous; + delete fCurrentAllocatorNode; + fCurrentAllocatorNode = node; + } +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/private/Requests.cpp b/src/add-ons/kernel/file_systems/userlandfs/private/Requests.cpp new file mode 100644 index 0000000000..119e8fd065 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/private/Requests.cpp @@ -0,0 +1,869 @@ +// Requests.cpp + +#include + +#include "Debug.h" +#include "Requests.h" + +#define _ADD_ADDRESS(_address, _flags) \ + if (*count >= MAX_REQUEST_ADDRESS_COUNT) \ + return B_BAD_VALUE; \ + infos[*count].address = &_address; \ + infos[*count].flags = _flags; \ + infos[(*count)++].max_size = LONG_MAX; // TODO:... + +#define ADD_ADDRESS(address) _ADD_ADDRESS(address, 0) +#define ADD_STRING(address) _ADD_ADDRESS(address, ADDRESS_IS_STRING) +#define ADD_NON_NULL_ADDRESS(address) _ADD_ADDRESS(address, ADDRESS_NOT_NULL) +#define ADD_NON_NULL_STRING(address) \ + _ADD_ADDRESS(address, (ADDRESS_IS_STRING | ADDRESS_NOT_NULL)) + +// FSConnectRequest +status_t +FSConnectRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(fsName); + return B_OK; +} + +// FSConnectReply +status_t +FSConnectReply::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_ADDRESS(portInfos); + return B_OK; +} + +// MountVolumeRequest +status_t +MountVolumeRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(cwd); + ADD_STRING(device); + ADD_STRING(parameters); + return B_OK; +} + +// InitializeVolumeRequest +status_t +InitializeVolumeRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_STRING(device); + ADD_STRING(parameters); + return B_OK; +} + +// CreateRequest +status_t +CreateRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(name); + return B_OK; +} + +// ReadReply +status_t +ReadReply::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_ADDRESS(buffer); + return B_OK; +} + +// WriteRequest +status_t +WriteRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_ADDRESS(buffer); + return B_OK; +} + +// IOCtlRequest +status_t +IOCtlRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_ADDRESS(buffer); + return B_OK; +} + +// IOCtlReply +status_t +IOCtlReply::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_ADDRESS(buffer); + return B_OK; +} + +// LinkRequest +status_t +LinkRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(name); + return B_OK; +} + +// UnlinkRequest +status_t +UnlinkRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(name); + return B_OK; +} + +// SymlinkRequest +status_t +SymlinkRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(name); + ADD_NON_NULL_STRING(target); + return B_OK; +} + +// ReadLinkReply +status_t +ReadLinkReply::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_STRING(buffer); + return B_OK; +} + +// RenameRequest +status_t +RenameRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(oldName); + ADD_NON_NULL_STRING(newName); + return B_OK; +} + +// MkDirRequest +status_t +MkDirRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(name); + return B_OK; +} + +// RmDirRequest +status_t +RmDirRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(name); + return B_OK; +} + +// ReadDirReply +status_t +ReadDirReply::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_ADDRESS(buffer); + return B_OK; +} + +// WalkRequest +status_t +WalkRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(entryName); + return B_OK; +} + +// WalkReply +status_t +WalkReply::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_STRING(resolvedPath); + return B_OK; +} + +// ReadAttrDirReply +status_t +ReadAttrDirReply::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_ADDRESS(buffer); + return B_OK; +} + +// ReadAttrRequest +status_t +ReadAttrRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(name); + return B_OK; +} + +// ReadAttrReply +status_t +ReadAttrReply::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_ADDRESS(buffer); + return B_OK; +} + +// WriteAttrRequest +status_t +WriteAttrRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(name); + ADD_ADDRESS(buffer); + return B_OK; +} + +// RemoveAttrRequest +status_t +RemoveAttrRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(name); + return B_OK; +} + +// RenameAttrRequest +status_t +RenameAttrRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(oldName); + ADD_NON_NULL_STRING(newName); + return B_OK; +} + +// StatAttrRequest +status_t +StatAttrRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(name); + return B_OK; +} + +// ReadIndexDirReply +status_t +ReadIndexDirReply::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_ADDRESS(buffer); + return B_OK; +} + +// CreateIndexRequest +status_t +CreateIndexRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(name); + return B_OK; +} + +// RemoveIndexRequest +status_t +RemoveIndexRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(name); + return B_OK; +} + +// RenameIndexRequest +status_t +RenameIndexRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(oldName); + ADD_NON_NULL_STRING(newName); + return B_OK; +} + +// StatIndexRequest +status_t +StatIndexRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(name); + return B_OK; +} + +// OpenQueryRequest +status_t +OpenQueryRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_NON_NULL_STRING(queryString); + return B_OK; +} + +// ReadQueryReply +status_t +ReadQueryReply::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_ADDRESS(buffer); + return B_OK; +} + +// NotifyListenerRequest +status_t +NotifyListenerRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_STRING(name); + return B_OK; +} + +// SendNotificationRequest +status_t +SendNotificationRequest::GetAddressInfos(AddressInfo* infos, int32* count) +{ + ADD_STRING(name); + return B_OK; +} + + +// #pragma mark - + +// RequestAddressInfoGetter +struct RequestAddressInfoGetter { + RequestAddressInfoGetter(AddressInfo* infos, int32* count) + : fInfos(infos), + fCount(count) + { + } + + template status_t operator()(R* request) + { + return request->GetAddressInfos(fInfos, fCount); + } + +private: + AddressInfo* fInfos; + int32* fCount; +}; + +// get_request_address_infos +status_t +UserlandFSUtil::get_request_address_infos(Request* request, AddressInfo* infos, + int32* count) +{ + if (!infos || !count) + return B_BAD_VALUE; + *count = 0; + RequestAddressInfoGetter task(infos, count); + return do_for_request(request, task); +} + +// RequestChecker +struct RequestChecker { + template status_t operator()(R* request) + { + return request->Check(); + } +}; + +// check_request +status_t +UserlandFSUtil::check_request(Request* request) +{ + RequestChecker task; + return do_for_request(request, task); +} + + +// is_error_reply +static inline +bool +is_error_reply(Request* request) +{ + return false; +} + +// is_error_reply +static inline +bool +is_error_reply(ReplyRequest* request) +{ + return (request->error != B_OK); +} + +// RequestRelocator +struct RequestRelocator { + RequestRelocator(int32 requestBufferSize, area_id* areas, int32* count) + : fRequestBufferSize(requestBufferSize), + fAreas(areas), + fAreaCount(count) + { + *fAreaCount = 0; + } + + ~RequestRelocator() + { + if (!fSuccess) { + for (int32 i = 0; i < *fAreaCount; i++) + delete_area(fAreas[i]); + } + } + + template status_t operator()(R* request) + { + // check the request buffer size + if (fRequestBufferSize < (int32)sizeof(R)) + RETURN_ERROR(B_BAD_DATA); + // no need to relocate the addresses of a reply that indicates an error + if (is_error_reply(request)) { + fSuccess = true; + return B_OK; + } + // get the address infos + AddressInfo infos[MAX_REQUEST_ADDRESS_COUNT]; + int32 count = 0; + status_t error = request->GetAddressInfos(infos, &count); + if (error != B_OK) + RETURN_ERROR(error); + // check and relocate the addresses + for (int32 i = 0; i < count; i++) { + // check + Address* address = infos[i].address; + int32 size = address->GetSize(); + int32 offset = address->GetOffset(); +//PRINT((" relocating address: area: %ld, offset: %ld, size: %ld...\n", +//address->GetArea(), offset, size)); + if (offset < 0 || size < 0 || size > infos[i].max_size) + RETURN_ERROR(B_BAD_DATA); + if ((infos[i].flags & ADDRESS_NOT_NULL) && size == 0) + RETURN_ERROR(B_BAD_DATA); + // relocate + area_id area = address->GetArea(); + if (area < 0) { + // data in the buffer itself + if (offset == 0 && size == 0) { +//PRINT((" -> relocated address: NULL\n")); + address->SetRelocatedAddress(NULL); + } else { + if (offset < (int32)sizeof(R) + || offset + size > fRequestBufferSize) { + RETURN_ERROR(B_BAD_DATA); + } +//PRINT((" -> relocated address: %p\n", (uint8*)request + offset)); + address->SetRelocatedAddress((uint8*)request + offset); + } + } else { + // clone the area + void* data; + area = clone_area("cloned request data", &data, +#ifdef _KERNEL_MODE + B_ANY_KERNEL_ADDRESS, +#else + B_ANY_ADDRESS, +#endif + B_READ_AREA, area); + if (area < 0) + RETURN_ERROR(area); + fAreas[(*fAreaCount)++] = area; + // check offset and size + area_info areaInfo; + error = get_area_info(area, &areaInfo); + if (error != B_OK) + RETURN_ERROR(error); + if (offset + size > (int32)areaInfo.size) + RETURN_ERROR(B_BAD_DATA); +//PRINT((" -> relocated address: %p\n", (uint8*)data + offset)); + address->SetRelocatedAddress((uint8*)data + offset); + } + } + // finally let the request check its integrity + error = request->Check(); + if (error != B_OK) + RETURN_ERROR(error); + fSuccess = true; +//PRINT(("RequestRelocator done: success\n")); + return B_OK; + } + +private: + int32 fRequestBufferSize; + area_id* fAreas; + int32* fAreaCount; + bool fSuccess; +}; + +// relocate_request +status_t +UserlandFSUtil::relocate_request(Request* request, int32 requestBufferSize, + area_id* areas, int32* count) +{ + if (!request || !areas || !count) + return B_BAD_VALUE; + RequestRelocator task(requestBufferSize, areas, count); + return do_for_request(request, task); +} + +// is_kernel_request +bool +UserlandFSUtil::is_kernel_request(uint32 type) +{ + switch (type) { + // kernel -> userland requests + // administrative + case UFS_DISCONNECT_REQUEST: + case FS_CONNECT_REQUEST: + return true; + case FS_CONNECT_REPLY: + return false; + // FS + case MOUNT_VOLUME_REQUEST: + case UNMOUNT_VOLUME_REQUEST: + case INITIALIZE_VOLUME_REQUEST: + case SYNC_VOLUME_REQUEST: + case READ_FS_STAT_REQUEST: + case WRITE_FS_STAT_REQUEST: + return true; + case MOUNT_VOLUME_REPLY: + case UNMOUNT_VOLUME_REPLY: + case INITIALIZE_VOLUME_REPLY: + case SYNC_VOLUME_REPLY: + case READ_FS_STAT_REPLY: + case WRITE_FS_STAT_REPLY: + return false; + // vnodes + case READ_VNODE_REQUEST: + case WRITE_VNODE_REQUEST: + case FS_REMOVE_VNODE_REQUEST: + return true; + case READ_VNODE_REPLY: + case WRITE_VNODE_REPLY: + case FS_REMOVE_VNODE_REPLY: + return false; + // nodes + case FSYNC_REQUEST: + case READ_STAT_REQUEST: + case WRITE_STAT_REQUEST: + case ACCESS_REQUEST: + return true; + case FSYNC_REPLY: + case READ_STAT_REPLY: + case WRITE_STAT_REPLY: + case ACCESS_REPLY: + return false; + // files + case CREATE_REQUEST: + case OPEN_REQUEST: + case CLOSE_REQUEST: + case FREE_COOKIE_REQUEST: + case READ_REQUEST: + case WRITE_REQUEST: + case IOCTL_REQUEST: + case SET_FLAGS_REQUEST: + case SELECT_REQUEST: + case DESELECT_REQUEST: + return true; + case CREATE_REPLY: + case OPEN_REPLY: + case CLOSE_REPLY: + case FREE_COOKIE_REPLY: + case READ_REPLY: + case WRITE_REPLY: + case IOCTL_REPLY: + case SET_FLAGS_REPLY: + case SELECT_REPLY: + case DESELECT_REPLY: + return false; + // hard links / symlinks + case LINK_REQUEST: + case UNLINK_REQUEST: + case SYMLINK_REQUEST: + case READ_LINK_REQUEST: + case RENAME_REQUEST: + return true; + case LINK_REPLY: + case UNLINK_REPLY: + case SYMLINK_REPLY: + case READ_LINK_REPLY: + case RENAME_REPLY: + return false; + // directories + case MKDIR_REQUEST: + case RMDIR_REQUEST: + case OPEN_DIR_REQUEST: + case CLOSE_DIR_REQUEST: + case FREE_DIR_COOKIE_REQUEST: + case READ_DIR_REQUEST: + case REWIND_DIR_REQUEST: + case WALK_REQUEST: + return true; + case MKDIR_REPLY: + case RMDIR_REPLY: + case OPEN_DIR_REPLY: + case CLOSE_DIR_REPLY: + case FREE_DIR_COOKIE_REPLY: + case READ_DIR_REPLY: + case REWIND_DIR_REPLY: + case WALK_REPLY: + return false; + // attributes + case OPEN_ATTR_DIR_REQUEST: + case CLOSE_ATTR_DIR_REQUEST: + case FREE_ATTR_DIR_COOKIE_REQUEST: + case READ_ATTR_DIR_REQUEST: + case REWIND_ATTR_DIR_REQUEST: + case READ_ATTR_REQUEST: + case WRITE_ATTR_REQUEST: + case REMOVE_ATTR_REQUEST: + case RENAME_ATTR_REQUEST: + case STAT_ATTR_REQUEST: + return true; + case OPEN_ATTR_DIR_REPLY: + case CLOSE_ATTR_DIR_REPLY: + case FREE_ATTR_DIR_COOKIE_REPLY: + case READ_ATTR_DIR_REPLY: + case REWIND_ATTR_DIR_REPLY: + case READ_ATTR_REPLY: + case WRITE_ATTR_REPLY: + case REMOVE_ATTR_REPLY: + case RENAME_ATTR_REPLY: + case STAT_ATTR_REPLY: + return false; + // indices + case OPEN_INDEX_DIR_REQUEST: + case CLOSE_INDEX_DIR_REQUEST: + case FREE_INDEX_DIR_COOKIE_REQUEST: + case READ_INDEX_DIR_REQUEST: + case REWIND_INDEX_DIR_REQUEST: + case CREATE_INDEX_REQUEST: + case REMOVE_INDEX_REQUEST: + case RENAME_INDEX_REQUEST: + case STAT_INDEX_REQUEST: + return true; + case OPEN_INDEX_DIR_REPLY: + case CLOSE_INDEX_DIR_REPLY: + case FREE_INDEX_DIR_COOKIE_REPLY: + case READ_INDEX_DIR_REPLY: + case REWIND_INDEX_DIR_REPLY: + case CREATE_INDEX_REPLY: + case REMOVE_INDEX_REPLY: + case RENAME_INDEX_REPLY: + case STAT_INDEX_REPLY: + return false; + // queries + case OPEN_QUERY_REQUEST: + case CLOSE_QUERY_REQUEST: + case FREE_QUERY_COOKIE_REQUEST: + case READ_QUERY_REQUEST: + return true; + case OPEN_QUERY_REPLY: + case CLOSE_QUERY_REPLY: + case FREE_QUERY_COOKIE_REPLY: + case READ_QUERY_REPLY: + return false; + + // userland -> kernel requests + // notifications + case NOTIFY_LISTENER_REQUEST: + case NOTIFY_SELECT_EVENT_REQUEST: + case SEND_NOTIFICATION_REQUEST: + return false; + case NOTIFY_LISTENER_REPLY: + case NOTIFY_SELECT_EVENT_REPLY: + case SEND_NOTIFICATION_REPLY: + return true; + // vnodes + case GET_VNODE_REQUEST: + case PUT_VNODE_REQUEST: + case NEW_VNODE_REQUEST: + case REMOVE_VNODE_REQUEST: + case UNREMOVE_VNODE_REQUEST: + case IS_VNODE_REMOVED_REQUEST: + return false; + case GET_VNODE_REPLY: + case PUT_VNODE_REPLY: + case NEW_VNODE_REPLY: + case REMOVE_VNODE_REPLY: + case UNREMOVE_VNODE_REPLY: + case IS_VNODE_REMOVED_REPLY: + return true; + + // general reply + case RECEIPT_ACK_REPLY: + return true; + default: + return false; + } +} + +// is_userland_request +bool +UserlandFSUtil::is_userland_request(uint32 type) +{ + switch (type) { + // kernel -> userland requests + // administrative + case UFS_DISCONNECT_REQUEST: + case FS_CONNECT_REQUEST: + return false; + case FS_CONNECT_REPLY: + return true; + // FS + case MOUNT_VOLUME_REQUEST: + case UNMOUNT_VOLUME_REQUEST: + case INITIALIZE_VOLUME_REQUEST: + case SYNC_VOLUME_REQUEST: + case READ_FS_STAT_REQUEST: + case WRITE_FS_STAT_REQUEST: + return false; + case MOUNT_VOLUME_REPLY: + case UNMOUNT_VOLUME_REPLY: + case INITIALIZE_VOLUME_REPLY: + case SYNC_VOLUME_REPLY: + case READ_FS_STAT_REPLY: + case WRITE_FS_STAT_REPLY: + return true; + // vnodes + case READ_VNODE_REQUEST: + case WRITE_VNODE_REQUEST: + case FS_REMOVE_VNODE_REQUEST: + return false; + case READ_VNODE_REPLY: + case WRITE_VNODE_REPLY: + case FS_REMOVE_VNODE_REPLY: + return true; + // nodes + case FSYNC_REQUEST: + case READ_STAT_REQUEST: + case WRITE_STAT_REQUEST: + case ACCESS_REQUEST: + return false; + case FSYNC_REPLY: + case READ_STAT_REPLY: + case WRITE_STAT_REPLY: + case ACCESS_REPLY: + return true; + // files + case CREATE_REQUEST: + case OPEN_REQUEST: + case CLOSE_REQUEST: + case FREE_COOKIE_REQUEST: + case READ_REQUEST: + case WRITE_REQUEST: + case IOCTL_REQUEST: + case SET_FLAGS_REQUEST: + case SELECT_REQUEST: + case DESELECT_REQUEST: + return false; + case CREATE_REPLY: + case OPEN_REPLY: + case CLOSE_REPLY: + case FREE_COOKIE_REPLY: + case READ_REPLY: + case WRITE_REPLY: + case IOCTL_REPLY: + case SET_FLAGS_REPLY: + case SELECT_REPLY: + case DESELECT_REPLY: + return true; + // hard links / symlinks + case LINK_REQUEST: + case UNLINK_REQUEST: + case SYMLINK_REQUEST: + case READ_LINK_REQUEST: + case RENAME_REQUEST: + return false; + case LINK_REPLY: + case UNLINK_REPLY: + case SYMLINK_REPLY: + case READ_LINK_REPLY: + case RENAME_REPLY: + return true; + // directories + case MKDIR_REQUEST: + case RMDIR_REQUEST: + case OPEN_DIR_REQUEST: + case CLOSE_DIR_REQUEST: + case FREE_DIR_COOKIE_REQUEST: + case READ_DIR_REQUEST: + case REWIND_DIR_REQUEST: + case WALK_REQUEST: + return false; + case MKDIR_REPLY: + case RMDIR_REPLY: + case OPEN_DIR_REPLY: + case CLOSE_DIR_REPLY: + case FREE_DIR_COOKIE_REPLY: + case READ_DIR_REPLY: + case REWIND_DIR_REPLY: + case WALK_REPLY: + return true; + // attributes + case OPEN_ATTR_DIR_REQUEST: + case CLOSE_ATTR_DIR_REQUEST: + case FREE_ATTR_DIR_COOKIE_REQUEST: + case READ_ATTR_DIR_REQUEST: + case REWIND_ATTR_DIR_REQUEST: + case READ_ATTR_REQUEST: + case WRITE_ATTR_REQUEST: + case REMOVE_ATTR_REQUEST: + case RENAME_ATTR_REQUEST: + case STAT_ATTR_REQUEST: + return false; + case OPEN_ATTR_DIR_REPLY: + case CLOSE_ATTR_DIR_REPLY: + case FREE_ATTR_DIR_COOKIE_REPLY: + case READ_ATTR_DIR_REPLY: + case REWIND_ATTR_DIR_REPLY: + case READ_ATTR_REPLY: + case WRITE_ATTR_REPLY: + case REMOVE_ATTR_REPLY: + case RENAME_ATTR_REPLY: + case STAT_ATTR_REPLY: + return true; + // indices + case OPEN_INDEX_DIR_REQUEST: + case CLOSE_INDEX_DIR_REQUEST: + case FREE_INDEX_DIR_COOKIE_REQUEST: + case READ_INDEX_DIR_REQUEST: + case REWIND_INDEX_DIR_REQUEST: + case CREATE_INDEX_REQUEST: + case REMOVE_INDEX_REQUEST: + case RENAME_INDEX_REQUEST: + case STAT_INDEX_REQUEST: + return false; + case OPEN_INDEX_DIR_REPLY: + case CLOSE_INDEX_DIR_REPLY: + case FREE_INDEX_DIR_COOKIE_REPLY: + case READ_INDEX_DIR_REPLY: + case REWIND_INDEX_DIR_REPLY: + case CREATE_INDEX_REPLY: + case REMOVE_INDEX_REPLY: + case RENAME_INDEX_REPLY: + case STAT_INDEX_REPLY: + return true; + // queries + case OPEN_QUERY_REQUEST: + case CLOSE_QUERY_REQUEST: + case FREE_QUERY_COOKIE_REQUEST: + case READ_QUERY_REQUEST: + return false; + case OPEN_QUERY_REPLY: + case CLOSE_QUERY_REPLY: + case FREE_QUERY_COOKIE_REPLY: + case READ_QUERY_REPLY: + return true; + + // userland -> kernel requests + // notifications + case NOTIFY_LISTENER_REQUEST: + case NOTIFY_SELECT_EVENT_REQUEST: + case SEND_NOTIFICATION_REQUEST: + return true; + case NOTIFY_LISTENER_REPLY: + case NOTIFY_SELECT_EVENT_REPLY: + case SEND_NOTIFICATION_REPLY: + return false; + // vnodes + case GET_VNODE_REQUEST: + case PUT_VNODE_REQUEST: + case NEW_VNODE_REQUEST: + case REMOVE_VNODE_REQUEST: + case UNREMOVE_VNODE_REQUEST: + case IS_VNODE_REMOVED_REQUEST: + return true; + case GET_VNODE_REPLY: + case PUT_VNODE_REPLY: + case NEW_VNODE_REPLY: + case REMOVE_VNODE_REPLY: + case UNREMOVE_VNODE_REPLY: + case IS_VNODE_REMOVED_REPLY: + return false; + + // general reply + case RECEIPT_ACK_REPLY: + return true; + default: + return false; + } +} diff --git a/src/add-ons/kernel/file_systems/userlandfs/private/SingleReplyRequestHandler.cpp b/src/add-ons/kernel/file_systems/userlandfs/private/SingleReplyRequestHandler.cpp new file mode 100644 index 0000000000..aedaeff94d --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/private/SingleReplyRequestHandler.cpp @@ -0,0 +1,39 @@ +// SingleReplyRequestHandler.cpp + +#include "Compatibility.h" +#include "Debug.h" +#include "Request.h" +#include "SingleReplyRequestHandler.h" + +// constructor +SingleReplyRequestHandler::SingleReplyRequestHandler() + : RequestHandler(), + fAcceptAnyRequest(true), + fExpectedReply(0) +{ +} + +// constructor +SingleReplyRequestHandler::SingleReplyRequestHandler(uint32 expectedReply) + : RequestHandler(), + fAcceptAnyRequest(false), + fExpectedReply(expectedReply) +{ +} + +// HandleRequest +status_t +SingleReplyRequestHandler::HandleRequest(Request* request) +{ + if (!fAcceptAnyRequest && request->GetType() != fExpectedReply) { +PRINT(("SingleReplyRequestHandler::HandleRequest(): unexpected request: %lu " +"expected was: %lu\n", request->GetType(), fExpectedReply)); +#if USER +debugger("SingleReplyRequestHandler::HandleRequest(): unexpected request!"); +#endif + return B_BAD_DATA; + } + fDone = true; + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/private/userlandfs_ioctl.cpp b/src/add-ons/kernel/file_systems/userlandfs/private/userlandfs_ioctl.cpp new file mode 100644 index 0000000000..fb13693ac6 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/private/userlandfs_ioctl.cpp @@ -0,0 +1,6 @@ +// userlandfs_ioctl.cpp + +#include "userlandfs_ioctl.h" + +const char kUserlandFSIOCtlMagic[USERLAND_IOCTL_MAGIC_LENGTH] + = "userlandfs mAGiC666"; diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/FSInfo.h b/src/add-ons/kernel/file_systems/userlandfs/server/FSInfo.h new file mode 100644 index 0000000000..c74879d95c --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/FSInfo.h @@ -0,0 +1,135 @@ +// FSInfo.h + +#ifndef USERLAND_FS_FS_INFO_H +#define USERLAND_FS_FS_INFO_H + +#include + +#include + +#include + +#include "Port.h" +#include "String.h" + +namespace UserlandFS { + +// FSInfo +class FSInfo { +public: + FSInfo() + : fInfos(NULL), + fCount(0) + { + } + + FSInfo(const char* fsName, const Port::Info* infos, int32 count) + : fName(), + fInfos(NULL), + fCount(0) + { + SetTo(fsName, infos, count); + } + + FSInfo(const BMessage* message) + : fName(), + fInfos(NULL), + fCount(0) + { + SetTo(message); + } + + FSInfo(const FSInfo& other) + : fName(), + fInfos(NULL), + fCount(0) + { + SetTo(other.GetName(), other.fInfos, other.fCount); + } + + ~FSInfo() + { + Unset(); + } + + status_t SetTo(const char* fsName, const Port::Info* infos, int32 count) + { + Unset(); + if (!fsName || !infos || count <= 0) + return B_BAD_VALUE; + if (!fName.SetTo(fsName)) + return B_NO_MEMORY; + fInfos = new(nothrow) Port::Info[count]; + if (!fInfos) + return B_NO_MEMORY; + memcpy(fInfos, infos, sizeof(Port::Info) * count); + fCount = count; + return B_OK; + } + + status_t SetTo(const BMessage* message) + { + Unset(); + if (!message) + return B_BAD_VALUE; + const void* infos; + ssize_t size; + const char* fsName; + if (message->FindData("infos", B_RAW_TYPE, &infos, &size) != B_OK + || size < 0 || message->FindString("fsName", &fsName) != B_OK) { + return B_BAD_VALUE; + } + return SetTo(fsName, (const Port::Info*)infos, + size / sizeof(Port::Info)); + } + + void Unset() + { + fName.Unset(); + delete[] fInfos; + fInfos = NULL; + fCount = 0; + } + + const char* GetName() const + { + return fName.GetString(); + } + + Port::Info* GetInfos() const + { + return fInfos; + } + + int32 CountInfos() const + { + return fCount; + } + + int32 GetSize() const + { + return fCount * sizeof(Port::Info); + } + + status_t Archive(BMessage* archive) + { + if (!fName.GetString() || !fInfos) + return B_NO_INIT; + status_t error = archive->AddString("fsName", fName.GetString()); + if (error != B_OK) + return error; + return archive->AddData("infos", B_RAW_TYPE, fInfos, + fCount * sizeof(Port::Info)); + } + +private: + String fName; + Port::Info* fInfos; + int32 fCount; +}; + +} // namespace UserlandFS + +using UserlandFS::FSInfo; + +#endif // USERLAND_FS_FS_INFO_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/FileSystem.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/FileSystem.cpp new file mode 100644 index 0000000000..cf41884637 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/FileSystem.cpp @@ -0,0 +1,145 @@ +// FileSystem.cpp + +#include +#include +#include +#include +#include +#include + +#include "Debug.h" +#include "DispatcherDefs.h" +#include "FileSystem.h" +#include "ServerDefs.h" + +// constructor +FileSystem::FileSystem(const char* name, status_t* _error) + : LazyInitializable(), + Referencable(), + fName(), + fInfo(NULL), + fTeam(-1), + fFinishInitSemaphore(-1), + fTeamLock() +{ + status_t error = B_OK; + if (!fName.SetTo(name)) + error = B_NO_MEMORY; + if (_error) + *_error = error; +} + +// constructor +FileSystem::FileSystem(team_id team, FSInfo* info, status_t* _error) + : LazyInitializable(false), + Referencable(), + fName(), + fInfo(info), + fTeam(team), + fFinishInitSemaphore(-1), + fTeamLock() +{ + status_t error = B_OK; + if (!fName.SetTo(info->GetName())) + error = B_NO_MEMORY; + if (_error) + *_error = error; +} + +// destructor +FileSystem::~FileSystem() +{ + if (fFinishInitSemaphore >= 0) + delete_sem(fFinishInitSemaphore); + delete fInfo; +} + +// GetName +const char* +FileSystem::GetName() const +{ + return fName.GetString(); +} + +// GetInfo +const FSInfo* +FileSystem::GetInfo() const +{ + return fInfo; +} + +// GetTeam +team_id +FileSystem::GetTeam() const +{ + BAutolock _(fTeamLock); + return fTeam; +} + +// CompleteInit +void +FileSystem::CompleteInit(FSInfo* info) +{ + fInfo = info; + if (fFinishInitSemaphore >= 0) + release_sem(fFinishInitSemaphore); +} + +// AbortInit +void +FileSystem::AbortInit() +{ + if (fInitStatus == B_OK) + fInitStatus = B_NO_INIT; + if (fFinishInitSemaphore >= 0) + release_sem(fFinishInitSemaphore); +} + +// FirstTimeInit +status_t +FileSystem::FirstTimeInit() +{ + if (fName.GetLength() == 0) + RETURN_ERROR(B_BAD_VALUE); + // create the init finish semaphore + fFinishInitSemaphore = create_sem(0, "FS init finish sem"); + if (fFinishInitSemaphore < 0) + return fFinishInitSemaphore; + // get a server entry ref + app_info appInfo; + status_t error = be_app->GetAppInfo(&appInfo); + if (error != B_OK) + RETURN_ERROR(error); + // launch a server instance + team_id team = -1; + fTeamLock.Lock(); + if (gServerSettings.ShallEnterDebugger()) { + int argc = 2; + const char *argv[] = { "--debug", fName.GetString(), NULL }; + error = be_roster->Launch(&appInfo.ref, argc, argv, &team); + } else { + int argc = 1; + const char *argv[] = { fName.GetString(), NULL }; + error = be_roster->Launch(&appInfo.ref, argc, argv, &team); + } + fTeam = team; + fTeamLock.Unlock(); + if (error != B_OK) + RETURN_ERROR(error); + // wait for the initialization to complete/fail + error = _WaitForInitToFinish(); + RETURN_ERROR(error); +} + +// _WaitForInitToFinish +status_t +FileSystem::_WaitForInitToFinish() +{ + status_t error = acquire_sem(fFinishInitSemaphore); + delete_sem(fFinishInitSemaphore); + fFinishInitSemaphore = -1; + if (error != B_OK) + return error; + return (fInfo ? B_OK : B_ERROR); +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/FileSystem.h b/src/add-ons/kernel/file_systems/userlandfs/server/FileSystem.h new file mode 100644 index 0000000000..47e62edb30 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/FileSystem.h @@ -0,0 +1,46 @@ +// FileSystem.h + +#ifndef USERLAND_FS_FILE_SYSTEM_H +#define USERLAND_FS_FILE_SYSTEM_H + +#include + +#include "FSInfo.h" +#include "LazyInitializable.h" +#include "Referencable.h" +#include "String.h" + +namespace UserlandFS { + +class FileSystem : public LazyInitializable, public Referencable { +public: + FileSystem(const char* name, status_t* error); + FileSystem(team_id team, FSInfo* info, + status_t* error); + ~FileSystem(); + + const char* GetName() const; + const FSInfo* GetInfo() const; + team_id GetTeam() const; + + void CompleteInit(FSInfo* info); + void AbortInit(); + +private: + virtual status_t FirstTimeInit(); + + status_t _WaitForInitToFinish(); + +private: + String fName; + FSInfo* fInfo; + team_id fTeam; + sem_id fFinishInitSemaphore; + mutable BLocker fTeamLock; +}; + +} // namespace UserlandFS + +using UserlandFS::FileSystem; + +#endif // USERLAND_FS_FILE_SYSTEM_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/Jamfile b/src/add-ons/kernel/file_systems/userlandfs/server/Jamfile new file mode 100644 index 0000000000..39812affb6 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/Jamfile @@ -0,0 +1,54 @@ +SubDir HAIKU_TOP src tests add-ons kernel file_systems userlandfs r5 src + server ; + +SetSubDirSupportedPlatforms r5 bone dano ; + +local userlandFSTop = [ FDirName $(HAIKU_TOP) src tests add-ons kernel + file_systems userlandfs r5 ] ; +local userlandFSIncludes = [ FDirName $(userlandFSTop) headers ] ; + +SubDirSysHdrs [ FDirName $(userlandFSIncludes) public ] ; +SubDirHdrs [ FDirName $(userlandFSIncludes) private ] ; +SubDirHdrs [ FDirName $(userlandFSIncludes) shared ] ; + +SEARCH_SOURCE += [ FDirName $(userlandFSTop) src private ] ; +SEARCH_SOURCE += [ FDirName $(userlandFSTop) src shared ] ; + +DEFINES += USER=1 ; +DEFINES += DEBUG_APP="\\\"UserlandFSServer\\\"" ; + +Application UserlandFSServer + : AreaSupport.cpp + Debug.cpp + DispatcherDefs.cpp + driver_settings.c + LazyInitializable.cpp + Locker.cpp + ObjectTracker.cpp + Port.cpp + Referencable.cpp + Request.cpp + RequestAllocator.cpp + RequestHandler.cpp + RequestPort.cpp + Requests.cpp + SingleReplyRequestHandler.cpp + String.cpp + + cache.c + sysdep.c + + FileSystem.cpp + kernel_emu.cpp + KernelUserFileSystem.cpp + KernelUserVolume.cpp + main.cpp + RequestThread.cpp + ServerDefs.cpp + UserFileSystem.cpp + UserlandFSDispatcher.cpp + UserlandFSServer.cpp + UserlandRequestHandler.cpp + UserVolume.cpp + : be +; diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/KernelUserFileSystem.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/KernelUserFileSystem.cpp new file mode 100644 index 0000000000..e7635dcd33 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/KernelUserFileSystem.cpp @@ -0,0 +1,45 @@ +// KernelUserFileSystem.cpp + +#include + +#include "KernelUserFileSystem.h" +#include "KernelUserVolume.h" + +// constructor +KernelUserFileSystem::KernelUserFileSystem(vnode_ops* fsOps) + : UserFileSystem(), + fFSOps(fsOps) +{ +} + +// destructor +KernelUserFileSystem::~KernelUserFileSystem() +{ +} + +// CreateVolume +status_t +KernelUserFileSystem::CreateVolume(UserVolume** volume, nspace_id id) +{ + // check initialization and parameters + if (!fFSOps) + return B_BAD_VALUE; + if (!volume) + return B_BAD_VALUE; + // create the volume + *volume = new(nothrow) KernelUserVolume(this, id, fFSOps); + if (!*volume) + return B_NO_MEMORY; + return B_OK; +} + +// DeleteVolume +status_t +KernelUserFileSystem::DeleteVolume(UserVolume* volume) +{ + if (!volume || !dynamic_cast(volume)) + return B_BAD_VALUE; + delete volume; + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/KernelUserFileSystem.h b/src/add-ons/kernel/file_systems/userlandfs/server/KernelUserFileSystem.h new file mode 100644 index 0000000000..abe9d40aab --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/KernelUserFileSystem.h @@ -0,0 +1,26 @@ +// KernelUserFileSystem.h + +#ifndef USERLAND_FS_KERNEL_USER_FILE_SYSTEM_H +#define USERLAND_FS_KERNEL_USER_FILE_SYSTEM_H + +#include "UserFileSystem.h" + +namespace UserlandFS { + +class KernelUserFileSystem : public UserFileSystem { +public: + KernelUserFileSystem(vnode_ops* fsOps); + virtual ~KernelUserFileSystem(); + + virtual status_t CreateVolume(UserVolume** volume, nspace_id id); + virtual status_t DeleteVolume(UserVolume* volume); + +private: + vnode_ops* fFSOps; +}; + +} // namespace UserlandFS + +using UserlandFS::KernelUserFileSystem; + +#endif // USERLAND_FS_KERNEL_USER_FILE_SYSTEM_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/KernelUserVolume.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/KernelUserVolume.cpp new file mode 100644 index 0000000000..a83b8adb57 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/KernelUserVolume.cpp @@ -0,0 +1,606 @@ +// KernelUserVolume.cpp + +#include "KernelUserVolume.h" + +// constructor +KernelUserVolume::KernelUserVolume(UserFileSystem* fileSystem, nspace_id id, + vnode_ops* fsOps) + : UserVolume(fileSystem, id), + fFSOps(fsOps), + fVolumeCookie(NULL) +{ +} + +// destructor +KernelUserVolume::~KernelUserVolume() +{ +} + +// #pragma mark - +// #pragma mark ----- FS ----- + +// Mount +status_t +KernelUserVolume::Mount(const char* device, ulong flags, const char* parameters, + int32 len, vnode_id* rootID) +{ + if (!fFSOps->mount) + return B_BAD_VALUE; + return fFSOps->mount(GetID(), device, flags, (void*)parameters, len, + &fVolumeCookie, rootID); +} + +// Unmount +status_t +KernelUserVolume::Unmount() +{ + if (!fFSOps->unmount) + return B_BAD_VALUE; + return fFSOps->unmount(fVolumeCookie); +} + +// Sync +status_t +KernelUserVolume::Sync() +{ + if (!fFSOps->sync) + return B_BAD_VALUE; + return fFSOps->sync(fVolumeCookie); +} + +// ReadFSStat +status_t +KernelUserVolume::ReadFSStat(fs_info* info) +{ + if (!fFSOps->rfsstat) + return B_BAD_VALUE; + return fFSOps->rfsstat(fVolumeCookie, info); +} + +// WriteFSStat +status_t +KernelUserVolume::WriteFSStat(struct fs_info *info, long mask) +{ + if (!fFSOps->wfsstat) + return B_BAD_VALUE; + return fFSOps->wfsstat(fVolumeCookie, info, mask); +} + +// #pragma mark - +// #pragma mark ----- vnodes ----- + +// ReadVNode +status_t +KernelUserVolume::ReadVNode(vnode_id vnid, char reenter, void** node) +{ + if (!fFSOps->read_vnode) + return B_BAD_VALUE; + return fFSOps->read_vnode(fVolumeCookie, vnid, reenter, node); +} + +// WriteVNode +status_t +KernelUserVolume::WriteVNode(void* node, char reenter) +{ + if (!fFSOps->write_vnode) + return B_BAD_VALUE; + return fFSOps->write_vnode(fVolumeCookie, node, reenter); +} + +// RemoveVNode +status_t +KernelUserVolume::RemoveVNode(void* node, char reenter) +{ + if (!fFSOps->remove_vnode) + return B_BAD_VALUE; + return fFSOps->remove_vnode(fVolumeCookie, node, reenter); +} + +// #pragma mark - +// #pragma mark ----- nodes ----- + +// FSync +status_t +KernelUserVolume::FSync(void* node) +{ + if (!fFSOps->fsync) + return B_BAD_VALUE; + return fFSOps->fsync(fVolumeCookie, node); +} + +// ReadStat +status_t +KernelUserVolume::ReadStat(void* node, struct stat* st) +{ + if (!fFSOps->rstat) + return B_BAD_VALUE; + return fFSOps->rstat(fVolumeCookie, node, st); +} + +// WriteStat +status_t +KernelUserVolume::WriteStat(void* node, struct stat* st, long mask) +{ + if (!fFSOps->wstat) + return B_BAD_VALUE; + return fFSOps->wstat(fVolumeCookie, node, st, mask); +} + +// Access +status_t +KernelUserVolume::Access(void* node, int mode) +{ + if (!fFSOps->access) + return B_BAD_VALUE; + return fFSOps->access(fVolumeCookie, node, mode); +} + +// #pragma mark - +// #pragma mark ----- files ----- + +// Create +status_t +KernelUserVolume::Create(void* dir, const char* name, int openMode, int mode, + vnode_id* vnid, void** cookie) +{ + if (!fFSOps->create) + return B_BAD_VALUE; + return fFSOps->create(fVolumeCookie, dir, name, openMode, mode, vnid, + cookie); +} + +// Open +status_t +KernelUserVolume::Open(void* node, int openMode, void** cookie) +{ + if (!fFSOps->open) + return B_BAD_VALUE; + return fFSOps->open(fVolumeCookie, node, openMode, cookie); +} + +// Close +status_t +KernelUserVolume::Close(void* node, void* cookie) +{ + if (!fFSOps->close) + return B_OK; + return fFSOps->close(fVolumeCookie, node, cookie); +} + +// FreeCookie +status_t +KernelUserVolume::FreeCookie(void* node, void* cookie) +{ + if (!fFSOps->free_cookie) + return B_OK; + return fFSOps->free_cookie(fVolumeCookie, node, cookie); +} + +// Read +status_t +KernelUserVolume::Read(void* node, void* cookie, off_t pos, void* buffer, + size_t bufferSize, size_t* bytesRead) +{ + if (!fFSOps->read) + return B_BAD_VALUE; + *bytesRead = bufferSize; + return fFSOps->read(fVolumeCookie, node, cookie, pos, buffer, bytesRead); +} + +// Write +status_t +KernelUserVolume::Write(void* node, void* cookie, off_t pos, const void* buffer, + size_t bufferSize, size_t* bytesWritten) +{ + if (!fFSOps->write) + return B_BAD_VALUE; + *bytesWritten = bufferSize; + return fFSOps->write(fVolumeCookie, node, cookie, pos, buffer, + bytesWritten); +} + +// IOCtl +status_t +KernelUserVolume::IOCtl(void* node, void* cookie, int command, void *buffer, + size_t size) +{ + if (!fFSOps->ioctl) + return B_BAD_VALUE; + return fFSOps->ioctl(fVolumeCookie, node, cookie, command, buffer, size); +} + +// SetFlags +status_t +KernelUserVolume::SetFlags(void* node, void* cookie, int flags) +{ + if (!fFSOps->setflags) + return B_BAD_VALUE; + return fFSOps->setflags(fVolumeCookie, node, cookie, flags); +} + +// Select +status_t +KernelUserVolume::Select(void* node, void* cookie, uint8 event, uint32 ref, + selectsync* sync) +{ + if (!fFSOps->select) { + notify_select_event(sync, ref); + return B_OK; + } + return fFSOps->select(fVolumeCookie, node, cookie, event, ref, sync); +} + +// Deselect +status_t +KernelUserVolume::Deselect(void* node, void* cookie, uint8 event, + selectsync* sync) +{ + if (!fFSOps->select || !fFSOps->deselect) + return B_OK; + return fFSOps->deselect(fVolumeCookie, node, cookie, event, sync); +} + +// #pragma mark - +// #pragma mark ----- hard links / symlinks ----- + +// Link +status_t +KernelUserVolume::Link(void* dir, const char* name, void* node) +{ + if (!fFSOps->link) + return B_BAD_VALUE; + return fFSOps->link(fVolumeCookie, dir, name, node); +} + +// Unlink +status_t +KernelUserVolume::Unlink(void* dir, const char* name) +{ + if (!fFSOps->unlink) + return B_BAD_VALUE; + return fFSOps->unlink(fVolumeCookie, dir, name); +} + +// Symlink +status_t +KernelUserVolume::Symlink(void* dir, const char* name, const char* target) +{ + if (!fFSOps->symlink) + return B_BAD_VALUE; + return fFSOps->symlink(fVolumeCookie, dir, name, target); +} + +// ReadLink +status_t +KernelUserVolume::ReadLink(void* node, char* buffer, size_t bufferSize, + size_t* bytesRead) +{ + if (!fFSOps->readlink) + return B_BAD_VALUE; + *bytesRead = bufferSize; + return fFSOps->readlink(fVolumeCookie, node, buffer, bytesRead); +} + +// Rename +status_t +KernelUserVolume::Rename(void* oldDir, const char* oldName, void* newDir, + const char* newName) +{ + if (!fFSOps->rename) + return B_BAD_VALUE; + return fFSOps->rename(fVolumeCookie, oldDir, oldName, newDir, newName); +} + +// #pragma mark - +// #pragma mark ----- directories ----- + +// MkDir +status_t +KernelUserVolume::MkDir(void* dir, const char* name, int mode) +{ + if (!fFSOps->mkdir) + return B_BAD_VALUE; + return fFSOps->mkdir(fVolumeCookie, dir, name, mode); +} + +// RmDir +status_t +KernelUserVolume::RmDir(void* dir, const char* name) +{ + if (!fFSOps->rmdir) + return B_BAD_VALUE; + return fFSOps->rmdir(fVolumeCookie, dir, name); +} + +// OpenDir +status_t +KernelUserVolume::OpenDir(void* node, void** cookie) +{ + if (!fFSOps->opendir) + return B_BAD_VALUE; + return fFSOps->opendir(fVolumeCookie, node, cookie); +} + +// CloseDir +status_t +KernelUserVolume::CloseDir(void* node, void* cookie) +{ + if (!fFSOps->closedir) + return B_OK; + return fFSOps->closedir(fVolumeCookie, node, cookie); +} + +// FreeDirCookie +status_t +KernelUserVolume::FreeDirCookie(void* node, void* cookie) +{ + if (!fFSOps->free_dircookie) + return B_OK; + return fFSOps->free_dircookie(fVolumeCookie, node, cookie); +} + +// ReadDir +status_t +KernelUserVolume::ReadDir(void* node, void* cookie, void* buffer, + size_t bufferSize, int32 count, int32* countRead) +{ + if (!fFSOps->readdir) + return B_BAD_VALUE; + *countRead = count; + return fFSOps->readdir(fVolumeCookie, node, cookie, countRead, + (dirent*)buffer, bufferSize); +} + +// RewindDir +status_t +KernelUserVolume::RewindDir(void* node, void* cookie) +{ + if (!fFSOps->rewinddir) + return B_BAD_VALUE; + return fFSOps->rewinddir(fVolumeCookie, node, cookie); +} + +// Walk +status_t +KernelUserVolume::Walk(void* dir, const char* entryName, char** resolvedPath, + vnode_id* vnid) +{ + if (!fFSOps->walk) + return B_BAD_VALUE; + return fFSOps->walk(fVolumeCookie, dir, entryName, resolvedPath, vnid); +} + +// #pragma mark - +// #pragma mark ----- attributes ----- + +// OpenAttrDir +status_t +KernelUserVolume::OpenAttrDir(void* node, void** cookie) +{ + if (!fFSOps->open_attrdir) + return B_BAD_VALUE; + return fFSOps->open_attrdir(fVolumeCookie, node, cookie); +} + +// CloseAttrDir +status_t +KernelUserVolume::CloseAttrDir(void* node, void* cookie) +{ + if (!fFSOps->close_attrdir) + return B_OK; + return fFSOps->close_attrdir(fVolumeCookie, node, cookie); +} + +// FreeAttrDirCookie +status_t +KernelUserVolume::FreeAttrDirCookie(void* node, void* cookie) +{ + if (!fFSOps->free_attrdircookie) + return B_OK; + return fFSOps->free_attrdircookie(fVolumeCookie, node, cookie); +} + +// ReadAttrDir +status_t +KernelUserVolume::ReadAttrDir(void* node, void* cookie, void* buffer, + size_t bufferSize, int32 count, int32* countRead) +{ + if (!fFSOps->read_attrdir) + return B_BAD_VALUE; + *countRead = count; + return fFSOps->read_attrdir(fVolumeCookie, node, cookie, countRead, + (struct dirent*)buffer, bufferSize); +} + +// RewindAttrDir +status_t +KernelUserVolume::RewindAttrDir(void* node, void* cookie) +{ + if (!fFSOps->rewind_attrdir) + return B_BAD_VALUE; + return fFSOps->rewind_attrdir(fVolumeCookie, node, cookie); +} + +// ReadAttr +status_t +KernelUserVolume::ReadAttr(void* node, const char* name, int type, off_t pos, + void* buffer, size_t bufferSize, size_t* bytesRead) +{ + if (!fFSOps->read_attr) + return B_BAD_VALUE; + *bytesRead = bufferSize; + return fFSOps->read_attr(fVolumeCookie, node, name, type, buffer, bytesRead, + pos); +} + +// WriteAttr +status_t +KernelUserVolume::WriteAttr(void* node, const char* name, int type, off_t pos, + const void* buffer, size_t bufferSize, size_t* bytesWritten) +{ + if (!fFSOps->write_attr) + return B_BAD_VALUE; + *bytesWritten = bufferSize; + return fFSOps->write_attr(fVolumeCookie, node, name, type, buffer, + bytesWritten, pos); +} + +// RemoveAttr +status_t +KernelUserVolume::RemoveAttr(void* node, const char* name) +{ + if (!fFSOps->remove_attr) + return B_BAD_VALUE; + return fFSOps->remove_attr(fVolumeCookie, node, name); +} + +// RenameAttr +status_t +KernelUserVolume::RenameAttr(void* node, const char* oldName, const char* newName) +{ + if (!fFSOps->rename_attr) + return B_BAD_VALUE; + return fFSOps->rename_attr(fVolumeCookie, node, oldName, newName); +} + +// StatAttr +status_t +KernelUserVolume::StatAttr(void* node, const char* name, + struct attr_info* attrInfo) +{ + if (!fFSOps->stat_attr) + return B_BAD_VALUE; + return fFSOps->stat_attr(fVolumeCookie, node, name, attrInfo); +} + +// #pragma mark - +// #pragma mark ----- indices ----- + +// OpenIndexDir +status_t +KernelUserVolume::OpenIndexDir(void** cookie) +{ + if (!fFSOps->open_indexdir) + return B_BAD_VALUE; + return fFSOps->open_indexdir(fVolumeCookie, cookie); +} + +// CloseIndexDir +status_t +KernelUserVolume::CloseIndexDir(void* cookie) +{ + if (!fFSOps->close_indexdir) + return B_OK; + return fFSOps->close_indexdir(fVolumeCookie, cookie); +} + +// FreeIndexDirCookie +status_t +KernelUserVolume::FreeIndexDirCookie(void* cookie) +{ + if (!fFSOps->free_indexdircookie) + return B_OK; + return fFSOps->free_indexdircookie(fVolumeCookie, NULL, cookie); +} + +// ReadIndexDir +status_t +KernelUserVolume::ReadIndexDir(void* cookie, void* buffer, size_t bufferSize, + int32 count, int32* countRead) +{ + if (!fFSOps->read_indexdir) + return B_BAD_VALUE; + *countRead = count; + return fFSOps->read_indexdir(fVolumeCookie, cookie, countRead, + (struct dirent*)buffer, bufferSize); +} + +// RewindIndexDir +status_t +KernelUserVolume::RewindIndexDir(void* cookie) +{ + if (!fFSOps->rewind_indexdir) + return B_BAD_VALUE; + return fFSOps->rewind_indexdir(fVolumeCookie, cookie); +} + +// CreateIndex +status_t +KernelUserVolume::CreateIndex(const char* name, int type, int flags) +{ + if (!fFSOps->create_index) + return B_BAD_VALUE; + return fFSOps->create_index(fVolumeCookie, name, type, flags); +} + +// RemoveIndex +status_t +KernelUserVolume::RemoveIndex(const char* name) +{ + if (!fFSOps->remove_index) + return B_BAD_VALUE; + return fFSOps->remove_index(fVolumeCookie, name); +} + +// RenameIndex +status_t +KernelUserVolume::RenameIndex(const char* oldName, const char* newName) +{ + if (!fFSOps->rename_index) + return B_BAD_VALUE; + return fFSOps->rename_index(fVolumeCookie, oldName, newName); +} + +// StatIndex +status_t +KernelUserVolume::StatIndex(const char *name, struct index_info* indexInfo) +{ + if (!fFSOps->stat_index) + return B_BAD_VALUE; + return fFSOps->stat_index(fVolumeCookie, name, indexInfo); +} + +// #pragma mark - +// #pragma mark ----- queries ----- + +// OpenQuery +status_t +KernelUserVolume::OpenQuery(const char* queryString, ulong flags, port_id port, + long token, void** cookie) +{ + if (!fFSOps->open_query) + return B_BAD_VALUE; + return fFSOps->open_query(fVolumeCookie, queryString, flags, port, + token, cookie); +} + +// CloseQuery +status_t +KernelUserVolume::CloseQuery(void* cookie) +{ + if (!fFSOps->close_query) + return B_OK; + return fFSOps->close_query(fVolumeCookie, cookie); +} + +// FreeQueryCookie +status_t +KernelUserVolume::FreeQueryCookie(void* cookie) +{ + if (!fFSOps->free_querycookie) + return B_OK; + return fFSOps->free_querycookie(fVolumeCookie, NULL, cookie); +} + +// ReadQuery +status_t +KernelUserVolume::ReadQuery(void* cookie, void* buffer, size_t bufferSize, + int32 count, int32* countRead) +{ + if (!fFSOps->read_query) + return B_BAD_VALUE; + *countRead = count; + return fFSOps->read_query(fVolumeCookie, cookie, countRead, + (struct dirent*)buffer, bufferSize); +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/KernelUserVolume.h b/src/add-ons/kernel/file_systems/userlandfs/server/KernelUserVolume.h new file mode 100644 index 0000000000..8f6d60f304 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/KernelUserVolume.h @@ -0,0 +1,139 @@ +// KernelUserVolume.h + +#ifndef USERLAND_FS_KERNEL_FS_VOLUME_H +#define USERLAND_FS_KERNEL_FS_VOLUME_H + +#include "UserVolume.h" + +struct vnode_ops; + +namespace UserlandFS { + +class KernelUserVolume : public UserVolume { +public: + KernelUserVolume(UserFileSystem* fileSystem, + nspace_id id, vnode_ops* fsOps); + virtual ~KernelUserVolume(); + + // FS + virtual status_t Mount(const char* device, ulong flags, + const char* parameters, int32 len, + vnode_id* rootID); + virtual status_t Unmount(); + virtual status_t Sync(); + virtual status_t ReadFSStat(fs_info* info); + virtual status_t WriteFSStat(struct fs_info *info, long mask); + + // vnodes + virtual status_t ReadVNode(vnode_id vnid, char reenter, + void** node); + virtual status_t WriteVNode(void* node, char reenter); + virtual status_t RemoveVNode(void* node, char reenter); + + // nodes + virtual status_t FSync(void* node); + virtual status_t ReadStat(void* node, struct stat* st); + virtual status_t WriteStat(void* node, struct stat* st, + long mask); + virtual status_t Access(void* node, int mode); + + // files + virtual status_t Create(void* dir, const char* name, + int openMode, int mode, vnode_id* vnid, + void** cookie); + virtual status_t Open(void* node, int openMode, void** cookie); + virtual status_t Close(void* node, void* cookie); + virtual status_t FreeCookie(void* node, void* cookie); + virtual status_t Read(void* node, void* cookie, off_t pos, + void* buffer, size_t bufferSize, + size_t* bytesRead); + virtual status_t Write(void* node, void* cookie, off_t pos, + const void* buffer, size_t bufferSize, + size_t* bytesWritten); + virtual status_t IOCtl(void* node, void* cookie, int command, + void *buffer, size_t size); + virtual status_t SetFlags(void* node, void* cookie, int flags); + virtual status_t Select(void* node, void* cookie, uint8 event, + uint32 ref, selectsync* sync); + virtual status_t Deselect(void* node, void* cookie, uint8 event, + selectsync* sync); + + // hard links / symlinks + virtual status_t Link(void* dir, const char* name, void* node); + virtual status_t Unlink(void* dir, const char* name); + virtual status_t Symlink(void* dir, const char* name, + const char* target); + virtual status_t ReadLink(void* node, char* buffer, + size_t bufferSize, size_t* bytesRead); + virtual status_t Rename(void* oldDir, const char* oldName, + void* newDir, const char* newName); + + // directories + virtual status_t MkDir(void* dir, const char* name, int mode); + virtual status_t RmDir(void* dir, const char* name); + virtual status_t OpenDir(void* node, void** cookie); + virtual status_t CloseDir(void* node, void* cookie); + virtual status_t FreeDirCookie(void* node, void* cookie); + virtual status_t ReadDir(void* node, void* cookie, + void* buffer, size_t bufferSize, + int32 count, int32* countRead); + virtual status_t RewindDir(void* node, void* cookie); + virtual status_t Walk(void* dir, const char* entryName, + char** resolvedPath, vnode_id* vnid); + + // attributes + virtual status_t OpenAttrDir(void* node, void** cookie); + virtual status_t CloseAttrDir(void* node, void* cookie); + virtual status_t FreeAttrDirCookie(void* node, void* cookie); + virtual status_t ReadAttrDir(void* node, void* cookie, + void* buffer, size_t bufferSize, + int32 count, int32* countRead); + virtual status_t RewindAttrDir(void* node, void* cookie); + virtual status_t ReadAttr(void* node, const char* name, + int type, off_t pos, void* buffer, + size_t bufferSize, size_t* bytesRead); + virtual status_t WriteAttr(void* node, const char* name, + int type, off_t pos, const void* buffer, + size_t bufferSize, size_t* bytesWritten); + virtual status_t RemoveAttr(void* node, const char* name); + virtual status_t RenameAttr(void* node, const char* oldName, + const char* newName); + virtual status_t StatAttr(void* node, const char* name, + struct attr_info* attrInfo); + + // indices + virtual status_t OpenIndexDir(void** cookie); + virtual status_t CloseIndexDir(void* cookie); + virtual status_t FreeIndexDirCookie(void* cookie); + virtual status_t ReadIndexDir(void* cookie, void* buffer, + size_t bufferSize, int32 count, + int32* countRead); + virtual status_t RewindIndexDir(void* cookie); + virtual status_t CreateIndex(const char* name, int type, + int flags); + virtual status_t RemoveIndex(const char* name); + virtual status_t RenameIndex(const char* oldName, + const char* newName); + virtual status_t StatIndex(const char *name, + struct index_info* indexInfo); + + // queries + virtual status_t OpenQuery(const char* queryString, + ulong flags, port_id port, long token, + void** cookie); + virtual status_t CloseQuery(void* cookie); + virtual status_t FreeQueryCookie(void* cookie); + virtual status_t ReadQuery(void* cookie, void* buffer, + size_t bufferSize, int32 count, + int32* countRead); + +private: + vnode_ops* fFSOps; + void* fVolumeCookie; +}; + +} // namespace UserlandFS + +using UserlandFS::KernelUserVolume; + +#endif // USERLAND_FS_KERNEL_FS_VOLUME_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/RequestThread.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/RequestThread.cpp new file mode 100644 index 0000000000..cd069b7204 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/RequestThread.cpp @@ -0,0 +1,179 @@ +// RequestThread.cpp + +#include + +#include + +#include "RequestThread.h" +#include "ServerDefs.h" +#include "UserlandRequestHandler.h" + +static const int32 sTLSVariable = tls_allocate(); + +// constructor +RequestThreadContext::RequestThreadContext(UserVolume* volume) + : fPreviousContext(NULL), + fThread(NULL), + fVolume(volume) +{ + fThread = RequestThread::GetCurrentThread(); + if (fThread) { + fPreviousContext = fThread->GetContext(); + fThread->SetContext(this); + } +} + +// destructor +RequestThreadContext::~RequestThreadContext() +{ + if (fThread) + fThread->SetContext(fPreviousContext); +} + +// GetThread +RequestThread* +RequestThreadContext::GetThread() const +{ + return fThread; +} + +// GetVolume +UserlandFS::UserVolume* +RequestThreadContext::GetVolume() const +{ + return fVolume; +} + + +// RequestThread + +// constructor +RequestThread::RequestThread() + : fThread(-1), + fFileSystem(NULL), + fPort(NULL), + fContext(NULL), + fTerminating(false) +{ +} + +// destructor +RequestThread::~RequestThread() +{ + PrepareTermination(); + Terminate(); + delete fPort; +} + +// Init +status_t +RequestThread::Init(UserFileSystem* fileSystem) +{ + if (!fileSystem) + return B_BAD_VALUE; + // create the port + fPort = new(nothrow) RequestPort(kRequestPortSize); + if (!fPort) + return B_NO_MEMORY; + status_t error = fPort->InitCheck(); + if (error != B_OK) + return error; + // spawn the thread + fThread = spawn_thread(_ThreadEntry, "request thread", B_NORMAL_PRIORITY, + this); + if (fThread < 0) + return fThread; + fFileSystem = fileSystem; + return B_OK; +} + +// Run +void +RequestThread::Run() +{ + resume_thread(fThread); +} + +// PrepareTermination +void +RequestThread::PrepareTermination() +{ + if (fTerminating) + return; + fTerminating = true; + if (fPort) + fPort->Close(); +} + +// Terminate +void +RequestThread::Terminate() +{ + if (fThread >= 0) { + int32 result; + wait_for_thread(fThread, &result); + fThread = -1; + } +} + +// GetPortInfo +const Port::Info* +RequestThread::GetPortInfo() const +{ + return (fPort ? fPort->GetPortInfo() : NULL); +} + +// GetFileSystem +UserlandFS::UserFileSystem* +RequestThread::GetFileSystem() const +{ + return fFileSystem; +} + +// GetPort +RequestPort* +RequestThread::GetPort() const +{ + return fPort; +} + +// GetContext +RequestThreadContext* +RequestThread::GetContext() const +{ + return fContext; +} + +// GetCurrentThread +RequestThread* +RequestThread::GetCurrentThread() +{ + return (RequestThread*)tls_get(sTLSVariable); +} + +// SetContext +void +RequestThread::SetContext(RequestThreadContext* context) +{ + fContext = context; +} + +// _ThreadEntry +int32 +RequestThread::_ThreadEntry(void* data) +{ + return ((RequestThread*)data)->_ThreadLoop(); +} + +// _ThreadLoop +int32 +RequestThread::_ThreadLoop() +{ + tls_set(sTLSVariable, this); + if (!fTerminating) { + UserlandRequestHandler handler(fFileSystem, false); + return fPort->HandleRequests(&handler); + } + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/RequestThread.h b/src/add-ons/kernel/file_systems/userlandfs/server/RequestThread.h new file mode 100644 index 0000000000..c1522b6f9c --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/RequestThread.h @@ -0,0 +1,69 @@ +// RequestThread.h + +#ifndef USERLAND_FS_REQUEST_THREAD_H +#define USERLAND_FS_REQUEST_THREAD_H + +#include "RequestPort.h" + +namespace UserlandFS { + +class RequestThread; +class UserFileSystem; +class UserVolume; + +// RequestThreadContext +class RequestThreadContext { +public: + RequestThreadContext(UserVolume* volume); + ~RequestThreadContext(); + + RequestThread* GetThread() const; + UserVolume* GetVolume() const; + +private: + RequestThreadContext* fPreviousContext; + RequestThread* fThread; + UserVolume* fVolume; +}; + +// RequestThread +class RequestThread { +public: + RequestThread(); + ~RequestThread(); + + status_t Init(UserFileSystem* fileSystem); + void Run(); + void PrepareTermination(); + void Terminate(); + + const Port::Info* GetPortInfo() const; + UserFileSystem* GetFileSystem() const; + RequestPort* GetPort() const; + RequestThreadContext* GetContext() const; + + static RequestThread* GetCurrentThread(); + +private: + void SetContext(RequestThreadContext* context); + +private: + static int32 _ThreadEntry(void* data); + int32 _ThreadLoop(); + +private: + friend class RequestThreadContext; + + thread_id fThread; + UserFileSystem* fFileSystem; + RequestPort* fPort; + RequestThreadContext* fContext; + bool fTerminating; +}; + +} // namespace UserlandFS + +using UserlandFS::RequestThreadContext; +using UserlandFS::RequestThread; + +#endif // USERLAND_FS_REQUEST_THREAD_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/ServerDefs.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/ServerDefs.cpp new file mode 100644 index 0000000000..907b42078b --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/ServerDefs.cpp @@ -0,0 +1,34 @@ +// ServerDefs.cpp + +#include "ServerDefs.h" + +// constructor +ServerSettings::ServerSettings() + : fEnterDebugger(false) +{ +} + +// destructor +ServerSettings::~ServerSettings() +{ +} + +// SetEnterDebugger +void +ServerSettings::SetEnterDebugger(bool enterDebugger) +{ + fEnterDebugger = enterDebugger; +} + +// ShallEnterDebugger +bool +ServerSettings::ShallEnterDebugger() const +{ + return fEnterDebugger; +} + +// the global settings +ServerSettings gServerSettings; + +const char* kUserlandFSDispatcherClipboardName = "userland fs dispatcher"; + diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/ServerDefs.h b/src/add-ons/kernel/file_systems/userlandfs/server/ServerDefs.h new file mode 100644 index 0000000000..775d1525a6 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/ServerDefs.h @@ -0,0 +1,41 @@ +// ServerDefs.h + +#ifndef USERLAND_FS_SERVER_DEFS_H +#define USERLAND_FS_SERVER_DEFS_H + +#include + +namespace UserlandFS { + +class ServerSettings { +public: + ServerSettings(); + ~ServerSettings(); + + void SetEnterDebugger(bool enterDebugger); + bool ShallEnterDebugger() const; + +private: + bool fEnterDebugger; +}; + +extern ServerSettings gServerSettings; + +enum { + UFS_REGISTER_FS = 'rgfs', + UFS_REGISTER_FS_ACK = 'rfsa', + UFS_REGISTER_FS_DENIED = 'rfsd', +}; + +extern const char* kUserlandFSDispatcherClipboardName; + +static const int32 kRequestPortSize = B_PAGE_SIZE; + +} // namespace UserlandFS + +using UserlandFS::ServerSettings; +using UserlandFS::gServerSettings; +using UserlandFS::kUserlandFSDispatcherClipboardName; +using UserlandFS::kRequestPortSize; + +#endif // USERLAND_FS_SERVER_DEFS_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/UserFileSystem.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/UserFileSystem.cpp new file mode 100644 index 0000000000..e709615616 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/UserFileSystem.cpp @@ -0,0 +1,14 @@ +// UserFileSystem.cpp + +#include "UserFileSystem.h" + +// constructor +UserFileSystem::UserFileSystem() +{ +} + +// destructor +UserFileSystem::~UserFileSystem() +{ +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/UserFileSystem.h b/src/add-ons/kernel/file_systems/userlandfs/server/UserFileSystem.h new file mode 100644 index 0000000000..af158db180 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/UserFileSystem.h @@ -0,0 +1,27 @@ +// UserFileSystem.h + +#ifndef USERLAND_FS_USER_FILE_SYSTEM_H +#define USERLAND_FS_USER_FILE_SYSTEM_H + +#include +#include + +namespace UserlandFS { + +class UserVolume; + +class UserFileSystem { +public: + UserFileSystem(); + virtual ~UserFileSystem(); + + virtual status_t CreateVolume(UserVolume** volume, + nspace_id id) = 0; + virtual status_t DeleteVolume(UserVolume* volume) = 0; +}; + +} // namespace UserlandFS + +using UserlandFS::UserFileSystem; + +#endif // USERLAND_FS_FILE_SYSTEM_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/UserVolume.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/UserVolume.cpp new file mode 100644 index 0000000000..e1f6dd21dc --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/UserVolume.cpp @@ -0,0 +1,479 @@ +// UserVolume.cpp + +#include "UserVolume.h" + +// constructor +UserVolume::UserVolume(UserFileSystem* fileSystem, nspace_id id) + : fFileSystem(fileSystem), + fID(id) +{ +} + +// destructor +UserVolume::~UserVolume() +{ +} + +// GetFileSystem +UserlandFS::UserFileSystem* +UserVolume::GetFileSystem() const +{ + return fFileSystem; +} + +// GetID +nspace_id +UserVolume::GetID() const +{ + return fID; +} + +// #pragma mark - +// #pragma mark ----- FS ----- + +// Mount +status_t +UserVolume::Mount(const char* device, ulong flags, const char* parameters, + int32 len, vnode_id* rootID) +{ + return B_BAD_VALUE; +} + +// Unmount +status_t +UserVolume::Unmount() +{ + return B_BAD_VALUE; +} + +// Sync +status_t +UserVolume::Sync() +{ + return B_BAD_VALUE; +} + +// ReadFSStat +status_t +UserVolume::ReadFSStat(fs_info* info) +{ + return B_BAD_VALUE; +} + +// WriteFSStat +status_t +UserVolume::WriteFSStat(struct fs_info *info, long mask) +{ + return B_BAD_VALUE; +} + +// #pragma mark - +// #pragma mark ----- vnodes ----- + +// ReadVNode +status_t +UserVolume::ReadVNode(vnode_id vnid, char reenter, void** node) +{ + return B_BAD_VALUE; +} + +// WriteVNode +status_t +UserVolume::WriteVNode(void* node, char reenter) +{ + return B_BAD_VALUE; +} + +// RemoveVNode +status_t +UserVolume::RemoveVNode(void* node, char reenter) +{ + return B_BAD_VALUE; +} + +// #pragma mark - +// #pragma mark ----- nodes ----- + +// FSync +status_t +UserVolume::FSync(void* node) +{ + return B_BAD_VALUE; +} + +// ReadStat +status_t +UserVolume::ReadStat(void* node, struct stat* st) +{ + return B_BAD_VALUE; +} + +// WriteStat +status_t +UserVolume::WriteStat(void* node, struct stat* st, long mask) +{ + return B_BAD_VALUE; +} + +// Access +status_t +UserVolume::Access(void* node, int mode) +{ + return B_BAD_VALUE; +} + +// #pragma mark - +// #pragma mark ----- files ----- + +// Create +status_t +UserVolume::Create(void* dir, const char* name, int openMode, int mode, + vnode_id* vnid, void** cookie) +{ + return B_BAD_VALUE; +} + +// Open +status_t +UserVolume::Open(void* node, int openMode, void** cookie) +{ + return B_BAD_VALUE; +} + +// Close +status_t +UserVolume::Close(void* node, void* cookie) +{ + return B_BAD_VALUE; +} + +// FreeCookie +status_t +UserVolume::FreeCookie(void* node, void* cookie) +{ + return B_BAD_VALUE; +} + +// Read +status_t +UserVolume::Read(void* node, void* cookie, off_t pos, void* buffer, + size_t bufferSize, size_t* bytesRead) +{ + return B_BAD_VALUE; +} + +// Write +status_t +UserVolume::Write(void* node, void* cookie, off_t pos, const void* buffer, + size_t bufferSize, size_t* bytesWritten) +{ + return B_BAD_VALUE; +} + +// IOCtl +status_t +UserVolume::IOCtl(void* node, void* cookie, int command, void *buffer, + size_t size) +{ + return B_BAD_VALUE; +} + +// SetFlags +status_t +UserVolume::SetFlags(void* node, void* cookie, int flags) +{ + return B_BAD_VALUE; +} + +// Select +status_t +UserVolume::Select(void* node, void* cookie, uint8 event, uint32 ref, + selectsync* sync) +{ + return B_BAD_VALUE; +} + +// Deselect +status_t +UserVolume::Deselect(void* node, void* cookie, uint8 event, selectsync* sync) +{ + return B_BAD_VALUE; +} + +// #pragma mark - +// #pragma mark ----- hard links / symlinks ----- + +// Link +status_t +UserVolume::Link(void* dir, const char* name, void* node) +{ + return B_BAD_VALUE; +} + +// Unlink +status_t +UserVolume::Unlink(void* dir, const char* name) +{ + return B_BAD_VALUE; +} + +// Symlink +status_t +UserVolume::Symlink(void* dir, const char* name, const char* target) +{ + return B_BAD_VALUE; +} + +// ReadLink +status_t +UserVolume::ReadLink(void* node, char* buffer, size_t bufferSize, + size_t* bytesRead) +{ + return B_BAD_VALUE; +} + +// Rename +status_t +UserVolume::Rename(void* oldDir, const char* oldName, void* newDir, + const char* newName) +{ + return B_BAD_VALUE; +} + +// #pragma mark - +// #pragma mark ----- directories ----- + +// MkDir +status_t +UserVolume::MkDir(void* dir, const char* name, int mode) +{ + return B_BAD_VALUE; +} + +// RmDir +status_t +UserVolume::RmDir(void* dir, const char* name) +{ + return B_BAD_VALUE; +} + +// OpenDir +status_t +UserVolume::OpenDir(void* node, void** cookie) +{ + return B_BAD_VALUE; +} + +// CloseDir +status_t +UserVolume::CloseDir(void* node, void* cookie) +{ + return B_BAD_VALUE; +} + +// FreeDirCookie +status_t +UserVolume::FreeDirCookie(void* node, void* cookie) +{ + return B_BAD_VALUE; +} + +// ReadDir +status_t +UserVolume::ReadDir(void* node, void* cookie, void* buffer, size_t bufferSize, + int32 count, int32* countRead) +{ + return B_BAD_VALUE; +} + +// RewindDir +status_t +UserVolume::RewindDir(void* node, void* cookie) +{ + return B_BAD_VALUE; +} + +// Walk +status_t +UserVolume::Walk(void* dir, const char* entryName, char** resolvedPath, + vnode_id* vnid) +{ + return B_BAD_VALUE; +} + +// #pragma mark - +// #pragma mark ----- attributes ----- + +// OpenAttrDir +status_t +UserVolume::OpenAttrDir(void* node, void** cookie) +{ + return B_BAD_VALUE; +} + +// CloseAttrDir +status_t +UserVolume::CloseAttrDir(void* node, void* cookie) +{ + return B_BAD_VALUE; +} + +// FreeAttrDirCookie +status_t +UserVolume::FreeAttrDirCookie(void* node, void* cookie) +{ + return B_BAD_VALUE; +} + +// ReadAttrDir +status_t +UserVolume::ReadAttrDir(void* node, void* cookie, void* buffer, size_t bufferSize, + int32 count, int32* countRead) +{ + return B_BAD_VALUE; +} + +// RewindAttrDir +status_t +UserVolume::RewindAttrDir(void* node, void* cookie) +{ + return B_BAD_VALUE; +} + +// ReadAttr +status_t +UserVolume::ReadAttr(void* node, const char* name, int type, off_t pos, + void* buffer, size_t bufferSize, size_t* bytesRead) +{ + return B_BAD_VALUE; +} + +// WriteAttr +status_t +UserVolume::WriteAttr(void* node, const char* name, int type, off_t pos, + const void* buffer, size_t bufferSize, size_t* bytesWritten) +{ + return B_BAD_VALUE; +} + +// RemoveAttr +status_t +UserVolume::RemoveAttr(void* node, const char* name) +{ + return B_BAD_VALUE; +} + +// RenameAttr +status_t +UserVolume::RenameAttr(void* node, const char* oldName, const char* newName) +{ + return B_BAD_VALUE; +} + +// StatAttr +status_t +UserVolume::StatAttr(void* node, const char* name, struct attr_info* attrInfo) +{ + return B_BAD_VALUE; +} + +// #pragma mark - +// #pragma mark ----- indices ----- + +// OpenIndexDir +status_t +UserVolume::OpenIndexDir(void** cookie) +{ + return B_BAD_VALUE; +} + +// CloseIndexDir +status_t +UserVolume::CloseIndexDir(void* cookie) +{ + return B_BAD_VALUE; +} + +// FreeIndexDirCookie +status_t +UserVolume::FreeIndexDirCookie(void* cookie) +{ + return B_BAD_VALUE; +} + +// ReadIndexDir +status_t +UserVolume::ReadIndexDir(void* cookie, void* buffer, size_t bufferSize, + int32 count, int32* countRead) +{ + return B_BAD_VALUE; +} + +// RewindIndexDir +status_t +UserVolume::RewindIndexDir(void* cookie) +{ + return B_BAD_VALUE; +} + +// CreateIndex +status_t +UserVolume::CreateIndex(const char* name, int type, int flags) +{ + return B_BAD_VALUE; +} + +// RemoveIndex +status_t +UserVolume::RemoveIndex(const char* name) +{ + return B_BAD_VALUE; +} + +// RenameIndex +status_t +UserVolume::RenameIndex(const char* oldName, const char* newName) +{ + return B_BAD_VALUE; +} + +// StatIndex +status_t +UserVolume::StatIndex(const char *name, struct index_info* indexInfo) +{ + return B_BAD_VALUE; +} + +// #pragma mark - +// #pragma mark ----- queries ----- + +// OpenQuery +status_t +UserVolume::OpenQuery(const char* queryString, ulong flags, port_id port, + long token, void** cookie) +{ + return B_BAD_VALUE; +} + +// CloseQuery +status_t +UserVolume::CloseQuery(void* cookie) +{ + return B_BAD_VALUE; +} + +// FreeQueryCookie +status_t +UserVolume::FreeQueryCookie(void* cookie) +{ + return B_BAD_VALUE; +} + +// ReadQuery +status_t +UserVolume::ReadQuery(void* cookie, void* buffer, size_t bufferSize, int32 count, + int32* countRead) +{ + return B_BAD_VALUE; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/UserVolume.h b/src/add-ons/kernel/file_systems/userlandfs/server/UserVolume.h new file mode 100644 index 0000000000..446ceb4386 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/UserVolume.h @@ -0,0 +1,143 @@ +// UserVolume.h + +#ifndef USERLAND_FS_USER_VOLUME_H +#define USERLAND_FS_USER_VOLUME_H + +#include +#include + +namespace UserlandFS { + +class UserFileSystem; + +class UserVolume { +public: + UserVolume(UserFileSystem* fileSystem, + nspace_id id); + virtual ~UserVolume(); + + UserFileSystem* GetFileSystem() const; + nspace_id GetID() const; + + // FS + virtual status_t Mount(const char* device, ulong flags, + const char* parameters, int32 len, + vnode_id* rootID); + virtual status_t Unmount(); + virtual status_t Sync(); + virtual status_t ReadFSStat(fs_info* info); + virtual status_t WriteFSStat(struct fs_info *info, long mask); + + // vnodes + virtual status_t ReadVNode(vnode_id vnid, char reenter, + void** node); + virtual status_t WriteVNode(void* node, char reenter); + virtual status_t RemoveVNode(void* node, char reenter); + + // nodes + virtual status_t FSync(void* node); + virtual status_t ReadStat(void* node, struct stat* st); + virtual status_t WriteStat(void* node, struct stat* st, + long mask); + virtual status_t Access(void* node, int mode); + + // files + virtual status_t Create(void* dir, const char* name, + int openMode, int mode, vnode_id* vnid, + void** cookie); + virtual status_t Open(void* node, int openMode, void** cookie); + virtual status_t Close(void* node, void* cookie); + virtual status_t FreeCookie(void* node, void* cookie); + virtual status_t Read(void* node, void* cookie, off_t pos, + void* buffer, size_t bufferSize, + size_t* bytesRead); + virtual status_t Write(void* node, void* cookie, off_t pos, + const void* buffer, size_t bufferSize, + size_t* bytesWritten); + virtual status_t IOCtl(void* node, void* cookie, int command, + void *buffer, size_t size); + virtual status_t SetFlags(void* node, void* cookie, int flags); + virtual status_t Select(void* node, void* cookie, uint8 event, + uint32 ref, selectsync* sync); + virtual status_t Deselect(void* node, void* cookie, uint8 event, + selectsync* sync); + + // hard links / symlinks + virtual status_t Link(void* dir, const char* name, void* node); + virtual status_t Unlink(void* dir, const char* name); + virtual status_t Symlink(void* dir, const char* name, + const char* target); + virtual status_t ReadLink(void* node, char* buffer, + size_t bufferSize, size_t* bytesRead); + virtual status_t Rename(void* oldDir, const char* oldName, + void* newDir, const char* newName); + + // directories + virtual status_t MkDir(void* dir, const char* name, int mode); + virtual status_t RmDir(void* dir, const char* name); + virtual status_t OpenDir(void* node, void** cookie); + virtual status_t CloseDir(void* node, void* cookie); + virtual status_t FreeDirCookie(void* node, void* cookie); + virtual status_t ReadDir(void* node, void* cookie, + void* buffer, size_t bufferSize, + int32 count, int32* countRead); + virtual status_t RewindDir(void* node, void* cookie); + virtual status_t Walk(void* dir, const char* entryName, + char** resolvedPath, vnode_id* vnid); + + // attributes + virtual status_t OpenAttrDir(void* node, void** cookie); + virtual status_t CloseAttrDir(void* node, void* cookie); + virtual status_t FreeAttrDirCookie(void* node, void* cookie); + virtual status_t ReadAttrDir(void* node, void* cookie, + void* buffer, size_t bufferSize, + int32 count, int32* countRead); + virtual status_t RewindAttrDir(void* node, void* cookie); + virtual status_t ReadAttr(void* node, const char* name, + int type, off_t pos, void* buffer, + size_t bufferSize, size_t* bytesRead); + virtual status_t WriteAttr(void* node, const char* name, + int type, off_t pos, const void* buffer, + size_t bufferSize, size_t* bytesWritten); + virtual status_t RemoveAttr(void* node, const char* name); + virtual status_t RenameAttr(void* node, const char* oldName, + const char* newName); + virtual status_t StatAttr(void* node, const char* name, + struct attr_info* attrInfo); + + // indices + virtual status_t OpenIndexDir(void** cookie); + virtual status_t CloseIndexDir(void* cookie); + virtual status_t FreeIndexDirCookie(void* cookie); + virtual status_t ReadIndexDir(void* cookie, void* buffer, + size_t bufferSize, int32 count, + int32* countRead); + virtual status_t RewindIndexDir(void* cookie); + virtual status_t CreateIndex(const char* name, int type, + int flags); + virtual status_t RemoveIndex(const char* name); + virtual status_t RenameIndex(const char* oldName, + const char* newName); + virtual status_t StatIndex(const char *name, + struct index_info* indexInfo); + + // queries + virtual status_t OpenQuery(const char* queryString, + ulong flags, port_id port, long token, + void** cookie); + virtual status_t CloseQuery(void* cookie); + virtual status_t FreeQueryCookie(void* cookie); + virtual status_t ReadQuery(void* cookie, void* buffer, + size_t bufferSize, int32 count, + int32* countRead); + +protected: + UserFileSystem* fFileSystem; + nspace_id fID; +}; + +} // namespace UserlandFS + +using UserlandFS::UserVolume; + +#endif // USERLAND_FS_USER_VOLUME_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/UserlandFSDispatcher.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/UserlandFSDispatcher.cpp new file mode 100644 index 0000000000..3fdfe6a6db --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/UserlandFSDispatcher.cpp @@ -0,0 +1,416 @@ +// UserlandFSDispatcher.cpp + +#include + +#include +#include +#include +#include +#include + +#include "AutoDeleter.h" +#include "AutoLocker.h" +#include "Compatibility.h" +#include "Debug.h" +#include "DispatcherDefs.h" +#include "FileSystem.h" +#include "FSInfo.h" +#include "RequestAllocator.h" +#include "RequestPort.h" +#include "Requests.h" +#include "ServerDefs.h" +#include "String.h" +#include "UserlandFSDispatcher.h" + +// constructor +UserlandFSDispatcher::UserlandFSDispatcher(const char* signature) + : BApplication(signature), + fTerminating(false), + fRequestProcessor(-1), + fConnectionPort(-1), + fConnectionReplyPort(-1), + fRequestLock(), + fRequestPort(NULL) +{ +} + +// destructor +UserlandFSDispatcher::~UserlandFSDispatcher() +{ + fTerminating = true; + // stop roster watching + be_roster->StopWatching(this); + // close/delete the ports + fRequestLock.Lock(); + if (fRequestPort) + fRequestPort->Close(); + fRequestLock.Unlock(); + if (fConnectionPort >= 0) + delete_port(fConnectionPort); + if (fConnectionReplyPort >= 0) + delete_port(fConnectionReplyPort); + // wait for the request processor + if (fRequestProcessor >= 0) { + int32 result; + wait_for_thread(fRequestProcessor, &result); + } +} + +// Init +status_t +UserlandFSDispatcher::Init() +{ + // ensure that we are the only dispatcher + BClipboard clipboard(kUserlandFSDispatcherClipboardName); + if (!clipboard.Lock()) { + ERROR(("Failed to lock the clipboard.\n")); + return B_ERROR; + } + status_t error = B_OK; + if (BMessage* data = clipboard.Data()) { + // check the old value in the clipboard + BMessenger messenger; + if (data->FindMessenger("messenger", &messenger) == B_OK) { + if (messenger.IsValid()) { + PRINT(("There's already a dispatcher running.\n")); + error = B_ERROR; + } + } + // clear the clipboard + if (error == B_OK) { + clipboard.Clear(); + data = clipboard.Data(); + if (!data) + error = B_ERROR; + } + // add our messenger + if (error == B_OK) { + SET_ERROR(error, data->AddMessenger("messenger", be_app_messenger)); + if (error == B_OK) + SET_ERROR(error, clipboard.Commit()); + // work-around for BeOS R5: The very first commit to a clipboard + // (i.e. the one that creates the clipboard) seems to be ignored. + if (error == B_OK) + SET_ERROR(error, clipboard.Commit()); + if (error != B_OK) + ERROR(("Failed to set clipboard messenger.\n")); + } + } else { + ERROR(("Failed to get clipboard data container\n")); + error = B_ERROR; + } + clipboard.Unlock(); + if (error != B_OK) + return error; + // create the connection port and connection reply port + fConnectionPort = create_port(1, kUserlandFSDispatcherPortName); + if (fConnectionPort < 0) + return fConnectionPort; + fConnectionReplyPort = create_port(1, kUserlandFSDispatcherReplyPortName); + if (fConnectionReplyPort < 0) + return fConnectionReplyPort; + // start watching for terminated applications + error = be_roster->StartWatching(this, B_REQUEST_QUIT); + if (error != B_OK) + return error ; + // spawn request processor thread + fRequestProcessor = spawn_thread(_RequestProcessorEntry, + "main request processor", B_NORMAL_PRIORITY, this); + if (fRequestProcessor < 0) + return fRequestProcessor; + resume_thread(fRequestProcessor); + return B_OK; +} + +// MessageReceived +void +UserlandFSDispatcher::MessageReceived(BMessage* message) +{ + switch (message->what) { + case UFS_REGISTER_FS: + { + // get the team + team_id team; + status_t error = message->FindInt32("team", &team); + if (error != B_OK) + PRINT(("UFS_REGISTER_FS failed: no team\n")); + // get the FS info + FSInfo* info = NULL; + if (error == B_OK) { + info = new(nothrow) FSInfo; + if (info) { + error = info->SetTo(message); + } else { + error = B_NO_MEMORY; + PRINT(("UFS_REGISTER_FS failed: failed to allocate " + "FSInfo\n")); + } + } + ObjectDeleter infoDeleter(info); + // find the FileSystem + FileSystem* fileSystem = NULL; + if (error == B_OK) { + AutoLocker _(fFileSystems); + fileSystem = _GetFileSystemNoInit(team); + if (fileSystem) { + fileSystem->CompleteInit(info); + infoDeleter.Detach(); + } else { + PRINT(("UFS_REGISTER_FS: no FileSystem found for " + "team %ld, trying to register anyway\n", team)); + // try to find by name + fileSystem = fFileSystems.Get(info->GetName()); + if (fileSystem) { + // there's already an FS with that name registered + PRINT(("UFS_REGISTER_FS failed: FileSystem with " + "name %s does already exist.\n", info->GetName())); + fileSystem = NULL; + error = B_ERROR; + } else { + // the FS is not known yet: create one + fileSystem = new FileSystem(team, info, &error); + if (fileSystem) { + infoDeleter.Detach(); + } else { + error = B_NO_MEMORY; + PRINT(("UFS_REGISTER_FS failed: failed to allocate " + "FileSystem\n")); + } + // add it + if (error == B_OK) { + error = fFileSystems.Put(info->GetName(), + fileSystem); + if (error != B_OK) { + PRINT(("UFS_REGISTER_FS failed: failed to " + "add FileSystem\n")); + delete fileSystem; + } + } + } + } + } + // send the reply + if (error == B_OK) + message->SendReply(UFS_REGISTER_FS_ACK); + else + message->SendReply(UFS_REGISTER_FS_DENIED); + if (fileSystem) + _PutFileSystem(fileSystem); + break; + } + case B_SOME_APP_QUIT: + { + // get the team + team_id team; + status_t error = message->FindInt32("be:team", &team); + if (error != B_OK) + return; + // find the FileSystem + FileSystem* fileSystem = _GetFileSystemNoInit(team); + if (!fileSystem) + return; + // abort the initialization + fileSystem->AbortInit(); + _PutFileSystem(fileSystem); + } + default: + BApplication::MessageReceived(message); + } +} + +// _GetFileSystem +status_t +UserlandFSDispatcher::_GetFileSystem(const char* name, FileSystem** _fileSystem) +{ + if (!name || !_fileSystem) + RETURN_ERROR(B_BAD_VALUE); + // get the file system + FileSystem* fileSystem; + { + AutoLocker _(fFileSystems); + fileSystem = fFileSystems.Get(name); + if (fileSystem) { + fileSystem->AddReference(); + } else { + // doesn't exists yet: create + status_t error; + fileSystem = new(nothrow) FileSystem(name, &error); + if (!fileSystem) + RETURN_ERROR(B_NO_MEMORY); + if (error == B_OK) + error = fFileSystems.Put(fileSystem->GetName(), fileSystem); + if (error != B_OK) { + delete fileSystem; + RETURN_ERROR(error); + } + } + } + // prepare access + status_t error = fileSystem->Access(); + if (error != B_OK) { + _PutFileSystem(fileSystem); + RETURN_ERROR(error); + } + *_fileSystem = fileSystem; + return B_OK; +} + +// _GetFileSystemNoInit +FileSystem* +UserlandFSDispatcher::_GetFileSystemNoInit(team_id team) +{ + AutoLocker _(fFileSystems); + for (FileSystemMap::Iterator it = fFileSystems.GetIterator(); + it.HasNext();) { + FileSystem* fileSystem = it.Next().value; + if (fileSystem->GetTeam() == team) { + // found it + if (fileSystem) + fileSystem->AddReference(); + return fileSystem; + } + } + return NULL; +} + +// _PutFileSystem +status_t +UserlandFSDispatcher::_PutFileSystem(FileSystem* fileSystem) +{ + if (!fileSystem) + RETURN_ERROR(B_BAD_VALUE); + AutoLocker _(fFileSystems); + if (fFileSystems.Get(fileSystem->GetName()) != fileSystem) + RETURN_ERROR(B_BAD_VALUE); + if (fileSystem->RemoveReference() && fileSystem->InitCheck() != B_OK) { +PRINT(("removing FileSystem `%s'\n", fileSystem->GetName())); + fFileSystems.Remove(fileSystem->GetName()); + delete fileSystem; + } + return B_OK; +} + +// _WaitForConnection +bool +UserlandFSDispatcher::_WaitForConnection() +{ + while (!fTerminating) { + int32 code; + char buffer; + size_t bytesRead = read_port(fConnectionPort, &code, &buffer, 0); + if (bytesRead >= 0 && code == UFS_DISPATCHER_CONNECT) { + const Port::Info* info = fRequestPort->GetPortInfo(); + size_t bytesWritten = write_port(fConnectionReplyPort, + UFS_DISPATCHER_CONNECT_ACK, info, sizeof(Port::Info)); + if (bytesWritten >= 0) + return true; + } + } + return false; +} + +// _ProcessRequests +status_t +UserlandFSDispatcher::_ProcessRequests() +{ + while (!fTerminating) { + Request* request; + status_t error = fRequestPort->ReceiveRequest(&request); + if (error != B_OK) + RETURN_ERROR(error); + RequestReleaser _(fRequestPort, request); + // check the request type + if (request->GetType() == UFS_DISCONNECT_REQUEST) + return B_OK; + if (request->GetType() != FS_CONNECT_REQUEST) + RETURN_ERROR(B_BAD_VALUE); + PRINT(("UserlandFSDispatcher::_ProcessRequests(): received FS connect " + "request\n")); + // it's an FS connect request + FSConnectRequest* connectRequest = (FSConnectRequest*)request; + // get the FS name + int32 len = connectRequest->fsName.GetSize(); + status_t result = B_OK; + if (len <= 0) + result = B_BAD_DATA; + String fsName; + if (result == B_OK) + fsName.SetTo((const char*)connectRequest->fsName.GetData(), len); + if (result == B_OK && fsName.GetLength() == 0) + result = B_BAD_DATA; + // prepare the reply + RequestAllocator allocator(fRequestPort->GetPort()); + FSConnectReply* reply; + error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + FileSystem* fileSystem = NULL; + if (result == B_OK) + result = _GetFileSystem(fsName.GetString(), &fileSystem); + if (result == B_OK) { + const FSInfo* info = fileSystem->GetInfo(); + result = allocator.AllocateData(reply->portInfos, + info->GetInfos(), info->GetSize(), sizeof(Port::Info)); + if (result == B_OK) + reply->portInfoCount = info->CountInfos(); + _PutFileSystem(fileSystem); + } + reply->error = result; + // send it + error = fRequestPort->SendRequest(&allocator); + if (error != B_OK) + RETURN_ERROR(error); + } + return B_OK; +} + +// _RequestProcessorEntry +int32 +UserlandFSDispatcher::_RequestProcessorEntry(void* data) +{ + return ((UserlandFSDispatcher*)data)->_RequestProcessor(); +} + +// _RequestProcessor +int32 +UserlandFSDispatcher::_RequestProcessor() +{ +PRINT(("UserlandFSDispatcher::_RequestProcessor()\n")); + while (!fTerminating) { + // allocate a request port + status_t error = B_OK; + { + fRequestLock.Lock(); + fRequestPort = new(nothrow) RequestPort(kRequestPortSize); + if (fRequestPort) + error = fRequestPort->InitCheck(); + else + error = B_NO_MEMORY; + if (error != B_OK) { + delete fRequestPort; + fRequestPort = NULL; + } + fRequestLock.Unlock(); + } + if (error != B_OK) { + be_app->PostMessage(B_QUIT_REQUESTED); +PRINT((" failed to allocate request port: %s\n", strerror(error))); + return error; + } + // wait for a connection and process the requests + if (_WaitForConnection()) { + PRINT(("UserlandFSDispatcher::_RequestProcessor(): connected\n")); + _ProcessRequests(); + PRINT(("UserlandFSDispatcher::_RequestProcessor(): " + "disconnected\n")); + } + // delete the request port + fRequestLock.Lock(); + delete fRequestPort; + fRequestPort = NULL; + fRequestLock.Unlock(); + } +PRINT(("UserlandFSDispatcher::_RequestProcessor() done\n")); + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/UserlandFSDispatcher.h b/src/add-ons/kernel/file_systems/userlandfs/server/UserlandFSDispatcher.h new file mode 100644 index 0000000000..97059864f9 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/UserlandFSDispatcher.h @@ -0,0 +1,65 @@ +// UserlandFSDispatcher.h + +#ifndef USERLAND_FS_DISPATCHER_H +#define USERLAND_FS_DISPATCHER_H + +#include +#include +#include + +#include "HashMap.h" + +namespace UserlandFSUtil { + +class RequestPort; +class String; + +} + +using UserlandFSUtil::RequestPort; +using UserlandFSUtil::String; + +namespace UserlandFS { + +class FileSystem; + +class UserlandFSDispatcher : public BApplication { +public: + UserlandFSDispatcher(const char* signature); + virtual ~UserlandFSDispatcher(); + + status_t Init(); + + virtual void MessageReceived(BMessage* message); + +private: + status_t _GetFileSystem(const char* name, + FileSystem** fileSystem); + status_t _GetFileSystemNoInit(const char* name, + FileSystem** fileSystem); + FileSystem* _GetFileSystemNoInit(team_id team); + status_t _PutFileSystem(FileSystem* fileSystem); + + bool _WaitForConnection(); + status_t _ProcessRequests(); + + static int32 _RequestProcessorEntry(void* data); + int32 _RequestProcessor(); + +private: + typedef SynchronizedHashMap FileSystemMap; + + bool fTerminating; + thread_id fRequestProcessor; + port_id fConnectionPort; + port_id fConnectionReplyPort; + BLocker fRequestLock; + RequestPort* fRequestPort; + FileSystemMap fFileSystems; +}; + +} // namespace UserlandFS + +using UserlandFS::UserlandFSDispatcher; + +#endif // USERLAND_FS_DISPATCHER_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/UserlandFSServer.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/UserlandFSServer.cpp new file mode 100644 index 0000000000..95348b3c82 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/UserlandFSServer.cpp @@ -0,0 +1,197 @@ +// UserlandFSServer.cpp + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AutoLocker.h" +#include "Compatibility.h" +#include "Debug.h" +#include "DispatcherDefs.h" +#include "FSInfo.h" +#include "KernelUserFileSystem.h" +#include "RequestThread.h" +#include "ServerDefs.h" +#include "UserFileSystem.h" +#include "UserlandFSServer.h" + +static const int32 kRequestThreadCount = 10; + +static const int32 kMaxBlockCacheBlocks = 16384; + +// constructor +UserlandFSServer::UserlandFSServer(const char* signature) + : BApplication(signature), + fAddOnImage(-1), + fFileSystem(NULL), + fNotificationRequestPort(NULL), + fRequestThreads(NULL), + fBlockCacheInitialized(false) +{ +} + +// destructor +UserlandFSServer::~UserlandFSServer() +{ + if (fRequestThreads) { + for (int32 i = 0; i < kRequestThreadCount; i++) + fRequestThreads[i].PrepareTermination(); + for (int32 i = 0; i < kRequestThreadCount; i++) + fRequestThreads[i].Terminate(); + delete[] fRequestThreads; + } + delete fNotificationRequestPort; + delete fFileSystem; + if (fBlockCacheInitialized) + shutdown_block_cache(); + if (fAddOnImage >= 0) + unload_add_on(fAddOnImage); +} + +// Init +status_t +UserlandFSServer::Init(const char* fileSystem) +{ + // get the add-on path + BPath addOnPath; + status_t error = find_directory(B_USER_ADDONS_DIRECTORY, &addOnPath); + if (error != B_OK) + RETURN_ERROR(error); + error = addOnPath.Append("userlandfs"); + if (error != B_OK) + RETURN_ERROR(error); + error = addOnPath.Append(fileSystem); + if (error != B_OK) + RETURN_ERROR(error); + // load the add-on + fAddOnImage = load_add_on(addOnPath.Path()); + if (fAddOnImage < 0) + RETURN_ERROR(fAddOnImage); + // get the symbols "fs_entry" and "api_version" + vnode_ops* fsOps; + error = get_image_symbol(fAddOnImage, "fs_entry", B_SYMBOL_TYPE_TEXT, + (void**)&fsOps); + if (error != B_OK) + RETURN_ERROR(error); + int32* apiVersion; + error = get_image_symbol(fAddOnImage, "api_version", B_SYMBOL_TYPE_DATA, + (void**)&apiVersion); + if (error != B_OK) + RETURN_ERROR(error); + // check api version + if (*apiVersion != B_CUR_FS_API_VERSION) + RETURN_ERROR(B_ERROR); + // create the file system + fFileSystem = new(nothrow) KernelUserFileSystem(fsOps); + if (!fileSystem) + RETURN_ERROR(B_NO_MEMORY); + // init the block cache + error = init_block_cache(kMaxBlockCacheBlocks, 0); + if (error != B_OK) + RETURN_ERROR(error); + fBlockCacheInitialized = true; + // create the notification request port + fNotificationRequestPort = new(nothrow) RequestPort(kRequestPortSize); + if (!fNotificationRequestPort) + RETURN_ERROR(B_NO_MEMORY); + error = fNotificationRequestPort->InitCheck(); + if (error != B_OK) + RETURN_ERROR(error); + // now create the request threads + fRequestThreads = new(nothrow) RequestThread[kRequestThreadCount]; + if (!fRequestThreads) + RETURN_ERROR(B_NO_MEMORY); + for (int32 i = 0; i < kRequestThreadCount; i++) { + error = fRequestThreads[i].Init(fFileSystem); + if (error != B_OK) + RETURN_ERROR(error); + } + // run the threads + for (int32 i = 0; i < kRequestThreadCount; i++) + fRequestThreads[i].Run(); + // enter the debugger here, if desired + if (gServerSettings.ShallEnterDebugger()) + debugger("File system ready to use."); + // finally register with the dispatcher + error = _RegisterWithDispatcher(fileSystem); + RETURN_ERROR(error); +} + +// GetNotificationRequestPort +RequestPort* +UserlandFSServer::GetNotificationRequestPort() +{ + if (UserlandFSServer* server = dynamic_cast(be_app)) + return server->fNotificationRequestPort; + return NULL; +} + +// GetFileSystem +UserFileSystem* +UserlandFSServer::GetFileSystem() +{ + if (UserlandFSServer* server = dynamic_cast(be_app)) + return server->fFileSystem; + return NULL; +} + +// _RegisterWithDispatcher +status_t +UserlandFSServer::_RegisterWithDispatcher(const char* fsName) +{ + // get the dispatcher messenger from the clipboard + BMessenger messenger; + BClipboard clipboard(kUserlandFSDispatcherClipboardName); + if (AutoLocker locker = clipboard) { + status_t error = B_OK; + if (BMessage* data = clipboard.Data()) { + error = data->FindMessenger("messenger", &messenger); + if (error != B_OK) { + ERROR(("No dispatcher messenger in clipboard.\n")); + return error; + } + if (!messenger.IsValid()) { + ERROR(("Found dispatcher messenger not valid.\n")); + return B_ERROR; + } + } else { + ERROR(("Failed to get clipboard data container\n")); + return B_ERROR; + } + } else { + ERROR(("Failed to lock the clipboard.\n")); + return B_ERROR; + } + // get the port infos + Port::Info infos[kRequestThreadCount + 1]; + infos[0] = *fNotificationRequestPort->GetPortInfo(); + for (int32 i = 0; i < kRequestThreadCount; i++) + infos[i + 1] = *fRequestThreads[i].GetPortInfo(); + // init an FS info + FSInfo info; + status_t error = info.SetTo(fsName, infos, kRequestThreadCount + 1); + // prepare the message + BMessage message(UFS_REGISTER_FS); + if (error == B_OK) + error = message.AddInt32("team", Team()); + if (error == B_OK) + error = info.Archive(&message); + // send the message + BMessage reply; + error = messenger.SendMessage(&message, &reply); + if (error == B_OK && reply.what != UFS_REGISTER_FS_ACK) { + ERROR(("FS registration failed.\n")); + error = B_ERROR; + } + return error; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/UserlandFSServer.h b/src/add-ons/kernel/file_systems/userlandfs/server/UserlandFSServer.h new file mode 100644 index 0000000000..946294f0dc --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/UserlandFSServer.h @@ -0,0 +1,39 @@ +// UserlandFSServer.h + +#ifndef USERLAND_FS_SERVER_H +#define USERLAND_FS_SERVER_H + +#include + +namespace UserlandFS { + +class RequestThread; +class UserFileSystem; + +class UserlandFSServer : public BApplication { +public: + UserlandFSServer(const char* signature); + virtual ~UserlandFSServer(); + + status_t Init(const char* fileSystem); + + static RequestPort* GetNotificationRequestPort(); + static UserFileSystem* GetFileSystem(); + +private: + status_t _RegisterWithDispatcher(const char* fsName); +private: + image_id fAddOnImage; + UserFileSystem* fFileSystem; + RequestPort* fNotificationRequestPort; + RequestThread* fRequestThreads; + bool fBlockCacheInitialized; +}; + +} // namespace UserlandFS + +using UserlandFS::RequestThread; +using UserlandFS::UserFileSystem; +using UserlandFS::UserlandFSServer; + +#endif // USERLAND_FS_SERVER_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/UserlandRequestHandler.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/UserlandRequestHandler.cpp new file mode 100644 index 0000000000..d5bb43d864 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/UserlandRequestHandler.cpp @@ -0,0 +1,1851 @@ +// UserlandRequestHandler.cpp + +#include "AutoDeleter.h" +#include "Compatibility.h" +#include "Debug.h" +#include "UserVolume.h" +#include "RequestPort.h" +#include "Requests.h" +#include "RequestThread.h" +#include "SingleReplyRequestHandler.h" +#include "UserFileSystem.h" +#include "UserlandRequestHandler.h" + +// constructor +UserlandRequestHandler::UserlandRequestHandler(UserFileSystem* fileSystem) + : RequestHandler(), + fFileSystem(fileSystem), + fExpectReply(false), + fExpectedReply(0) +{ +} + +// constructor +UserlandRequestHandler::UserlandRequestHandler(UserFileSystem* fileSystem, + uint32 expectedReply) + : RequestHandler(), + fFileSystem(fileSystem), + fExpectReply(true), + fExpectedReply(expectedReply) +{ +} + +// destructor +UserlandRequestHandler::~UserlandRequestHandler() +{ +} + +// HandleRequest +status_t +UserlandRequestHandler::HandleRequest(Request* request) +{ + if (fExpectReply && request->GetType() == fExpectedReply) { + fDone = true; + return B_OK; + } + switch (request->GetType()) { + // FS + case MOUNT_VOLUME_REQUEST: + return _HandleRequest((MountVolumeRequest*)request); + case UNMOUNT_VOLUME_REQUEST: + return _HandleRequest((UnmountVolumeRequest*)request); + case SYNC_VOLUME_REQUEST: + return _HandleRequest((SyncVolumeRequest*)request); + case READ_FS_STAT_REQUEST: + return _HandleRequest((ReadFSStatRequest*)request); + case WRITE_FS_STAT_REQUEST: + return _HandleRequest((WriteFSStatRequest*)request); + // vnodes + case READ_VNODE_REQUEST: + return _HandleRequest((ReadVNodeRequest*)request); + case WRITE_VNODE_REQUEST: + return _HandleRequest((WriteVNodeRequest*)request); + case FS_REMOVE_VNODE_REQUEST: + return _HandleRequest((FSRemoveVNodeRequest*)request); + // nodes + case FSYNC_REQUEST: + return _HandleRequest((FSyncRequest*)request); + case READ_STAT_REQUEST: + return _HandleRequest((ReadStatRequest*)request); + case WRITE_STAT_REQUEST: + return _HandleRequest((WriteStatRequest*)request); + case ACCESS_REQUEST: + return _HandleRequest((AccessRequest*)request); + // files + case CREATE_REQUEST: + return _HandleRequest((CreateRequest*)request); + case OPEN_REQUEST: + return _HandleRequest((OpenRequest*)request); + case CLOSE_REQUEST: + return _HandleRequest((CloseRequest*)request); + case FREE_COOKIE_REQUEST: + return _HandleRequest((FreeCookieRequest*)request); + case READ_REQUEST: + return _HandleRequest((ReadRequest*)request); + case WRITE_REQUEST: + return _HandleRequest((WriteRequest*)request); + case IOCTL_REQUEST: + return _HandleRequest((IOCtlRequest*)request); + case SET_FLAGS_REQUEST: + return _HandleRequest((SetFlagsRequest*)request); + case SELECT_REQUEST: + return _HandleRequest((SelectRequest*)request); + case DESELECT_REQUEST: + return _HandleRequest((DeselectRequest*)request); + // hard links / symlinks + case LINK_REQUEST: + return _HandleRequest((LinkRequest*)request); + case UNLINK_REQUEST: + return _HandleRequest((UnlinkRequest*)request); + case SYMLINK_REQUEST: + return _HandleRequest((SymlinkRequest*)request); + case READ_LINK_REQUEST: + return _HandleRequest((ReadLinkRequest*)request); + case RENAME_REQUEST: + return _HandleRequest((RenameRequest*)request); + // directories + case MKDIR_REQUEST: + return _HandleRequest((MkDirRequest*)request); + case RMDIR_REQUEST: + return _HandleRequest((RmDirRequest*)request); + case OPEN_DIR_REQUEST: + return _HandleRequest((OpenDirRequest*)request); + case CLOSE_DIR_REQUEST: + return _HandleRequest((CloseDirRequest*)request); + case FREE_DIR_COOKIE_REQUEST: + return _HandleRequest((FreeDirCookieRequest*)request); + case READ_DIR_REQUEST: + return _HandleRequest((ReadDirRequest*)request); + case REWIND_DIR_REQUEST: + return _HandleRequest((RewindDirRequest*)request); + case WALK_REQUEST: + return _HandleRequest((WalkRequest*)request); + // attributes + case OPEN_ATTR_DIR_REQUEST: + return _HandleRequest((OpenAttrDirRequest*)request); + case CLOSE_ATTR_DIR_REQUEST: + return _HandleRequest((CloseAttrDirRequest*)request); + case FREE_ATTR_DIR_COOKIE_REQUEST: + return _HandleRequest((FreeAttrDirCookieRequest*)request); + case READ_ATTR_DIR_REQUEST: + return _HandleRequest((ReadAttrDirRequest*)request); + case REWIND_ATTR_DIR_REQUEST: + return _HandleRequest((RewindAttrDirRequest*)request); + case READ_ATTR_REQUEST: + return _HandleRequest((ReadAttrRequest*)request); + case WRITE_ATTR_REQUEST: + return _HandleRequest((WriteAttrRequest*)request); + case REMOVE_ATTR_REQUEST: + return _HandleRequest((RemoveAttrRequest*)request); + case RENAME_ATTR_REQUEST: + return _HandleRequest((RenameAttrRequest*)request); + case STAT_ATTR_REQUEST: + return _HandleRequest((StatAttrRequest*)request); + // indices + case OPEN_INDEX_DIR_REQUEST: + return _HandleRequest((OpenIndexDirRequest*)request); + case CLOSE_INDEX_DIR_REQUEST: + return _HandleRequest((CloseIndexDirRequest*)request); + case FREE_INDEX_DIR_COOKIE_REQUEST: + return _HandleRequest((FreeIndexDirCookieRequest*)request); + case READ_INDEX_DIR_REQUEST: + return _HandleRequest((ReadIndexDirRequest*)request); + case REWIND_INDEX_DIR_REQUEST: + return _HandleRequest((RewindIndexDirRequest*)request); + case CREATE_INDEX_REQUEST: + return _HandleRequest((CreateIndexRequest*)request); + case REMOVE_INDEX_REQUEST: + return _HandleRequest((RemoveIndexRequest*)request); + case RENAME_INDEX_REQUEST: + return _HandleRequest((RenameIndexRequest*)request); + case STAT_INDEX_REQUEST: + return _HandleRequest((StatIndexRequest*)request); + // queries + case OPEN_QUERY_REQUEST: + return _HandleRequest((OpenQueryRequest*)request); + case CLOSE_QUERY_REQUEST: + return _HandleRequest((CloseQueryRequest*)request); + case FREE_QUERY_COOKIE_REQUEST: + return _HandleRequest((FreeQueryCookieRequest*)request); + case READ_QUERY_REQUEST: + return _HandleRequest((ReadQueryRequest*)request); + } +PRINT(("UserlandRequestHandler::HandleRequest(): unexpected request: %lu\n", +request->GetType())); + return B_BAD_DATA; +} + + +// #pragma mark - +// #pragma mark ----- FS ----- + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(MountVolumeRequest* request) +{ + // check and execute the request + status_t result = B_OK; + // if the device path is relative, make it absolute by appending the + // provided CWD + const char* device = (const char*)request->device.GetData(); + char stackDevice[B_PATH_NAME_LENGTH]; + if (result == B_OK) { + if (device && device[0] != '/') { + if (const char* cwd = (const char*)request->cwd.GetData()) { + int32 deviceLen = strlen(device); + int32 cwdLen = strlen(cwd); + if (cwdLen + 1 + deviceLen < (int32)sizeof(stackDevice)) { + strcpy(stackDevice, cwd); + strcat(stackDevice, "/"); + strcat(stackDevice, device); + device = stackDevice; + } else + result = B_NAME_TOO_LONG; + } else + result = B_BAD_VALUE; + } + } + + // create the volume + UserVolume* volume = NULL; + if (result == B_OK) + result = fFileSystem->CreateVolume(&volume, request->nsid); + + // mount it + vnode_id rootID; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->Mount(device, request->flags, + (const char*)request->parameters.GetData(), + request->parameters.GetSize(), &rootID); + if (result != B_OK) + fFileSystem->DeleteVolume(volume); + } + if (result != B_OK) + volume = NULL; + + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + MountVolumeReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + reply->volume = volume; + reply->rootID = rootID; + + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(UnmountVolumeRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->Unmount(); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + UnmountVolumeReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(SyncVolumeRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->Sync(); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + SyncVolumeReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(ReadFSStatRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + fs_info info; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->ReadFSStat(&info); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + ReadFSStatReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + reply->info = info; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(WriteFSStatRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->WriteFSStat(&request->info, request->mask); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + WriteFSStatReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + + +// #pragma mark - +// #pragma mark ----- vnodes ----- + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(ReadVNodeRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + void* node; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->ReadVNode(request->vnid, request->reenter, &node); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + ReadVNodeReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + reply->node = node; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(WriteVNodeRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->WriteVNode(request->node, request->reenter); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + WriteVNodeReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(FSRemoveVNodeRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->RemoveVNode(request->node, request->reenter); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + FSRemoveVNodeReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + + +// #pragma mark - +// #pragma mark ----- nodes ----- + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(FSyncRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->FSync(request->node); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + FSyncReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(ReadStatRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + struct stat st; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->ReadStat(request->node, &st); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + ReadStatReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + reply->st = st; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(WriteStatRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->WriteStat(request->node, &request->st, request->mask); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + WriteStatReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(AccessRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->Access(request->node, request->mode); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + AccessReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + + +// #pragma mark - +// #pragma mark ----- files ----- + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(CreateRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + vnode_id vnid; + void* fileCookie; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->Create(request->node, + (const char*)request->name.GetData(), request->openMode, + request->mode, &vnid, &fileCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + CreateReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + reply->vnid = vnid; + reply->fileCookie = fileCookie; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(OpenRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + void* fileCookie; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->Open(request->node, request->openMode, &fileCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + OpenReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + reply->fileCookie = fileCookie; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(CloseRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->Close(request->node, request->fileCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + CloseReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(FreeCookieRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->FreeCookie(request->node, request->fileCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + FreeCookieReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(ReadRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + void* node = request->node; + void* fileCookie = request->fileCookie; + off_t pos = request->pos; + size_t size = request->size; + // allocate the reply + RequestAllocator allocator(fPort->GetPort()); + ReadReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + void* buffer; + if (result == B_OK) { + result = allocator.AllocateAddress(reply->buffer, size, 1, &buffer, + true); + } + // execute the request + size_t bytesRead; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->Read(node, fileCookie, pos, buffer, size, &bytesRead); + } + // reconstruct the reply, in case it has been overwritten + reply = new(reply) ReadReply; + // send the reply + reply->error = result; + reply->bytesRead = bytesRead; + return _SendReply(allocator, (result == B_OK)); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(WriteRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + size_t bytesWritten; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->Write(request->node, request->fileCookie, + request->pos, request->buffer.GetData(), request->buffer.GetSize(), + &bytesWritten); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + WriteReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + reply->bytesWritten = bytesWritten; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(IOCtlRequest* request) +{ + // get the request parameters + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + void* buffer = request->bufferParameter; + size_t len = request->lenParameter; + bool isBuffer = request->isBuffer; + int32 bufferSize = 0; + int32 writeSize = request->writeSize; + if (isBuffer) { + buffer = request->buffer.GetData(); + bufferSize = request->buffer.GetSize(); + } + // allocate the reply + RequestAllocator allocator(fPort->GetPort()); + IOCtlReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + // allocate the writable buffer for the call + void* writeBuffer = NULL; + if (result == B_OK && isBuffer && writeSize > 0) { + if (writeSize < bufferSize) + writeSize = bufferSize; + result = allocator.AllocateAddress(reply->buffer, writeSize, 8, + (void**)&writeBuffer, true); + if (result == B_OK && bufferSize > 0) + memcpy(writeBuffer, buffer, bufferSize); + buffer = writeBuffer; + } + // execute the request + status_t ioctlError = B_OK; + if (result == B_OK) { + RequestThreadContext context(volume); + ioctlError = volume->IOCtl(request->node, request->fileCookie, + request->command, buffer, len); + } + // reconstruct the reply, in case it has been overwritten + reply = new(reply) IOCtlReply; + // send the reply + reply->error = result; + reply->ioctlError = ioctlError; + return _SendReply(allocator, (result == B_OK && writeBuffer)); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(SetFlagsRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->SetFlags(request->node, request->fileCookie, + request->flags); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + SetFlagsReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(SelectRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->Select(request->node, request->fileCookie, + request->event, request->ref, request->sync); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + SelectReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(DeselectRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->Deselect(request->node, request->fileCookie, + request->event, request->sync); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + DeselectReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + + +// #pragma mark - +// #pragma mark ----- hard links / symlinks ----- + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(LinkRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->Link(request->node, + (const char*)request->name.GetData(), request->target); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + LinkReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(UnlinkRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->Unlink(request->node, + (const char*)request->name.GetData()); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + UnlinkReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(SymlinkRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->Symlink(request->node, + (const char*)request->name.GetData(), + (const char*)request->target.GetData()); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + SymlinkReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(ReadLinkRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + void* node = request->node; + size_t bufferSize = request->size; + // allocate the reply + RequestAllocator allocator(fPort->GetPort()); + ReadLinkReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + char* buffer; + if (result == B_OK) { + result = allocator.AllocateAddress(reply->buffer, bufferSize, 1, + (void**)&buffer, true); + } + // execute the request + size_t bytesRead; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->ReadLink(node, buffer, bufferSize, &bytesRead); + } + // reconstruct the reply, in case it has been overwritten + reply = new(reply) ReadLinkReply; + // send the reply + reply->error = result; + reply->bytesRead = bytesRead; + return _SendReply(allocator, (result == B_OK)); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(RenameRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->Rename(request->oldDir, + (const char*)request->oldName.GetData(), request->newDir, + (const char*)request->newName.GetData()); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + RenameReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + + +// #pragma mark - +// #pragma mark ----- directories ----- + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(MkDirRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->MkDir(request->node, + (const char*)request->name.GetData(), request->mode); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + MkDirReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(RmDirRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->RmDir(request->node, + (const char*)request->name.GetData()); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + RmDirReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(OpenDirRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + void* dirCookie; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->OpenDir(request->node, &dirCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + OpenDirReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + reply->dirCookie = dirCookie; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(CloseDirRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->CloseDir(request->node, request->dirCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + CloseDirReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(FreeDirCookieRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->FreeDirCookie(request->node, request->dirCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + FreeDirCookieReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(ReadDirRequest* request) +{ + // check the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + void* node = request->node; + void* dirCookie = request->dirCookie; + size_t bufferSize = request->bufferSize; + int32 count = request->count; + // allocate the reply + RequestAllocator allocator(fPort->GetPort()); + ReadDirReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + void* buffer; + if (result == B_OK) { + result = allocator.AllocateAddress(reply->buffer, bufferSize, 1, + &buffer, true); + } + // execute the request + int32 countRead; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->ReadDir(node, dirCookie, buffer, bufferSize, count, + &countRead); + } +D( +if (result == B_OK && countRead > 0) { + dirent* entry = (dirent*)buffer; + PRINT((" entry: d_dev: %ld, d_pdev: %ld, d_ino: %Ld, d_pino: %Ld, " + "d_reclen: %hu, d_name: %.32s\n", + entry->d_dev, entry->d_pdev, entry->d_ino, entry->d_pino, + entry->d_reclen, entry->d_name)); +} +) + // reconstruct the reply, in case it has been overwritten + reply = new(reply) ReadDirReply; + // send the reply + reply->error = result; + reply->count = countRead; + return _SendReply(allocator, (result == B_OK)); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(RewindDirRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->RewindDir(request->node, request->dirCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + RewindDirReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(WalkRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + vnode_id vnid; + char* resolvedPath = NULL; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->Walk(request->node, + (const char*)request->entryName.GetData(), + (request->traverseLink ? &resolvedPath : NULL), &vnid); + } + MemoryDeleter _(resolvedPath); + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + WalkReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + if (result == B_OK && resolvedPath) + result = allocator.AllocateString(reply->resolvedPath, resolvedPath); + reply->vnid = vnid; + reply->error = result; + // send the reply + return _SendReply(allocator, (result == B_OK && resolvedPath)); +} + + +// #pragma mark - +// #pragma mark ----- attributes ----- + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(OpenAttrDirRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + void* attrDirCookie; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->OpenAttrDir(request->node, &attrDirCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + OpenAttrDirReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + reply->attrDirCookie = attrDirCookie; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(CloseAttrDirRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->CloseAttrDir(request->node, request->attrDirCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + CloseAttrDirReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(FreeAttrDirCookieRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->FreeAttrDirCookie(request->node, + request->attrDirCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + FreeAttrDirCookieReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(ReadAttrDirRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + void* node = request->node; + void* attrDirCookie = request->attrDirCookie; + size_t bufferSize = request->bufferSize; + int32 count = request->count; + // allocate the reply + RequestAllocator allocator(fPort->GetPort()); + ReadAttrDirReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + void* buffer; + if (result == B_OK) { + result = allocator.AllocateAddress(reply->buffer, bufferSize, 1, + &buffer, true); + } + // execute the request + int32 countRead; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->ReadAttrDir(node, attrDirCookie, buffer, bufferSize, + count, &countRead); + } + // reconstruct the reply, in case it has been overwritten + reply = new(reply) ReadAttrDirReply; + // send the reply + reply->error = result; + reply->count = countRead; + return _SendReply(allocator, (result == B_OK)); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(RewindAttrDirRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->RewindAttrDir(request->node, request->attrDirCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + RewindAttrDirReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(ReadAttrRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + void* node = request->node; + off_t pos = request->pos; + size_t size = request->size; + // allocate the reply + RequestAllocator allocator(fPort->GetPort()); + ReadAttrReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + void* buffer; + if (result == B_OK) { + result = allocator.AllocateAddress(reply->buffer, size, 1, &buffer, + true); + } + // execute the request + size_t bytesRead; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->ReadAttr(node, (const char*)request->name.GetData(), + request->type, pos, buffer, size, &bytesRead); + } + // reconstruct the reply, in case it has been overwritten + reply = new(reply) ReadAttrReply; + // send the reply + reply->error = result; + reply->bytesRead = bytesRead; + return _SendReply(allocator, (result == B_OK)); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(WriteAttrRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + size_t bytesWritten; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->WriteAttr(request->node, + (const char*)request->name.GetData(), request->type, request->pos, + request->buffer.GetData(), request->buffer.GetSize(), + &bytesWritten); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + WriteAttrReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + reply->bytesWritten = bytesWritten; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(RemoveAttrRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->RemoveAttr(request->node, + (const char*)request->name.GetData()); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + RemoveAttrReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(RenameAttrRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->RenameAttr(request->node, + (const char*)request->oldName.GetData(), + (const char*)request->newName.GetData()); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + RenameAttrReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(StatAttrRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + attr_info info; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->StatAttr(request->node, + (const char*)request->name.GetData(), &info); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + StatAttrReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + reply->info = info; + // send the reply + return _SendReply(allocator, false); +} + + +// #pragma mark - +// #pragma mark ----- indices ----- + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(OpenIndexDirRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + void* indexDirCookie; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->OpenIndexDir(&indexDirCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + OpenIndexDirReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + reply->indexDirCookie = indexDirCookie; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(CloseIndexDirRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->CloseIndexDir(request->indexDirCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + CloseIndexDirReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(FreeIndexDirCookieRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->FreeIndexDirCookie(request->indexDirCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + FreeIndexDirCookieReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(ReadIndexDirRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + void* indexDirCookie = request->indexDirCookie; + size_t bufferSize = request->bufferSize; + int32 count = request->count; + // allocate the reply + RequestAllocator allocator(fPort->GetPort()); + ReadIndexDirReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + void* buffer; + if (result == B_OK) { + result = allocator.AllocateAddress(reply->buffer, bufferSize, 1, + &buffer, true); + } + // execute the request + int32 countRead; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->ReadIndexDir(indexDirCookie, buffer, bufferSize, + count, &countRead); + } + // reconstruct the reply, in case it has been overwritten + reply = new(reply) ReadIndexDirReply; + // send the reply + reply->error = result; + reply->count = countRead; + return _SendReply(allocator, (result == B_OK)); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(RewindIndexDirRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->RewindIndexDir(request->indexDirCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + RewindIndexDirReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(CreateIndexRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->CreateIndex((const char*)request->name.GetData(), + request->type, request->flags); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + CreateIndexReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(RemoveIndexRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->RemoveIndex((const char*)request->name.GetData()); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + RemoveIndexReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(RenameIndexRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->RenameIndex((const char*)request->oldName.GetData(), + (const char*)request->newName.GetData()); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + RenameIndexReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(StatIndexRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + index_info info; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->StatIndex((const char*)request->name.GetData(), &info); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + StatIndexReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + reply->info = info; + // send the reply + return _SendReply(allocator, false); +} + + +// #pragma mark - +// #pragma mark ----- queries ----- + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(OpenQueryRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + void* queryCookie; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->OpenQuery((const char*)request->queryString.GetData(), + request->flags, request->port, request->token, &queryCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + OpenQueryReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + reply->queryCookie = queryCookie; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(CloseQueryRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->CloseQuery(request->queryCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + CloseQueryReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(FreeQueryCookieRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->FreeQueryCookie(request->queryCookie); + } + // prepare the reply + RequestAllocator allocator(fPort->GetPort()); + FreeQueryCookieReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + reply->error = result; + // send the reply + return _SendReply(allocator, false); +} + +// _HandleRequest +status_t +UserlandRequestHandler::_HandleRequest(ReadQueryRequest* request) +{ + // check and execute the request + status_t result = B_OK; + UserVolume* volume = (UserVolume*)request->volume; + if (!volume) + result = B_BAD_VALUE; + void* queryCookie = request->queryCookie; + size_t bufferSize = request->bufferSize; + int32 count = request->count; + // allocate the reply + RequestAllocator allocator(fPort->GetPort()); + ReadQueryReply* reply; + status_t error = AllocateRequest(allocator, &reply); + if (error != B_OK) + RETURN_ERROR(error); + void* buffer; + if (result == B_OK) { + result = allocator.AllocateAddress(reply->buffer, bufferSize, 1, + &buffer, true); + } + // execute the request + int32 countRead; + if (result == B_OK) { + RequestThreadContext context(volume); + result = volume->ReadQuery(queryCookie, buffer, bufferSize, + count, &countRead); + } + // reconstruct the reply, in case it has been overwritten + reply = new(reply) ReadQueryReply; + // send the reply + reply->error = result; + reply->count = countRead; + return _SendReply(allocator, (result == B_OK)); +} + + +// #pragma mark - +// #pragma mark ----- other ----- + +// _SendReply +status_t +UserlandRequestHandler::_SendReply(RequestAllocator& allocator, + bool expectsReceipt) +{ + if (expectsReceipt) { + SingleReplyRequestHandler handler(RECEIPT_ACK_REPLY); + return fPort->SendRequest(&allocator, &handler); + } else + return fPort->SendRequest(&allocator); +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/UserlandRequestHandler.h b/src/add-ons/kernel/file_systems/userlandfs/server/UserlandRequestHandler.h new file mode 100644 index 0000000000..2e2b70920e --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/UserlandRequestHandler.h @@ -0,0 +1,138 @@ +// UserlandRequestHandler.h + +#ifndef USERLAND_FS_USERLAND_REQUEST_HANDLER_H +#define USERLAND_FS_USERLAND_REQUEST_HANDLER_H + +#include "RequestHandler.h" + +namespace UserlandFSUtil { + +class MountVolumeRequest; +class UnmountVolumeRequest; +class ReadFSStatRequest; +class ReadVNodeRequest; +class WriteVNodeRequest; +class ReadStatRequest; +class AccessRequest; +class OpenRequest; +class CloseRequest; +class FreeCookieRequest; +class ReadRequest; +class WalkRequest; +class OpenDirRequest; +class ReadDirRequest; +class RewindDirRequest; +class CloseDirRequest; +class FreeDirCookieRequest; +class ReadLinkRequest; + +} // namespace UserlandFSUtil + +namespace UserlandFS { + +class UserFileSystem; + +class UserlandRequestHandler : public RequestHandler { +public: + UserlandRequestHandler( + UserFileSystem* fileSystem); + UserlandRequestHandler( + UserFileSystem* fileSystem, + uint32 expectedReply); + virtual ~UserlandRequestHandler(); + + virtual status_t HandleRequest(Request* request); + +private: + // FS + status_t _HandleRequest(MountVolumeRequest* request); + status_t _HandleRequest(UnmountVolumeRequest* request); + status_t _HandleRequest(SyncVolumeRequest* request); + status_t _HandleRequest(ReadFSStatRequest* request); + status_t _HandleRequest(WriteFSStatRequest* request); + + // vnodes + status_t _HandleRequest(ReadVNodeRequest* request); + status_t _HandleRequest(WriteVNodeRequest* request); + status_t _HandleRequest(FSRemoveVNodeRequest* request); + + // nodes + status_t _HandleRequest(FSyncRequest* request); + status_t _HandleRequest(ReadStatRequest* request); + status_t _HandleRequest(WriteStatRequest* request); + status_t _HandleRequest(AccessRequest* request); + + // files + status_t _HandleRequest(CreateRequest* request); + status_t _HandleRequest(OpenRequest* request); + status_t _HandleRequest(CloseRequest* request); + status_t _HandleRequest(FreeCookieRequest* request); + status_t _HandleRequest(ReadRequest* request); + status_t _HandleRequest(WriteRequest* request); + status_t _HandleRequest(IOCtlRequest* request); + status_t _HandleRequest(SetFlagsRequest* request); + status_t _HandleRequest(SelectRequest* request); + status_t _HandleRequest(DeselectRequest* request); + + // hard links / symlinks + status_t _HandleRequest(LinkRequest* request); + status_t _HandleRequest(UnlinkRequest* request); + status_t _HandleRequest(SymlinkRequest* request); + status_t _HandleRequest(ReadLinkRequest* request); + status_t _HandleRequest(RenameRequest* request); + + // directories + status_t _HandleRequest(MkDirRequest* request); + status_t _HandleRequest(RmDirRequest* request); + status_t _HandleRequest(OpenDirRequest* request); + status_t _HandleRequest(CloseDirRequest* request); + status_t _HandleRequest(FreeDirCookieRequest* request); + status_t _HandleRequest(ReadDirRequest* request); + status_t _HandleRequest(RewindDirRequest* request); + status_t _HandleRequest(WalkRequest* request); + + // attributes + status_t _HandleRequest(OpenAttrDirRequest* request); + status_t _HandleRequest(CloseAttrDirRequest* request); + status_t _HandleRequest( + FreeAttrDirCookieRequest* request); + status_t _HandleRequest(ReadAttrDirRequest* request); + status_t _HandleRequest(RewindAttrDirRequest* request); + status_t _HandleRequest(ReadAttrRequest* request); + status_t _HandleRequest(WriteAttrRequest* request); + status_t _HandleRequest(RemoveAttrRequest* request); + status_t _HandleRequest(RenameAttrRequest* request); + status_t _HandleRequest(StatAttrRequest* request); + + // indices + status_t _HandleRequest(OpenIndexDirRequest* request); + status_t _HandleRequest(CloseIndexDirRequest* request); + status_t _HandleRequest( + FreeIndexDirCookieRequest* request); + status_t _HandleRequest(ReadIndexDirRequest* request); + status_t _HandleRequest(RewindIndexDirRequest* request); + status_t _HandleRequest(CreateIndexRequest* request); + status_t _HandleRequest(RemoveIndexRequest* request); + status_t _HandleRequest(RenameIndexRequest* request); + status_t _HandleRequest(StatIndexRequest* request); + + // queries + status_t _HandleRequest(OpenQueryRequest* request); + status_t _HandleRequest(CloseQueryRequest* request); + status_t _HandleRequest(FreeQueryCookieRequest* request); + status_t _HandleRequest(ReadQueryRequest* request); + + status_t _SendReply(RequestAllocator& allocator, + bool expectsReceipt); + +private: + UserFileSystem* fFileSystem; + bool fExpectReply; + uint32 fExpectedReply; +}; + +} // namespace UserlandFS + +using UserlandFS::UserlandRequestHandler; + +#endif // USERLAND_FS_USERLAND_REQUEST_HANDLER_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/cache.c b/src/add-ons/kernel/file_systems/userlandfs/server/cache.c new file mode 100644 index 0000000000..a4d4fc3597 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/cache.c @@ -0,0 +1,2427 @@ +/* + This file contains the global device cache for the BeOS. All + file system I/O comes through here. The cache can handle blocks + of different sizes for multiple different underlying physical + devices. + + The cache is organized as a hash table (for lookups by device + and block number) and two doubly-linked lists. The normal + list is for "normal" blocks which are either clean or dirty. + The locked list is for blocks that are locked in the cache by + BFS. The lists are LRU ordered. + + Most of the work happens in the function cache_block_io() which + is quite lengthy. The other functions of interest are get_ents() + which picks victims to be kicked out of the cache; flush_ents() + which does the work of flushing them; and set_blocks_info() which + handles cloning blocks and setting callbacks so that the BFS + journal will work properly. If you want to modify this code it + will take some study but it's not too bad. Do not think about + separating the list of clean and dirty blocks into two lists as + I did that already and it's slower. + + Originally this cache code was written while listening to the album + "Ride the Lightning" by Metallica. The current version was written + while listening to "Welcome to SkyValley" by Kyuss as well as an + ambient collection on the German label FAX. Music helps a lot when + writing code. + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifdef __BEOS__ +#include +#include +#endif + +#include "compat.h" +#include "lock.h" +#include "cache.h" + + + +#ifndef USER +#define printf dprintf +#endif +#ifdef USER +#define kprintf printf +#endif + + +/* forward prototypes */ +static int flush_ents(cache_ent **ents, int n_ents); + +//static int do_dump(int argc, char **argv); +//static int do_find_block(int argc, char **argv); +//static int do_find_data(int argc, char **argv); +//static void cache_flusher(void *arg, int phase); + + +int chatty_io = 0; + +#define CHUNK (512 * 1024) /* a hack to work around scsi driver bugs */ + +size_t +read_phys_blocks(int fd, fs_off_t bnum, void *data, uint num_blocks, int bsize) +{ + size_t ret = 0; + size_t sum; + + if (chatty_io) + printf("R: %8Ld : %3d\n", bnum, num_blocks); + + if (num_blocks * bsize < CHUNK) + ret = read_pos(fd, bnum * bsize, data, num_blocks * bsize); + else { + for(sum=0; (sum + CHUNK) <= (num_blocks * bsize); sum += CHUNK) { + ret = read_pos(fd, (bnum * bsize) + sum, data, CHUNK); + if (ret != CHUNK) + break; + + data = (void *)((char *)data + CHUNK); + } + + if (ret == CHUNK && ((num_blocks * bsize) - sum) > 0) { + ret = read_pos(fd, (bnum * bsize) + sum, data, + (num_blocks * bsize) - sum); + + if (ret == (num_blocks * bsize) - sum) + ret = num_blocks * bsize; + } else if (ret == CHUNK) { + ret = num_blocks * bsize; + } + } + + if (ret == num_blocks * bsize) + return 0; + else + return EBADF; +} + +size_t +write_phys_blocks(int fd, fs_off_t bnum, void *data, uint num_blocks, int bsize) +{ + size_t ret = 0; + size_t sum; + + if (chatty_io) + printf("W: %8Ld : %3d\n", bnum, num_blocks); + + if (num_blocks * bsize < CHUNK) + ret = write_pos(fd, bnum * bsize, data, num_blocks * bsize); + else { + for(sum=0; (sum + CHUNK) <= (num_blocks * bsize); sum += CHUNK) { + ret = write_pos(fd, (bnum * bsize) + sum, data, CHUNK); + if (ret != CHUNK) + break; + + data = (void *)((char *)data + CHUNK); + } + + if (ret == CHUNK && ((num_blocks * bsize) - sum) > 0) { + ret = write_pos(fd, (bnum * bsize) + sum, data, + (num_blocks * bsize) - sum); + + if (ret == (num_blocks * bsize) - sum) + ret = num_blocks * bsize; + } else if (ret == CHUNK) { + ret = num_blocks * bsize; + } + } + + + if (ret == num_blocks * bsize) + return 0; + else + return EBADF; +} + +// #pragma mark - + +static int +init_hash_table(hash_table *ht) +{ + ht->max = HT_DEFAULT_MAX; + ht->mask = ht->max - 1; + ht->num_elements = 0; + + ht->table = (hash_ent **)calloc(ht->max, sizeof(hash_ent *)); + if (ht->table == NULL) + return ENOMEM; + + return 0; +} + + +static void +shutdown_hash_table(hash_table *ht) +{ + int i, hash_len; + hash_ent *he, *next; + + for(i=0; i < ht->max; i++) { + he = ht->table[i]; + + for(hash_len=0; he; hash_len++, he=next) { + next = he->next; + free(he); + } + } + + if (ht->table) + free(ht->table); + ht->table = NULL; +} + +#if 0 +static void +print_hash_stats(hash_table *ht) +{ + int i, hash_len, max = -1, sum = 0; + hash_ent *he, *next; + + for(i=0; i < ht->max; i++) { + he = ht->table[i]; + + for(hash_len=0; he; hash_len++, he=next) { + next = he->next; + } + if (hash_len) + printf("bucket %3d : %3d\n", i, hash_len); + + sum += hash_len; + if (hash_len > max) + max = hash_len; + } + + printf("max # of chains: %d, average chain length %d\n", max,sum/ht->max); +} +#endif + +#define HASH(d, b) ((((fs_off_t)d) << (sizeof(fs_off_t)*8 - 6)) | (b)) + +static hash_ent * +new_hash_ent(int dev, fs_off_t bnum, void *data) +{ + hash_ent *he; + + he = (hash_ent *)malloc(sizeof(*he)); + if (he == NULL) + return NULL; + + he->hash_val = HASH(dev, bnum); + he->dev = dev; + he->bnum = bnum; + he->data = data; + he->next = NULL; + + return he; +} + + +static int +grow_hash_table(hash_table *ht) +{ + int i, omax, newsize, newmask; + fs_off_t hash; + hash_ent **new_table, *he, *next; + + if (ht->max & ht->mask) { + printf("*** hashtable size %d or mask %d looks weird!\n", ht->max, + ht->mask); + } + + omax = ht->max; + newsize = omax * 2; /* have to grow in powers of two */ + newmask = newsize - 1; + + new_table = (hash_ent **)calloc(newsize, sizeof(hash_ent *)); + if (new_table == NULL) + return ENOMEM; + + for(i=0; i < omax; i++) { + for(he=ht->table[i]; he; he=next) { + hash = he->hash_val & newmask; + next = he->next; + + he->next = new_table[hash]; + new_table[hash] = he; + } + } + + free(ht->table); + ht->table = new_table; + ht->max = newsize; + ht->mask = newmask; + + return 0; +} + + + + +static int +hash_insert(hash_table *ht, int dev, fs_off_t bnum, void *data) +{ + fs_off_t hash; + hash_ent *he, *curr; + + hash = HASH(dev, bnum) & ht->mask; + + curr = ht->table[hash]; + for(; curr != NULL; curr=curr->next) + if (curr->dev == dev && curr->bnum == bnum) + break; + + if (curr && curr->dev == dev && curr->bnum == bnum) { + printf("entry %d:%Ld already in the hash table!\n", dev, bnum); + return EEXIST; + } + + he = new_hash_ent(dev, bnum, data); + if (he == NULL) + return ENOMEM; + + he->next = ht->table[hash]; + ht->table[hash] = he; + + ht->num_elements++; + if (ht->num_elements >= ((ht->max * 3) / 4)) { + if (grow_hash_table(ht) != 0) + return ENOMEM; + } + + return 0; +} + +static void * +hash_lookup(hash_table *ht, int dev, fs_off_t bnum) +{ + hash_ent *he; + + he = ht->table[HASH(dev, bnum) & ht->mask]; + + for(; he != NULL; he=he->next) { + if (he->dev == dev && he->bnum == bnum) + break; + } + + if (he) + return he->data; + else + return NULL; +} + + +static void * +hash_delete(hash_table *ht, int dev, fs_off_t bnum) +{ + void *data; + fs_off_t hash; + hash_ent *he, *prev = NULL; + + hash = HASH(dev, bnum) & ht->mask; + he = ht->table[hash]; + + for(; he != NULL; prev=he,he=he->next) { + if (he->dev == dev && he->bnum == bnum) + break; + } + + if (he == NULL) { + printf("*** hash_delete: tried to delete non-existent block %d:%Ld\n", + dev, bnum); + return NULL; + } + + data = he->data; + + if (ht->table[hash] == he) + ht->table[hash] = he->next; + else if (prev) + prev->next = he->next; + else + panic("hash table is inconsistent\n"); + + free(he); + ht->num_elements--; + + return data; +} + +// #pragma mark - + +/* + These are the global variables for the cache. +*/ +static block_cache bc; + +#define MAX_IOVECS 64 /* # of iovecs for use by cache code */ +static lock iovec_lock; +static struct iovec *iovec_pool[MAX_IOVECS]; /* each ptr is to an array of iovecs */ +static int iovec_used[MAX_IOVECS]; /* non-zero == iovec is in use */ + +#define NUM_FLUSH_BLOCKS 64 /* size of the iovec array pointed by each ptr */ + + +#define DEFAULT_READ_AHEAD_SIZE (32 * 1024) +static int read_ahead_size = DEFAULT_READ_AHEAD_SIZE; + +/* this array stores the size of each device so we can error check requests */ +#define MAX_DEVICES 256 +fs_off_t max_device_blocks[MAX_DEVICES]; + + +/* has the time of the last cache access so cache flushing doesn't interfere */ +static bigtime_t last_cache_access = 0; + + +int +init_block_cache(int max_blocks, int flags) +{ + memset(&bc, 0, sizeof(bc)); + memset(iovec_pool, 0, sizeof(iovec_pool)); + memset(iovec_used, 0, sizeof(iovec_used)); + memset(&max_device_blocks, 0, sizeof(max_device_blocks)); + + if (init_hash_table(&bc.ht) != 0) + return ENOMEM; + + bc.lock.s = iovec_lock.s = -1; + + bc.max_blocks = max_blocks; + bc.flags = flags; + if (new_lock(&bc.lock, "bollockcache") != 0) + goto err; + + if (new_lock(&iovec_lock, "iovec_lock") != 0) + goto err; + + /* allocate two of these up front so vm won't accidently re-enter itself */ + iovec_pool[0] = (struct iovec *)malloc(sizeof(struct iovec)*NUM_FLUSH_BLOCKS); + iovec_pool[1] = (struct iovec *)malloc(sizeof(struct iovec)*NUM_FLUSH_BLOCKS); + +#ifndef USER +#ifdef DEBUG + add_debugger_command("bcache", do_dump, "dump the block cache list"); + add_debugger_command("fblock", do_find_block, "find a block in the cache"); + add_debugger_command("fdata", do_find_data, "find a data block ptr in the cache"); +#endif + register_kernel_daemon(cache_flusher, NULL, 3); +#endif + + return 0; + + err: + if (bc.lock.s >= 0) + free_lock(&bc.lock); + + if (iovec_lock.s >= 0) + free_lock(&iovec_lock); + + shutdown_hash_table(&bc.ht); + memset((void *)&bc, 0, sizeof(bc)); + return ENOMEM; +} + + +static struct iovec * +get_iovec_array(void) +{ + int i; + struct iovec *iov; + + LOCK(iovec_lock); + + for(i=0; i < MAX_IOVECS; i++) { + if (iovec_used[i] == 0) + break; + } + + if (i >= MAX_IOVECS) /* uh-oh */ + panic("cache: ran out of iovecs (pool 0x%x, used 0x%x)!\n", + &iovec_pool[0], &iovec_used[0]); + + if (iovec_pool[i] == NULL) { + iovec_pool[i] = (struct iovec *)malloc(sizeof(struct iovec)*NUM_FLUSH_BLOCKS); + if (iovec_pool == NULL) + panic("can't allocate an iovec!\n"); + } + + iov = iovec_pool[i]; + iovec_used[i] = 1; + + UNLOCK(iovec_lock); + + return iov; +} + + +static void +release_iovec_array(struct iovec *iov) +{ + int i; + + LOCK(iovec_lock); + + for(i=0; i < MAX_IOVECS; i++) { + if (iov == iovec_pool[i]) + break; + } + + if (i < MAX_IOVECS) + iovec_used[i] = 0; + else /* uh-oh */ + printf("cache: released an iovec I don't own (iov %p)\n", iov); + + + UNLOCK(iovec_lock); +} + + + + +static void +real_dump_cache_list(cache_ent_list *cel) +{ + cache_ent *ce; + + kprintf("starting from LRU end:\n"); + + for (ce = cel->lru; ce; ce = ce->next) { + kprintf("ce %p dev %2d bnum %6Ld lock %d flag %d arg %p " + "clone %p\n", ce, ce->dev, ce->block_num, ce->lock, + ce->flags, ce->arg, ce->clone); + } + kprintf("MRU end\n"); +} + +static void +dump_cache_list(void) +{ + kprintf("NORMAL BLOCKS\n"); + real_dump_cache_list(&bc.normal); + + kprintf("LOCKED BLOCKS\n"); + real_dump_cache_list(&bc.locked); + + kprintf("cur blocks %d, max blocks %d ht @ %p\n", bc.cur_blocks, + bc.max_blocks, &bc.ht); +} + +#if 0 +static void +check_bcache(char *str) +{ + int count = 0; + cache_ent *ce, *prev = NULL; + + LOCK(bc.lock); + + for(ce=bc.normal.lru; ce; prev=ce, ce=ce->next) { + count++; + } + + for(ce=bc.locked.lru; ce; prev=ce, ce=ce->next) { + count++; + } + + if (count != bc.cur_blocks) { + if (count < bc.cur_blocks - 16) + panic("%s: count == %d, cur_blocks %d, prev 0x%x\n", + str, count, bc.cur_blocks, prev); + else + printf("%s: count == %d, cur_blocks %d, prev 0x%x\n", + str, count, bc.cur_blocks, prev); + } + + UNLOCK(bc.lock); +} + + +static void +dump_lists(void) +{ + cache_ent *nce; + + printf("LOCKED 0x%x (tail 0x%x, head 0x%x)\n", &bc.locked, + bc.locked.lru, bc.locked.mru); + for(nce=bc.locked.lru; nce; nce=nce->next) + printf("nce @ 0x%x dev %d bnum %ld flags %d lock %d clone 0x%x func 0x%x\n", + nce, nce->dev, nce->block_num, nce->flags, nce->lock, nce->clone, + nce->func); + + printf("NORMAL 0x%x (tail 0x%x, head 0x%x)\n", &bc.normal, + bc.normal.lru, bc.normal.mru); + for(nce=bc.normal.lru; nce; nce=nce->next) + printf("nce @ 0x%x dev %d bnum %ld flags %d lock %d clone 0x%x func 0x%x\n", + nce, nce->dev, nce->block_num, nce->flags, nce->lock, nce->clone, + nce->func); +} + + +static void +check_lists(void) +{ + cache_ent *ce, *prev, *oce; + cache_ent_list *cel; + + cel = &bc.normal; + for(ce=cel->lru,prev=NULL; ce; prev=ce, ce=ce->next) { + for(oce=bc.locked.lru; oce; oce=oce->next) { + if (oce == ce) { + dump_lists(); + panic("1:ce @ 0x%x is in two lists(cel 0x%x &LOCKED)\n",ce,cel); + } + } + } + if (prev && prev != cel->mru) { + dump_lists(); + panic("*** last element in list != cel mru (ce 0x%x, cel 0x%x)\n", + prev, cel); + } + + cel = &bc.locked; + for(ce=cel->lru,prev=NULL; ce; prev=ce, ce=ce->next) { + for(oce=bc.normal.lru; oce; oce=oce->next) { + if (oce == ce) { + dump_lists(); + panic("3:ce @ 0x%x is in two lists(cel 0x%x & DIRTY)\n",ce,cel); + } + } + } + if (prev && prev != cel->mru) { + dump_lists(); + panic("*** last element in list != cel mru (ce 0x%x, cel 0x%x)\n", + prev, cel); + } +} +#endif + + +#ifdef DEBUG +#if 0 +static int +do_dump(int argc, char **argv) +{ + dump_cache_list(); + return 1; +} + + +static int +do_find_block(int argc, char **argv) +{ + int i; + fs_off_t bnum; + cache_ent *ce; + + if (argc < 2) { + kprintf("%s: needs a block # argument\n", argv[0]); + return 1; + } + + for(i=1; i < argc; i++) { + bnum = strtoul(argv[i], NULL, 0); + + for(ce=bc.normal.lru; ce; ce=ce->next) { + if (ce->block_num == bnum) { + kprintf("found clean bnum %ld @ 0x%lx (data @ 0x%lx)\n", + bnum, ce, ce->data); + } + } + + for(ce=bc.locked.lru; ce; ce=ce->next) { + if (ce->block_num == bnum) { + kprintf("found locked bnum %ld @ 0x%lx (data @ 0x%lx)\n", + bnum, ce, ce->data); + } + } + } + + return 0; +} + +static int +do_find_data(int argc, char **argv) +{ + int i; + void *data; + cache_ent *ce; + + if (argc < 2) { + kprintf("%s: needs a block # argument\n", argv[0]); + return 1; + } + + for(i=1; i < argc; i++) { + data = (void *)strtoul(argv[i], NULL, 0); + + for(ce=bc.normal.lru; ce; ce=ce->next) { + if (ce->data == data) { + kprintf("found normal data ptr for bnum %ld @ ce 0x%lx\n", + ce->block_num, ce); + } + } + + for(ce=bc.locked.lru; ce; ce=ce->next) { + if (ce->data == data) { + kprintf("found locked data ptr for bnum %ld @ ce 0x%lx\n", + ce->block_num, ce); + } + } + } + + return 0; +} +#endif +#endif /* DEBUG */ + + + +/* + this function detaches the cache_ent from the list. +*/ +static void +delete_from_list(cache_ent_list *cel, cache_ent *ce) +{ + if (ce->next) + ce->next->prev = ce->prev; + if (ce->prev) + ce->prev->next = ce->next; + + if (cel->lru == ce) + cel->lru = ce->next; + if (cel->mru == ce) + cel->mru = ce->prev; + + ce->next = NULL; + ce->prev = NULL; +} + + + +/* + this function adds the cache_ent ce to the head of the + list (i.e. the MRU end). the cache_ent should *not* + be in any lists. +*/ +static void +add_to_head(cache_ent_list *cel, cache_ent *ce) +{ +if (ce->next != NULL || ce->prev != NULL) { + panic("*** ath: ce has non-null next/prev ptr (ce 0x%x nxt 0x%x, prv 0x%x)\n", + ce, ce->next, ce->prev); +} + + ce->next = NULL; + ce->prev = cel->mru; + + if (cel->mru) + cel->mru->next = ce; + cel->mru = ce; + + if (cel->lru == NULL) + cel->lru = ce; +} + + +/* + this function adds the cache_ent ce to the tail of the + list (i.e. the MRU end). the cache_ent should *not* + be in any lists. +*/ +static void +add_to_tail(cache_ent_list *cel, cache_ent *ce) +{ +if (ce->next != NULL || ce->prev != NULL) { + panic("*** att: ce has non-null next/prev ptr (ce 0x%x nxt 0x%x, prv 0x%x)\n", + ce, ce->next, ce->prev); +} + + ce->next = cel->lru; + ce->prev = NULL; + + if (cel->lru) + cel->lru->prev = ce; + cel->lru = ce; + + if (cel->mru == NULL) + cel->mru = ce; +} + + +static int +cache_ent_cmp(const void *a, const void *b) +{ + fs_off_t diff; + cache_ent *p1 = *(cache_ent **)a, *p2 = *(cache_ent **)b; + + if (p1 == NULL || p2 == NULL) + panic("cache_ent pointers are null?!? (a 0x%lx, b 0x%lx\n)\n", a, b); + + if (p1->dev == p2->dev) { + diff = p1->block_num - p2->block_num; + return (int)diff; + } else { + return p1->dev - p2->dev; + } +} + +#if 0 +// ToDo: add this to the fsh (as a background thread)? +static void +cache_flusher(void *arg, int phase) +{ + int i, num_ents, err; + bigtime_t now = system_time(); + static cache_ent *ce = NULL; + static cache_ent *ents[NUM_FLUSH_BLOCKS]; + + /* + if someone else was in the cache recently then just bail out so + we don't lock them out unnecessarily + */ + if ((now - last_cache_access) < 1000000) + return; + + LOCK(bc.lock); + + ce = bc.normal.lru; + + for(num_ents=0; ce && num_ents < NUM_FLUSH_BLOCKS; ce=ce->next) { + if (ce->flags & CE_BUSY) + continue; + + if ((ce->flags & CE_DIRTY) == 0 && ce->clone == NULL) + continue; + + ents[num_ents] = ce; + ents[num_ents]->flags |= CE_BUSY; + num_ents++; + } + + /* if we've got some room left over, look for cloned locked blocks */ + if (num_ents < NUM_FLUSH_BLOCKS) { + ce = bc.locked.lru; + + for(; num_ents < NUM_FLUSH_BLOCKS;) { + for(; + ce && ((ce->flags & CE_BUSY) || ce->clone == NULL); + ce=ce->next) + /* skip ents that meet the above criteria */; + + if (ce == NULL) + break; + + ents[num_ents] = ce; + ents[num_ents]->flags |= CE_BUSY; + ce = ce->next; + num_ents++; + } + } + + UNLOCK(bc.lock); + + if (num_ents == 0) + return; + + qsort(ents, num_ents, sizeof(cache_ent **), cache_ent_cmp); + + if ((err = flush_ents(ents, num_ents)) != 0) { + printf("flush ents failed (ents @ 0x%lx, num_ents %d!\n", + (ulong)ents, num_ents); + } + + for(i=0; i < num_ents; i++) { /* clear the busy bit on each of ent */ + ents[i]->flags &= ~CE_BUSY; + } +} +#endif + + +static int +flush_cache_ent(cache_ent *ce) +{ + int ret = 0; + void *data; + + /* if true, then there's nothing to flush */ + if ((ce->flags & CE_DIRTY) == 0 && ce->clone == NULL) + return 0; + + /* same thing here */ + if (ce->clone == NULL && ce->lock != 0) + return 0; + + restart: + if (ce->clone) + data = ce->clone; + else + data = ce->data; + + if (chatty_io > 2) printf("flush: %7Ld\n", ce->block_num); + ret = write_phys_blocks(ce->dev, ce->block_num, data, 1, ce->bsize); + + if (ce->func) { + ce->func(ce->logged_bnum, 1, ce->arg); + ce->func = NULL; + } + + if (ce->clone) { + free(ce->clone); + ce->clone = NULL; + + if (ce->lock == 0 && (ce->flags & CE_DIRTY)) + goto restart; /* also write the real data ptr */ + } else { + ce->flags &= ~CE_DIRTY; + } + + return ret; +} + + +static int +flush_ents(cache_ent **ents, int n_ents) +{ + int i, j, k, ret = 0, bsize, iocnt, do_again = 0; + fs_off_t start_bnum; + struct iovec *iov; + + iov = get_iovec_array(); + if (iov == NULL) + return ENOMEM; + +restart: + for(i=0; i < n_ents; i++) { + /* if true, then there's nothing to flush */ + if ((ents[i]->flags & CE_DIRTY) == 0 && ents[i]->clone == NULL) + continue; + + /* if true we can't touch the dirty data yet because it's locked */ + if (ents[i]->clone == NULL && ents[i]->lock != 0) + continue; + + + bsize = ents[i]->bsize; + start_bnum = ents[i]->block_num; + + for(j=i+1; j < n_ents && (j - i) < NUM_FLUSH_BLOCKS; j++) { + if (ents[j]->dev != ents[i]->dev || + ents[j]->block_num != start_bnum + (j - i)) + break; + + if (ents[j]->clone == NULL && ents[j]->lock != 0) + break; + } + + if (j == i+1) { /* only one block, just flush it directly */ + if ((ret = flush_cache_ent(ents[i])) != 0) + break; + continue; + } + + + for(k=i,iocnt=0; k < j; k++,iocnt++) { + if (ents[k]->clone) + iov[iocnt].iov_base = ents[k]->clone; + else + iov[iocnt].iov_base = ents[k]->data; + + iov[iocnt].iov_len = bsize; + } + + if (chatty_io) + printf("writev @ %Ld for %d blocks\n", start_bnum, iocnt); + + ret = writev_pos(ents[i]->dev, start_bnum * (fs_off_t)bsize, + &iov[0], iocnt); + if (ret != iocnt*bsize) { + int idx; + + printf("flush_ents: writev failed: iocnt %d start bnum %Ld " + "bsize %d, ret %d\n", iocnt, start_bnum, bsize, ret); + + for(idx=0; idx < iocnt; idx++) + printf("iov[%2d] = %p :: %ld\n", idx, iov[idx].iov_base, + iov[idx].iov_len); + + printf("error %s writing blocks %Ld:%d (%d != %d)\n", + strerror(errno), start_bnum, iocnt, ret, iocnt*bsize); + ret = EINVAL; + break; + } + ret = 0; + + + for(k=i; k < j; k++) { + if (ents[k]->func) { + ents[k]->func(ents[k]->logged_bnum, 1, ents[k]->arg); + ents[k]->func = NULL; + } + + if (ents[k]->clone) { + free(ents[k]->clone); + ents[k]->clone = NULL; + } else { + ents[k]->flags &= ~CE_DIRTY; + } + } + + + i = j - 1; /* i gets incremented by the outer for loop */ + } + + /* + here we have to go back through and flush any blocks that are + still dirty. with an arched brow you astutely ask, "but how + could this happen given the above loop?" Ahhh young grasshopper + I say, the path through the cache is long and twisty and fraught + with peril. The reason it can happen is that a block can be both + cloned and dirty. The above loop would only flush the cloned half + of the data, not the main dirty block. So we have to go back + through and see if there are any blocks that are still dirty. If + there are we go back to the top of the function and do the whole + thing over. Kind of grody but it is necessary to insure the + correctness of the log for the Be file system. + */ + if (do_again == 0) { + for(i=0; i < n_ents; i++) { + if ((ents[i]->flags & CE_DIRTY) == 0 || ents[i]->lock) + continue; + + do_again = 1; + break; + } + + if (do_again) + goto restart; + } + + release_iovec_array(iov); + + + return ret; +} + +static void +delete_cache_list(cache_ent_list *cel) +{ + void *junk; + cache_ent *ce, *next; + + for (ce = cel->lru; ce; ce = next) { + next = ce->next; + if (ce->lock != 0) { + if (ce->func) + printf("*** shutdown_block_cache: block %Ld, lock == %d " + "(arg %p)!\n", ce->block_num, ce->lock, ce->arg); + else + printf("*** shutdown_block_cache: block %Ld, lock == %d!\n", + ce->block_num, ce->lock); + } + + if (ce->flags & CE_BUSY) { + printf("* shutdown block cache: bnum %Ld is busy? ce %p\n", + ce->block_num, ce); + } + + if ((ce->flags & CE_DIRTY) || ce->clone) { + flush_cache_ent(ce); + } + + if (ce->clone) + free(ce->clone); + ce->clone = NULL; + + if (ce->data) + free(ce->data); + ce->data = NULL; + + if ((junk = hash_delete(&bc.ht, ce->dev, ce->block_num)) != ce) { + printf("*** free_device_cache: bad hash table entry %Ld " + "%p != %p\n", ce->block_num, junk, ce); + } + + memset(ce, 0xfd, sizeof(*ce)); + free(ce); + + bc.cur_blocks--; + } +} + + +void +shutdown_block_cache(void) +{ + /* print_hash_stats(&bc.ht); */ + + if (bc.lock.s > 0) + LOCK(bc.lock); + +#ifndef USER + unregister_kernel_daemon(cache_flusher, NULL); +#endif + + delete_cache_list(&bc.normal); + delete_cache_list(&bc.locked); + + bc.normal.lru = bc.normal.mru = NULL; + bc.locked.lru = bc.locked.mru = NULL; + + shutdown_hash_table(&bc.ht); + + if (bc.lock.s > 0) + free_lock(&bc.lock); + bc.lock.s = -1; + + if (iovec_lock.s >= 0) + free_lock(&iovec_lock); +} + + + +int +init_cache_for_device(int fd, fs_off_t max_blocks) +{ + int ret = 0; + + if (fd >= MAX_DEVICES) + return -1; + + LOCK(bc.lock); + + if (max_device_blocks[fd] != 0) { + printf("device %d is already initialized!\n", fd); + ret = -1; + } else { + max_device_blocks[fd] = max_blocks; + } + + UNLOCK(bc.lock); + + return ret; +} + + + +/* + this routine assumes that bc.lock has been acquired +*/ +static cache_ent * +block_lookup(int dev, fs_off_t bnum) +{ + int count = 0; + cache_ent *ce; + + while (1) { + ce = hash_lookup(&bc.ht, dev, bnum); + if (ce == NULL) + return NULL; + + if ((ce->flags & CE_BUSY) == 0) /* it's ok, break out and return it */ + break; + + /* else, it's busy and we need to retry our lookup */ + UNLOCK(bc.lock); + + snooze(5000); + if (count++ == 5000) { /* then a lot of time has elapsed */ + printf("block %Ld isn't coming un-busy (ce @ %p)\n", + ce->block_num, ce); + } + + LOCK(bc.lock); + } + + if (ce->flags & CE_BUSY) + panic("block lookup: returning a busy block @ 0x%lx?!?\n",(ulong)ce); + + return ce; +} + + + +int +set_blocks_info(int dev, fs_off_t *blocks, int nblocks, + void (*func)(fs_off_t bnum, size_t nblocks, void *arg), void *arg) +{ + int i, j, cur; + cache_ent *ce; + cache_ent *ents[NUM_FLUSH_BLOCKS]; + + LOCK(bc.lock); + + + for(i=0, cur=0; i < nblocks; i++) { + + /* printf("sbi: %ld (arg 0x%x)\n", blocks[i], arg); */ + ce = block_lookup(dev, blocks[i]); + if (ce == NULL) { + panic("*** set_block_info can't find bnum %ld!\n", blocks[i]); + UNLOCK(bc.lock); + return ENOENT; /* hopefully this doesn't happen... */ + } + + + if (blocks[i] != ce->block_num || dev != ce->dev) { + UNLOCK(bc.lock); + panic("** error1: looked up dev %d block %ld but found dev %d " + "bnum %ld\n", dev, blocks[i], ce->dev, ce->block_num); + return EBADF; + } + + if (ce->lock == 0) { + panic("* set_block_info on bnum %ld (%d) but it's not locked!\n", + blocks[i], nblocks); + } + + + if ((ce->flags & CE_DIRTY) == 0) { + panic("*** set_block_info on non-dirty block bnum %ld (%d)!\n", + blocks[i], nblocks); + } + + ce->flags |= CE_BUSY; /* mark all blocks as busy till we're done */ + + /* if there is cloned data, it needs to be flushed now */ + if (ce->clone && ce->func) { + ents[cur++] = ce; + + if (cur >= NUM_FLUSH_BLOCKS) { + UNLOCK(bc.lock); + + qsort(ents, cur, sizeof(cache_ent **), cache_ent_cmp); + + flush_ents(ents, cur); + + LOCK(bc.lock); + for(j=0; j < cur; j++) + ents[j]->flags &= ~CE_BUSY; + cur = 0; + } + } + } + + + if (cur != 0) { + UNLOCK(bc.lock); + + qsort(ents, cur, sizeof(cache_ent **), cache_ent_cmp); + + flush_ents(ents, cur); + + LOCK(bc.lock); + for(j=0; j < cur; j++) + ents[j]->flags &= ~CE_BUSY; + cur = 0; + } + + + /* now go through and set the info that we were asked to */ + for (i = 0; i < nblocks; i++) { + /* we can call hash_lookup() here because we know it's around */ + ce = hash_lookup(&bc.ht, dev, blocks[i]); + if (ce == NULL) { + panic("*** set_block_info can't find bnum %Ld!\n", blocks[i]); + UNLOCK(bc.lock); + return ENOENT; /* hopefully this doesn't happen... */ + } + + ce->flags &= ~(CE_DIRTY | CE_BUSY); + + if (ce->func != NULL) { + panic("*** set_block_info non-null callback on bnum %Ld\n", + ce->block_num); + } + + if (ce->clone != NULL) { + panic("*** ce->clone == %p, not NULL in set_block_info\n", + ce->clone); + } + + ce->clone = (void *)malloc(ce->bsize); + if (ce->clone == NULL) + panic("*** can't clone bnum %Ld (bsize %d)\n", + ce->block_num, ce->bsize); + + + memcpy(ce->clone, ce->data, ce->bsize); + + ce->func = func; + ce->arg = arg; + + ce->logged_bnum = blocks[i]; + + ce->lock--; + if (ce->lock < 0) { + printf("sbi: whoa nellie! ce @ %p (%Ld) has lock == %d\n", + ce, ce->block_num, ce->lock); + } + + if (ce->lock == 0) { + delete_from_list(&bc.locked, ce); + add_to_head(&bc.normal, ce); + } + } + + UNLOCK(bc.lock); + + return 0; +} + + +/* this function is only for use by flush_device() */ +static void +do_flush(cache_ent **ents, int max) +{ + int i; + + for(i=0; i < max; i++) { + ents[i]->flags |= CE_BUSY; + } + + UNLOCK(bc.lock); + + qsort(ents, max, sizeof(cache_ent **), cache_ent_cmp); + flush_ents(ents, max); + + LOCK(bc.lock); + for(i=0; i < max; i++) { + ents[i]->flags &= ~CE_BUSY; + } +} + +int +flush_device(int dev, int warn_locked) +{ + int cur; + cache_ent *ce; + cache_ent *ents[NUM_FLUSH_BLOCKS]; + + LOCK(bc.lock); + + cur = 0; + ce = bc.normal.lru; + while (ce) { + if (ce->dev != dev || (ce->flags & CE_BUSY)) { + ce = ce->next; + continue; + } + + if ((ce->flags & CE_DIRTY) || ce->clone) { + ents[cur++] = ce; + if (cur >= NUM_FLUSH_BLOCKS) { + do_flush(ents, cur); + + ce = bc.normal.lru; + cur = 0; + continue; + } + } + + ce = ce->next; + } + + if (cur != 0) + do_flush(ents, cur); + + cur = 0; + ce = bc.locked.lru; + while (ce) { + if (ce->dev != dev || (ce->flags & CE_BUSY)) { + ce = ce->next; + continue; + } + + if (ce->clone) { + ents[cur++] = ce; + if (cur >= NUM_FLUSH_BLOCKS) { + do_flush(ents, cur); + + ce = bc.locked.lru; + cur = 0; + continue; + } + } + + ce = ce->next; + } + + if (cur != 0) + do_flush(ents, cur); + + UNLOCK(bc.lock); + + return 0; +} + + +static void +real_remove_cached_blocks(int dev, int allow_writes, cache_ent_list *cel) +{ + void *junk; + cache_ent *ce, *next = NULL; + + for(ce=cel->lru; ce; ce=next) { + next = ce->next; + + if (ce->dev != dev) { + continue; + } + + if (ce->lock != 0 || (ce->flags & CE_BUSY)) { + printf("*** remove_cached_dev: block %Ld has lock = %d, flags " + "0x%x! ce @ 0x%lx\n", ce->block_num, ce->lock, ce->flags, + (ulong)ce); + } + + if (allow_writes == ALLOW_WRITES && + ((ce->flags & CE_DIRTY) || ce->clone)) { + ce->flags |= CE_BUSY; + flush_cache_ent(ce); + ce->flags &= ~CE_BUSY; + } + + /* unlink this guy */ + if (cel->lru == ce) + cel->lru = ce->next; + + if (cel->mru == ce) + cel->mru = ce->prev; + + if (ce->prev) + ce->prev->next = ce->next; + if (ce->next) + ce->next->prev = ce->prev; + + if (ce->clone) + free(ce->clone); + ce->clone = NULL; + + if (ce->data) + free(ce->data); + ce->data = NULL; + + if ((junk = hash_delete(&bc.ht, ce->dev, ce->block_num)) != ce) { + panic("*** remove_cached_device: bad hash table entry %ld " + "0x%lx != 0x%lx\n", ce->block_num, (ulong)junk, (ulong)ce); + } + + free(ce); + + bc.cur_blocks--; + } + +} + +int +remove_cached_device_blocks(int dev, int allow_writes) +{ + LOCK(bc.lock); + + real_remove_cached_blocks(dev, allow_writes, &bc.normal); + real_remove_cached_blocks(dev, allow_writes, &bc.locked); + + max_device_blocks[dev] = 0; + + UNLOCK(bc.lock); + + return 0; +} + + + +int +flush_blocks(int dev, fs_off_t bnum, int nblocks) +{ + int cur, i; + cache_ent *ce; + cache_ent *ents[NUM_FLUSH_BLOCKS]; + + if (nblocks == 0) /* might as well check for this */ + return 0; + + LOCK(bc.lock); + + cur = 0; + for(; nblocks > 0; nblocks--, bnum++) { + ce = block_lookup(dev, bnum); + if (ce == NULL) + continue; + + if (bnum != ce->block_num || dev != ce->dev) { + UNLOCK(bc.lock); + panic("error2: looked up dev %d block %ld but found %d %ld\n", + dev, bnum, ce->dev, ce->block_num); + return EBADF; + } + + if ((ce->flags & CE_DIRTY) == 0 && ce->clone == NULL) + continue; + + ce->flags |= CE_BUSY; + ents[cur++] = ce; + + if (cur >= NUM_FLUSH_BLOCKS) { + UNLOCK(bc.lock); + + qsort(ents, cur, sizeof(cache_ent **), cache_ent_cmp); + flush_ents(ents, cur); + + LOCK(bc.lock); + for(i=0; i < cur; i++) { + ents[i]->flags &= ~CE_BUSY; + } + cur = 0; + } + } + + UNLOCK(bc.lock); + + if (cur == 0) /* nothing more to do */ + return 0; + + /* flush out the last few buggers */ + qsort(ents, cur, sizeof(cache_ent **), cache_ent_cmp); + flush_ents(ents, cur); + + for(i=0; i < cur; i++) { + ents[i]->flags &= ~CE_BUSY; + } + + return 0; +} + + +int +mark_blocks_dirty(int dev, fs_off_t bnum, int nblocks) +{ + int ret = 0; + cache_ent *ce; + + LOCK(bc.lock); + + while(nblocks > 0) { + ce = block_lookup(dev, bnum); + if (ce) { + ce->flags |= CE_DIRTY; + bnum += 1; + nblocks -= 1; + } else { /* hmmm, that's odd, didn't find it */ + printf("** mark_blocks_diry couldn't find block %Ld (len %d)\n", + bnum, nblocks); + ret = ENOENT; + break; + } + } + + UNLOCK(bc.lock); + + return ret; +} + + + +int +release_block(int dev, fs_off_t bnum) +{ + cache_ent *ce; + + /* printf("rlsb: %ld\n", bnum); */ + LOCK(bc.lock); + + ce = block_lookup(dev, bnum); + if (ce) { + if (bnum != ce->block_num || dev != ce->dev) { + panic("*** error3: looked up dev %d block %ld but found %d %ld\n", + dev, bnum, ce->dev, ce->block_num); + UNLOCK(bc.lock); + return EBADF; + } + + ce->lock--; + + if (ce->lock < 0) { + printf("rlsb: whoa nellie! ce %Ld has lock == %d\n", + ce->block_num, ce->lock); + } + + if (ce->lock == 0) { + delete_from_list(&bc.locked, ce); + add_to_head(&bc.normal, ce); + } + + } else { /* hmmm, that's odd, didn't find it */ + panic("** release_block asked to find %ld but it's not here\n", + bnum); + } + + UNLOCK(bc.lock); + + return 0; +} + + +static cache_ent * +new_cache_ent(int bsize) +{ + cache_ent *ce; + + ce = (cache_ent *)calloc(1, sizeof(cache_ent)); + if (ce == NULL) { + panic("*** error: cache can't allocate memory!\n"); + return NULL; + } + + ce->data = malloc(bsize); + if (ce->data == NULL) { + free(ce); + panic("** error cache can't allocate data memory\n"); + UNLOCK(bc.lock); + return NULL; + } + + ce->dev = -1; + ce->block_num = -1; + + return ce; +} + + +static void +get_ents(cache_ent **ents, int num_needed, int max, int *num_gotten, int bsize) +{ + int cur, retry_counter = 0, max_retry = num_needed * 256; + cache_ent *ce; + + if (num_needed > max) + panic("get_ents: num_needed %d but max %d (doh!)\n", num_needed, max); + + /* if the cache isn't full yet, just allocate the blocks */ + for(cur=0; bc.cur_blocks < bc.max_blocks && cur < num_needed; cur++) { + ents[cur] = new_cache_ent(bsize); + if (ents[cur] == NULL) + break; + bc.cur_blocks++; + } + + /* pluck off blocks from the LRU end of the normal list, keep trying too */ + while(cur < num_needed && retry_counter < max_retry) { + for(ce=bc.normal.lru; ce && cur < num_needed; ce=ce->next) { + if (ce->lock) + panic("get_ents: normal list has locked blocks (ce 0x%x)\n",ce); + + if (ce->flags & CE_BUSY) /* don't touch busy blocks */ + continue; + + ce->flags |= CE_BUSY; + ents[cur++] = ce; + } + + if (cur < num_needed) { + UNLOCK(bc.lock); + snooze(10000); + LOCK(bc.lock); + retry_counter++; + } + } + + if (cur < num_needed && retry_counter >= max_retry) { /* oh shit! */ + dump_cache_list(); + UNLOCK(bc.lock); + panic("get_ents: waited too long; can't get enough ce's (c %d n %d)\n", + cur, num_needed); + } + + /* + If the last block is a dirty one, try to get more of 'em so + that we can flush a bunch of blocks at once. + */ + if (cur && cur < max && + ((ents[cur-1]->flags & CE_DIRTY) || ents[cur-1]->clone)) { + + for(ce=ents[cur-1]->next; ce && cur < max; ce=ce->next) { + if (ce->flags & CE_BUSY) /* don't touch busy blocks */ + continue; + + if (ce->lock) + panic("get_ents:2 dirty list has locked blocks (ce 0x%x)\n",ce); + + ce->flags |= CE_BUSY; + ents[cur++] = ce; + } + } + + *num_gotten = cur; +} + + +static int +read_into_ents(int dev, fs_off_t bnum, cache_ent **ents, int num, int bsize) +{ + int i, ret; + struct iovec *iov; + + iov = get_iovec_array(); + + for (i = 0; i < num; i++) { + iov[i].iov_base = ents[i]->data; + iov[i].iov_len = bsize; + } + + if (chatty_io > 2) + printf("readv @ %Ld for %d blocks (at %Ld, block_size = %d)\n", bnum, num, bnum*bsize, bsize); + ret = readv_pos(dev, bnum*bsize, iov, num); + + release_iovec_array(iov); + + if (ret != num*bsize) { + printf("read_into_ents: asked to read %d bytes but got %d\n", + num*bsize, ret); + printf("*** iov @ %p (num %d)\n", iov, num); + return EINVAL; + } else + return 0; +} + + + +#define CACHE_READ 0x0001 +#define CACHE_WRITE 0x0002 +#define CACHE_NOOP 0x0004 /* for getting empty blocks */ +#define CACHE_LOCKED 0x0008 +#define CACHE_READ_AHEAD_OK 0x0010 /* it's ok to do read-ahead */ + + +static char * +op_to_str(int op) +{ + static char buff[128]; + + if (op & CACHE_READ) + strcpy(buff, "READ"); + else if (op & CACHE_WRITE) + strcpy(buff, "WRITE"); + else if (op & CACHE_NOOP) + strcpy(buff, "NOP"); + + if (op & CACHE_LOCKED) + strcat(buff, " LOCKED"); + + if (op & CACHE_READ_AHEAD_OK) + strcat(buff, " (AHEAD)"); + + return buff; +} + +static int +cache_block_io(int dev, fs_off_t bnum, void *data, fs_off_t num_blocks, int bsize, + int op, void **dataptr) +{ + size_t err = 0; + cache_ent *ce; + cache_ent_list *cel; + + if (chatty_io > 1) + printf("cbio: bnum = %Ld, num_blocks = %Ld, bsize = %d, op = %s\n", bnum, num_blocks, + bsize, op_to_str(op)); + + /* some sanity checks first */ + if (bsize == 0) + panic("cache_io: block size == 0 for bnum %Ld?!?\n", bnum); + + if (num_blocks == 0) + panic("cache_io: bnum %Ld has num_blocks == 0!\n", bnum); + + if (data == NULL && dataptr == NULL) { + printf("major butthead move: null data and dataptr! bnum %Ld:%Ld\n", + bnum, num_blocks); + return ENOMEM; + } + + if (data == NULL) { + if (num_blocks != 1) /* get_block() should never do that */ + panic("cache_io: num_blocks %Ld but should be 1\n", + num_blocks); + + if (op & CACHE_WRITE) + panic("cache_io: get_block() asked to write?!?\n"); + } + + if (bnum + num_blocks > max_device_blocks[dev]) { + printf("dev %d: access to blocks %Ld:%Ld but max_dev_blocks is %Ld\n", + dev, bnum, num_blocks, max_device_blocks[dev]); + + // let the app crash here + *(int *)0x3100 = 0xc0debabe; + return EINVAL; + } + + last_cache_access = system_time(); + + /* if the i/o is greater than 64k, do it directly */ + if (num_blocks * bsize >= 64 * 1024) { + char *ptr; + fs_off_t tmp; + + if (data == NULL || (op & CACHE_LOCKED)) { + panic("*** asked to do a large locked io that's too hard!\n"); + } + + + if (op & CACHE_READ) { + if (read_phys_blocks(dev, bnum, data, num_blocks, bsize) != 0) { + printf("cache read:read_phys_blocks failed (%s on blocks %Ld:%Ld)!\n", + strerror(errno), bnum, num_blocks); + return EINVAL; + } + + LOCK(bc.lock); + + /* if any of the blocks are in the cache, grab them instead */ + ptr = data; + for(tmp=bnum; tmp < bnum+num_blocks; tmp++, ptr+=bsize) { + ce = block_lookup(dev, tmp); + /* + if we find a block in the cache we have to copy its + data just in case it is more recent than what we just + read from disk (which could happen if someone wrote + these blocks after we did the read but before we locked + the cache and entered this loop). + */ + if (ce) { + if (tmp != ce->block_num || dev != ce->dev) { + UNLOCK(bc.lock); + panic("*** error4: looked up dev %d block %Ld but " + "found %d %Ld\n", dev, tmp, ce->dev, + ce->block_num); + } + + memcpy(ptr, ce->data, bsize); + } + } + + UNLOCK(bc.lock); + } else if (op & CACHE_WRITE) { + LOCK(bc.lock); + + /* if any of the blocks are in the cache, update them too */ + ptr = data; + for(tmp=bnum; tmp < bnum+num_blocks; tmp++, ptr+=bsize) { + ce = block_lookup(dev, tmp); + if (ce) { + if (tmp != ce->block_num || dev != ce->dev) { + UNLOCK(bc.lock); + panic("*** error5: looked up dev %d block %Ld but " + "found %d %Ld\n", dev, tmp, ce->dev, + ce->block_num); + return EBADF; + } + + /* XXXdbg -- this isn't strictly necessary */ + if (ce->clone) { + printf("over-writing cloned data (ce %p bnum %Ld)...\n", ce, tmp); + flush_cache_ent(ce); + } + + /* copy the data into the cache */ + memcpy(ce->data, ptr, bsize); + } + } + + UNLOCK(bc.lock); + + if (write_phys_blocks(dev, bnum, data, num_blocks, bsize) != 0) { + printf("cache write: write_phys_blocks failed (%s on blocks " + "%Ld:%Ld)!\n", strerror(errno), bnum, num_blocks); + return EINVAL; + } + } else { + printf("bad cache op %d (bnum %Ld nblocks %Ld)\n", op, bnum, num_blocks); + return EINVAL; + } + + return 0; + } + + + LOCK(bc.lock); + while(num_blocks) { + + ce = block_lookup(dev, bnum); + if (ce) { + if (bnum != ce->block_num || dev != ce->dev) { + UNLOCK(bc.lock); + panic("*** error6: looked up dev %d block %ld but found " + "%d %ld\n", dev, bnum, ce->dev, ce->block_num); + return EBADF; + } + + if (bsize != ce->bsize) { + panic("*** requested bsize %d but ce->bsize %d ce @ 0x%x\n", + bsize, ce->bsize, ce); + } + + /* delete this ent from the list it is in because it may change */ + if (ce->lock) + cel = &bc.locked; + else + cel = &bc.normal; + + delete_from_list(cel, ce); + + if (op & CACHE_READ) { + if (data && data != ce->data) { + memcpy(data, ce->data, bsize); + } else if (dataptr) { + *dataptr = ce->data; + } else { + printf("cbio:data %p dptr %p ce @ %p ce->data %p\n", + data, dataptr, ce, ce->data); + } + } else if (op & CACHE_WRITE) { + if (data && data != ce->data) + memcpy(ce->data, data, bsize); + + ce->flags |= CE_DIRTY; + } else if (op & CACHE_NOOP) { + memset(ce->data, 0, bsize); + if (data) + memset(data, 0, bsize); + + if (dataptr) + *dataptr = ce->data; + + ce->flags |= CE_DIRTY; + } else { + panic("cached_block_io: bogus op %d\n", op); + } + + if (op & CACHE_LOCKED) + ce->lock++; + + if (ce->lock) + cel = &bc.locked; + else + cel = &bc.normal; + + /* now put this ent at the head of the appropriate list */ + add_to_head(cel, ce); + + if (data != NULL) + data = (void *)((char *)data + bsize); + + bnum += 1; + num_blocks -= 1; + + continue; + } else { /* it's not in the cache */ + int cur, cur_nblocks, num_dirty, real_nblocks, num_needed; + cache_ent *ents[NUM_FLUSH_BLOCKS]; + + /* + here we find out how many additional blocks in this request + are not in the cache. the idea is that then we can do one + big i/o on that many blocks at once. + */ + for(cur_nblocks=1; + cur_nblocks < num_blocks && cur_nblocks < NUM_FLUSH_BLOCKS; + cur_nblocks++) { + + /* we can call hash_lookup() directly instead of + block_lookup() because we don't care about the + state of the busy bit of the block at this point + */ + if (hash_lookup(&bc.ht, dev, bnum + cur_nblocks)) + break; + } + + /* + here we try to figure out how many extra blocks we should read + for read-ahead. we want to read as many as possible that are + not already in the cache and that don't cause us to try and + read beyond the end of the disk. + */ + if ((op & CACHE_READ) && (op & CACHE_READ_AHEAD_OK) && + (cur_nblocks * bsize) < read_ahead_size) { + + for(num_needed=cur_nblocks; + num_needed < (read_ahead_size / bsize); + num_needed++) { + + if ((bnum + num_needed) >= max_device_blocks[dev]) + break; + + if (hash_lookup(&bc.ht, dev, bnum + num_needed)) + break; + } + } else { + num_needed = cur_nblocks; + } + + /* this will get us pointers to a bunch of cache_ents we can use */ + get_ents(ents, num_needed, NUM_FLUSH_BLOCKS, &real_nblocks, bsize); + + if (real_nblocks < num_needed) { + panic("don't have enough cache ents (need %d got %d %ld::%d)\n", + num_needed, real_nblocks, bnum, num_blocks); + } + + /* + There are now three variables used as limits within the ents + array. This is how they are related: + + cur_nblocks <= num_needed <= real_nblocks + + Ents from 0 to cur_nblocks-1 are going to be used to fulfill + this IO request. Ents from cur_nblocks to num_needed-1 are + for read-ahead. Ents from num_needed to real_nblocks are + extra blocks that get_ents() asked us to flush. Often (and + always on writes) cur_nblocks == num_needed. + + Below, we sort the list of ents so that when we flush them + they go out in order. + */ + + qsort(ents, real_nblocks, sizeof(cache_ent **), cache_ent_cmp); + + /* + delete each ent from its list because it will change. also + count up how many dirty blocks there are and insert into the + hash table any new blocks so that no one else will try to + read them in when we release the cache semaphore to do our I/O. + */ + for(cur=0,num_dirty=0; cur < real_nblocks; cur++) { + ce = ents[cur]; + ce->flags |= CE_BUSY; + + /* + insert the new block into the hash table with its new block + number. note that the block is still in the hash table for + its old block number -- and it has to be until we are done + flushing it from the cache (to prevent someone else from + sneaking in in front of us and trying to read the same + block that we're flushing). + */ + if (cur < num_needed) { + if (hash_insert(&bc.ht, dev, bnum + cur, ce) != 0) + panic("could not insert cache ent for %d %ld (0x%lx)\n", + dev, bnum + cur, (ulong)ents[cur]); + } + + if (ce->dev == -1) + continue; + + if ((ce->flags & CE_DIRTY) || ce->clone) + num_dirty++; + + if (ce->lock) + panic("cbio: can't use locked blocks here ce @ 0x%x\n",ce); + else + cel = &bc.normal; + + delete_from_list(cel, ce); + } + ce = NULL; + + + /* + we release the block cache semaphore here so that we can + go do all the i/o we need to do (flushing dirty blocks + that we're kicking out as well as reading any new data). + + because all the blocks we're touching are marked busy + no one else should mess with them while we're doing this. + */ + if (num_dirty || (op & CACHE_READ)) { + UNLOCK(bc.lock); + + /* this flushes any blocks we're kicking out that are dirty */ + if (num_dirty && (err = flush_ents(ents, real_nblocks)) != 0) { + printf("flush ents failed (ents @ 0x%lx, nblocks %d!\n", + (ulong)ents, cur_nblocks); + goto handle_err; + } + + } + + /* + now that everything is flushed to disk, go through and + make sure that the data blocks we're going to use are + the right block size for this current request (it's + possible we're kicking out some smaller blocks and need + to reallocate the data block pointer). We do this in two + steps, first free'ing everything and then going through + and doing the malloc's to try and be nice to the memory + system (i.e. allow it to coalesce stuff, etc). + */ + err = 0; + for(cur=0; cur < num_needed; cur++) { + if (ents[cur]->bsize != bsize) { + free(ents[cur]->data); + ents[cur]->data = NULL; + + if (ents[cur]->clone) { + free(ents[cur]->clone); + ents[cur]->clone = NULL; + } + } + } + + for(cur=0; cur < num_needed; cur++) { + if (ents[cur]->data == NULL) { + ents[cur]->data = (void *)malloc(bsize); + ents[cur]->bsize = bsize; + } + + if (ents[cur]->data == NULL) { + printf("cache: no memory for block (bsize %d)!\n", + bsize); + err = ENOMEM; + break; + } + } + + /* + if this condition is true it's a pretty serious error. + we'll try and back out gracefully but we're in pretty + deep at this point and it ain't going to be easy. + */ + handle_err: + if (err) { + for(cur=0; cur < num_needed; cur++) { + cache_ent *tmp_ce; + + tmp_ce = (cache_ent *)hash_delete(&bc.ht,dev,bnum+cur); + if (tmp_ce != ents[cur]) { + panic("hash_del0: %d %ld got 0x%lx, not 0x%lx\n", + dev, bnum+cur, (ulong)tmp_ce, + (ulong)ents[cur]); + } + + tmp_ce = (cache_ent *)hash_delete(&bc.ht,ents[cur]->dev, + ents[cur]->block_num); + if (tmp_ce != ents[cur]) { + panic("hash_del1: %d %ld got 0x%lx, not 0x%lx\n", + ents[cur]->dev, ents[cur]->block_num, (ulong)tmp_ce, + (ulong)ents[cur]); + } + + ents[cur]->flags &= ~CE_BUSY; + if (ents[cur]->data) + free(ents[cur]->data); + free(ents[cur]); + ents[cur] = NULL; + + bc.cur_blocks--; + } + + if (cur < real_nblocks) { + LOCK(bc.lock); + for(; cur < real_nblocks; cur++) { + ents[cur]->flags &= ~CE_BUSY; + + /* we have to put them back here */ + add_to_tail(&bc.normal, ents[cur]); + } + UNLOCK(bc.lock); + } + + return ENOMEM; + } + + + /* + If we go into this if statement, the block cache lock + has *already been released* up above when we flushed the + dirty entries. As always, since the blocks we're mucking + with are marked busy, they shouldn't get messed with. + */ + err = 0; + if (num_dirty || (op & CACHE_READ)) { + /* this section performs the i/o that we need to do */ + if (op & CACHE_READ) { + err = read_into_ents(dev, bnum, ents, num_needed, bsize); + } else { + err = 0; + } + + if (err != 0) { + printf("err %s on dev %d block %Ld:%d (%d) " + "data %p, ents[0] %p\n", + strerror(errno), dev, bnum, cur_nblocks, + bsize, data, ents[0]); + } + + /* + acquire the semaphore here so that we can go on mucking + with the cache data structures. We need to delete old + block numbers from the hash table and set the new block + number's for the blocks we just read in. We also put the + read-ahead blocks at the head of mru list. + */ + + LOCK(bc.lock); + } + + for(cur=0; cur < num_needed; cur++) { + cache_ent *tmp_ce; + + ce = ents[cur]; + if (ce->dev != -1) { + tmp_ce = hash_delete(&bc.ht, ce->dev, ce->block_num); + if (tmp_ce == NULL || tmp_ce != ce) { + panic("*** hash_delete failure (ce 0x%x tce 0x%x)\n", + ce, tmp_ce); + } + } + + if (err == 0 && cur >= cur_nblocks) { + ce->dev = dev; + ce->block_num = bnum + cur; + ce->flags &= ~CE_BUSY; + add_to_head(&bc.normal, ce); + } + } + ce = NULL; + + /* + clear the busy bit on the blocks we force-flushed and + put them on the normal list since they're now clean. + */ + for(; cur < real_nblocks; cur++) { + ents[cur]->flags &= ~CE_BUSY; + + if (ents[cur]->lock) + panic("should not have locked blocks here (ce 0x%x)\n", + ents[cur]); + + add_to_tail(&bc.normal, ents[cur]); + } + + if (err) { /* then we have some cleanup to do */ + for(cur=0; cur < num_needed; cur++) { + cache_ent *tmp_ce; + + /* we delete all blocks from the cache so we don't + leave partially written blocks in the cache */ + + tmp_ce = (cache_ent *)hash_delete(&bc.ht,dev,bnum+cur); + if (tmp_ce != ents[cur]) { + panic("hash_del: %d %ld got 0x%lx, not 0x%lx\n", + dev, bnum+cur, (ulong)tmp_ce, + (ulong)ents[cur]); + } + + ce = ents[cur]; + ce->flags &= ~CE_BUSY; + + free(ce->data); + ce->data = NULL; + + free(ce); + ents[cur] = NULL; + + bc.cur_blocks--; + } + ce = NULL; + + UNLOCK(bc.lock); + return err; + } + + + /* + last step: go through and make sure all the cache_ent + structures have the right data in them, delete old guys, etc. + */ + for(cur=0; cur < cur_nblocks; cur++) { + ce = ents[cur]; + + if (ce->dev != -1) { /* then clean this guy up */ + if (ce->next || ce->prev) + panic("ce @ 0x%x should not be in a list yet!\n", ce); + + if (ce->clone) + free(ce->clone); + + if (ce->data == NULL) + panic("ce @ 0x%lx has a null data ptr\n", (ulong)ce); + } + + ce->dev = dev; + ce->block_num = bnum + cur; + ce->bsize = bsize; + ce->flags = CE_NORMAL; + ce->lock = 0; + ce->clone = NULL; + ce->func = ce->arg = NULL; + ce->next = ce->prev = NULL; + + if (op & CACHE_READ) { + if (data) + memcpy(data, ce->data, bsize); + } else if (op & CACHE_WRITE) { + ce->flags |= CE_DIRTY; + memcpy(ce->data, data, bsize); + } else if (op & CACHE_NOOP) { + memset(ce->data, 0, bsize); + if (data) + memset(data, 0, bsize); + + ce->flags |= CE_DIRTY; + } + + if (op & CACHE_LOCKED) { + ce->lock++; + cel = &bc.locked; + } else { + cel = &bc.normal; + } + + /* now stick this puppy at the head of the mru list */ + add_to_head(cel, ce); + + + if (dataptr) { + *dataptr = ce->data; + } + + if (data != NULL) + data = (void *)((char *)data + bsize); + else if (cur_nblocks != 1) + panic("cache can't handle setting data_ptr twice!\n"); + } /* end of for(cur=0; cur < cur_nblocks; cur++) */ + + bnum += cur_nblocks; + num_blocks -= cur_nblocks; + + } /* end of else it's not in the cache */ + + } /* end of while(num_blocks) */ + + UNLOCK(bc.lock); + + return 0; +} + + +void * +get_block(int dev, fs_off_t bnum, int bsize) +{ + void *data; + + if (cache_block_io(dev, bnum, NULL, 1, bsize, CACHE_READ|CACHE_LOCKED|CACHE_READ_AHEAD_OK, + &data) != 0) + return NULL; + + return data; +} + +void * +get_empty_block(int dev, fs_off_t bnum, int bsize) +{ + void *data; + + if (cache_block_io(dev, bnum, NULL, 1, bsize, CACHE_NOOP|CACHE_LOCKED, + &data) != 0) + return NULL; + + return data; +} + +int +cached_read(int dev, fs_off_t bnum, void *data, fs_off_t num_blocks, int bsize) +{ + return cache_block_io(dev, bnum, data, num_blocks, bsize, + CACHE_READ | CACHE_READ_AHEAD_OK, NULL); +} + + +int +cached_write(int dev, fs_off_t bnum, const void *data, fs_off_t num_blocks,int bsize) +{ + return cache_block_io(dev, bnum, (void *)data, num_blocks, bsize, + CACHE_WRITE, NULL); +} + +int +cached_write_locked(int dev, fs_off_t bnum, const void *data, + fs_off_t num_blocks, int bsize) +{ + return cache_block_io(dev, bnum, (void *)data, num_blocks, bsize, + CACHE_WRITE | CACHE_LOCKED, NULL); +} + + +void +force_cache_flush(int dev, int prefer_log_blocks) +{ + int i, count = 0; + cache_ent *ce; + cache_ent *ents[NUM_FLUSH_BLOCKS]; + + + LOCK(bc.lock); + + for(ce=bc.normal.lru; ce; ce=ce->next) { + if ((ce->dev == dev) && + (ce->flags & CE_BUSY) == 0 && + ((ce->flags & CE_DIRTY) || ce->clone) && + ((prefer_log_blocks && ce->func) || (prefer_log_blocks == 0))) { + + ce->flags |= CE_BUSY; + ents[count++] = ce; + + if (count >= NUM_FLUSH_BLOCKS) { + break; + } + } + } + + /* if we've got some room left, try and grab any cloned blocks */ + if (count < NUM_FLUSH_BLOCKS) { + for(ce=bc.locked.lru; ce; ce=ce->next) { + if ((ce->dev == dev) && + (ce->flags & CE_BUSY) == 0 && + (ce->clone)) { + + ce->flags |= CE_BUSY; + ents[count++] = ce; + + if (count >= NUM_FLUSH_BLOCKS) { + break; + } + } + } + } + + UNLOCK(bc.lock); + + if (count != 0) { + qsort(ents, count, sizeof(cache_ent **), cache_ent_cmp); + flush_ents(ents, count); + + for(i=0; i < count; i++) + ents[i]->flags &= ~CE_BUSY; + } +} diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/compat.h b/src/add-ons/kernel/file_systems/userlandfs/server/compat.h new file mode 100644 index 0000000000..6135a01702 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/compat.h @@ -0,0 +1,204 @@ +/* + This file contains some kit-wide typedefs and structs that basically + emulate most of a normal posix-y type system. The purpose of hiding + everything behind these typedefs is to avoid inconsistencies between + various systems (such as the difference in size between off_t on BeOS + and some versions of Unix). To further avoid complications I've also + hidden the stat and dirent structs since those vary even more widely. + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com +*/ + + +#ifndef _COMPAT_H +#define _COMPAT_H + + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __BEOS__ +#include /* for typedefs and prototypes */ +#include /* for a few typedefs */ +#include /* for various ioctl structs, etc */ +#include /* because we're boneheads sometimes */ +#else +#include +#endif + + +/* + By default (for portability reasons) the size of off_t's and ino_t's + is 32-bit. You can change the file system to be 64-bit if you want + by defining OFF_T_SIZE to be 8. + + NOTE: if you change the size of OFF_T_SIZE to be 8 you will have to + go through the code and change any calls to printf() to use the + appropriate format for 64-bit integers on your OS. I have seen + 4 different formats now: %Ld (BeOS and Linux), %qd (FreeBSD), + %lld (Irix) and %I64d (NT). +*/ +#define OFF_T_SIZE 8 + +#if OFF_T_SIZE == 4 +typedef long fs_off_t; +typedef long my_ino_t; +#elif OFF_T_SIZE == 8 +typedef long long fs_off_t; +typedef long long my_ino_t; +#else +#error OFF_T_SIZE must be either 4 or 8. +#endif + +typedef int my_dev_t; +typedef int my_mode_t; +typedef int my_uid_t; +typedef int my_gid_t; + +/* This is the maximum length of a file name. Adjust it as you see fit */ +#define FILE_NAME_LENGTH 256 + +/* This is maximum name size for naming a volume or semaphore/lock */ +#define IDENT_NAME_LENGTH 32 + + +typedef struct my_dirent { + my_dev_t d_dev; + my_ino_t d_ino; + unsigned short d_reclen; + char d_name[1]; +} my_dirent_t; + +typedef struct { + int fd; + struct my_dirent ent; +} MY_DIR; + + +/* + This is a pretty regular stat structure but it's our "internal" + version since if we depended on the host version we'd be exposed + to all sorts of nasty things (different sized ino_t's, etc). + We also can't use the normal naming style of "st_" for each field + name because on some systems fields like st_atime are really just + define's that expand to all sorts of weird stuff. +*/ +struct my_stat { + my_dev_t dev; /* "device" that this file resides on */ + my_ino_t ino; /* this file's inode #, unique per device */ + my_mode_t mode; /* mode bits (rwx for user, group, etc) */ + int nlink; /* number of hard links to this file */ + my_uid_t uid; /* user id of the owner of this file */ + my_gid_t gid; /* group id of the owner of this file */ + fs_off_t size; /* size in bytes of this file */ + size_t blksize; /* preferred block size for i/o */ + time_t atime; /* last access time */ + time_t mtime; /* last modification time */ + time_t ctime; /* last change time, not creation time */ + time_t crtime; /* creation time; not posix but useful */ +}; + + +#define MY_S_IFMT 00000170000 /* type of file */ +#define MY_S_IFLNK 00000120000 /* symbolic link */ +#define MY_S_IFREG 00000100000 /* regular */ +#define MY_S_IFBLK 00000060000 /* block special */ +#define MY_S_IFDIR 00000040000 /* directory */ +#define MY_S_IFCHR 00000020000 /* character special */ +#define MY_S_IFIFO 00000010000 /* fifo */ + +#define MY_S_ISREG(m) (((m) & MY_S_IFMT) == MY_S_IFREG) +#define MY_S_ISLNK(m) (((m) & MY_S_IFMT) == MY_S_IFLNK) +#define MY_S_ISBLK(m) (((m) & MY_S_IFMT) == MY_S_IFBLK) +#define MY_S_ISDIR(m) (((m) & MY_S_IFMT) == MY_S_IFDIR) +#define MY_S_ISCHR(m) (((m) & MY_S_IFMT) == MY_S_IFCHR) +#define MY_S_ISFIFO(m) (((m) & MY_S_IFMT) == MY_S_IFIFO) + +#define MY_S_IUMSK 07777 /* user settable bits */ + +#define MY_S_ISUID 04000 /* set user id on execution */ +#define MY_S_ISGID 02000 /* set group id on execution */ + +#define MY_S_ISVTX 01000 /* save swapped text even after use */ + +#define MY_S_IRWXU 00700 /* read, write, execute: owner */ +#define MY_S_IRUSR 00400 /* read permission: owner */ +#define MY_S_IWUSR 00200 /* write permission: owner */ +#define MY_S_IXUSR 00100 /* execute permission: owner */ +#define MY_S_IRWXG 00070 /* read, write, execute: group */ +#define MY_S_IRGRP 00040 /* read permission: group */ +#define MY_S_IWGRP 00020 /* write permission: group */ +#define MY_S_IXGRP 00010 /* execute permission: group */ +#define MY_S_IRWXO 00007 /* read, write, execute: other */ +#define MY_S_IROTH 00004 /* read permission: other */ +#define MY_S_IWOTH 00002 /* write permission: other */ +#define MY_S_IXOTH 00001 /* execute permission: other */ + + + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef __BEOS__ +typedef long sem_id; +typedef unsigned char uchar; +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +#define ulong unsigned long /* make it a #define to avoid conflicts */ +typedef long long int64; +typedef unsigned long long uint64; +typedef unsigned int port_id; +typedef int bool; +typedef int image_id; +typedef long long bigtime_t; +typedef long thread_id; +typedef long status_t; + +sem_id create_sem(long count, const char *name); +long delete_sem(sem_id sem); +long acquire_sem(sem_id sem); +long acquire_sem_etc(sem_id sem, int count, int flags, + bigtime_t microsecond_timeout); +long release_sem(sem_id sem); +long release_sem_etc(sem_id sem, long count, long flags); + +long atomic_add(long *value, long addvalue); +int snooze(bigtime_t f); +bigtime_t system_time(void); +ssize_t read_pos(int fd, fs_off_t _pos, void *data, size_t nbytes); +ssize_t write_pos(int fd, fs_off_t _pos, const void *data, size_t nbytes); +ssize_t readv_pos(int fd, fs_off_t _pos, struct iovec *iov, int count); +ssize_t writev_pos(int fd, fs_off_t _pos, struct iovec *iov, int count); + + +#endif /* __BEOS__ */ + +void panic(const char *msg, ...); +int device_is_read_only(const char *device); +int get_device_block_size(int fd); +fs_off_t get_num_device_blocks(int fd); +int device_is_removeable(int fd); +int lock_removeable_device(int fd, bool on_or_off); +void hexdump(void *address, int size); + + +#endif /* _COMPAT_H */ diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/kernel_emu.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/kernel_emu.cpp new file mode 100644 index 0000000000..4045779094 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/kernel_emu.cpp @@ -0,0 +1,501 @@ +// kernel_emu.cpp + +#include +#include +#include + +#include +#include +#include + +#include "RequestPort.h" +#include "Requests.h" +#include "RequestThread.h" +#include "UserlandFSServer.h" +#include "UserlandRequestHandler.h" + +// Taken from the OBOS Storage Kit (storage_support.cpp) +/*! The length of the first component is returned as well as the index at + which the next one starts. These values are only valid, if the function + returns \c B_OK. + \param path the path to be parsed + \param length the variable the length of the first component is written + into + \param nextComponent the variable the index of the next component is + written into. \c 0 is returned, if there is no next component. + \return \c B_OK, if \a path is not \c NULL, \c B_BAD_VALUE otherwise +*/ +static +status_t +parse_first_path_component(const char *path, int32& length, + int32& nextComponent) +{ + status_t error = (path ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + int32 i = 0; + // find first '/' or end of name + for (; path[i] != '/' && path[i] != '\0'; i++); + // handle special case "/..." (absolute path) + if (i == 0 && path[i] != '\0') + i = 1; + length = i; + // find last '/' or end of name + for (; path[i] == '/' && path[i] != '\0'; i++); + if (path[i] == '\0') // this covers "" as well + nextComponent = 0; + else + nextComponent = i; + } + return error; +} + +// new_path +int +new_path(const char *path, char **copy) +{ + // check errors and special cases + if (!copy) + return B_BAD_VALUE; + if (!path) { + *copy = NULL; + return B_OK; + } + int32 len = strlen(path); + if (len < 1) + return B_ENTRY_NOT_FOUND; + bool appendDot = (path[len - 1] == '/'); + if (appendDot) + len++; + if (len >= B_PATH_NAME_LENGTH) + return B_NAME_TOO_LONG; + // check the path components + const char *remainder = path; + int32 length, nextComponent; + do { + status_t error + = parse_first_path_component(remainder, length, nextComponent); + if (error != B_OK) + return error; + if (length >= B_FILE_NAME_LENGTH) + error = B_NAME_TOO_LONG; + remainder += nextComponent; + } while (nextComponent != 0); + // clone the path + char *copiedPath = (char*)malloc(len + 1); + if (!copiedPath) + return B_NO_MEMORY; + strcpy(copiedPath, path); + // append a dot, if desired + if (appendDot) { + copiedPath[len] = '.'; + copiedPath[len] = '\0'; + } + *copy = copiedPath; + return B_OK; +} + +// free_path +void +free_path(char *p) +{ + free(p); +} + +// #pragma mark - + +// get_port_and_fs +static +status_t +get_port_and_fs(RequestPort** port, UserFileSystem** fileSystem) +{ + // get the request thread + RequestThread* thread = RequestThread::GetCurrentThread(); + if (thread) { + *port = thread->GetPort(); + *fileSystem = thread->GetFileSystem(); + } else { + *port = UserlandFSServer::GetNotificationRequestPort(); + *fileSystem = UserlandFSServer::GetFileSystem(); + if (!*port || !*fileSystem) + return B_BAD_VALUE; + } + return B_OK; +} + +// notify_listener +int +notify_listener(int op, nspace_id nsid, vnode_id vnida, vnode_id vnidb, + vnode_id vnidc, const char *name) +{ + // get the request port and the file system + RequestPort* port; + UserFileSystem* fileSystem; + status_t error = get_port_and_fs(&port, &fileSystem); + if (error != B_OK) + return error; + // prepare the request + RequestAllocator allocator(port->GetPort()); + NotifyListenerRequest* request; + error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->operation = op; + request->nsid = nsid; + request->vnida = vnida; + request->vnidb = vnidb; + request->vnidc = vnidc; + error = allocator.AllocateString(request->name, name); + if (error != B_OK) + return error; + // send the request + UserlandRequestHandler handler(fileSystem, NOTIFY_LISTENER_REPLY); + NotifyListenerReply* reply; + error = port->SendRequest(&allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// notify_select_event +void +notify_select_event(selectsync *sync, uint32 ref) +{ + // get the request port and the file system + RequestPort* port; + UserFileSystem* fileSystem; + status_t error = get_port_and_fs(&port, &fileSystem); + if (error != B_OK) + return; + // prepare the request + RequestAllocator allocator(port->GetPort()); + NotifySelectEventRequest* request; + error = AllocateRequest(allocator, &request); + if (error != B_OK) + return; + request->sync = sync; + request->ref = ref; + // send the request + UserlandRequestHandler handler(fileSystem, NOTIFY_SELECT_EVENT_REPLY); + NotifySelectEventReply* reply; + error = port->SendRequest(&allocator, &handler, (Request**)&reply); + if (error != B_OK) + return; + RequestReleaser requestReleaser(port, reply); + // process the reply: nothing to do +} + +// send_notification +int +send_notification(port_id targetPort, long token, ulong what, long op, + nspace_id nsida, nspace_id nsidb, vnode_id vnida, vnode_id vnidb, + vnode_id vnidc, const char *name) +{ + // get the request port and the file system + RequestPort* port; + UserFileSystem* fileSystem; + status_t error = get_port_and_fs(&port, &fileSystem); + if (error != B_OK) + return error; + // prepare the request + RequestAllocator allocator(port->GetPort()); + SendNotificationRequest* request; + error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->port = targetPort; + request->token = token; + request->what = what; + request->operation = op; + request->nsida = nsida; + request->nsidb = nsidb; + request->vnida = vnida; + request->vnidb = vnidb; + request->vnidc = vnidc; + error = allocator.AllocateString(request->name, name); + if (error != B_OK) + return error; + // send the request + UserlandRequestHandler handler(fileSystem, SEND_NOTIFICATION_REPLY); + SendNotificationReply* reply; + error = port->SendRequest(&allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// #pragma mark - + +// get_vnode +_EXPORT +int +get_vnode(nspace_id nsid, vnode_id vnid, void** data) +{ + // get the request port and the file system + RequestPort* port; + UserFileSystem* fileSystem; + status_t error = get_port_and_fs(&port, &fileSystem); + if (error != B_OK) + return error; + // prepare the request + RequestAllocator allocator(port->GetPort()); + GetVNodeRequest* request; + error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->nsid = nsid; + request->vnid = vnid; + // send the request + UserlandRequestHandler handler(fileSystem, GET_VNODE_REPLY); + GetVNodeReply* reply; + error = port->SendRequest(&allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + *data = reply->node; + return error; +} + +// put_vnode +_EXPORT +int +put_vnode(nspace_id nsid, vnode_id vnid) +{ + // get the request port and the file system + RequestPort* port; + UserFileSystem* fileSystem; + status_t error = get_port_and_fs(&port, &fileSystem); + if (error != B_OK) + return error; + // prepare the request + RequestAllocator allocator(port->GetPort()); + PutVNodeRequest* request; + error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->nsid = nsid; + request->vnid = vnid; + // send the request + UserlandRequestHandler handler(fileSystem, PUT_VNODE_REPLY); + PutVNodeReply* reply; + error = port->SendRequest(&allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// new_vnode +_EXPORT +int +new_vnode(nspace_id nsid, vnode_id vnid, void* data) +{ + // get the request port and the file system + RequestPort* port; + UserFileSystem* fileSystem; + status_t error = get_port_and_fs(&port, &fileSystem); + if (error != B_OK) + return error; + // prepare the request + RequestAllocator allocator(port->GetPort()); + NewVNodeRequest* request; + error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->nsid = nsid; + request->vnid = vnid; + request->node = data; + // send the request + UserlandRequestHandler handler(fileSystem, NEW_VNODE_REPLY); + NewVNodeReply* reply; + error = port->SendRequest(&allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// remove_vnode +_EXPORT +int +remove_vnode(nspace_id nsid, vnode_id vnid) +{ + // get the request port and the file system + RequestPort* port; + UserFileSystem* fileSystem; + status_t error = get_port_and_fs(&port, &fileSystem); + if (error != B_OK) + return error; + // prepare the request + RequestAllocator allocator(port->GetPort()); + RemoveVNodeRequest* request; + error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->nsid = nsid; + request->vnid = vnid; + // send the request + UserlandRequestHandler handler(fileSystem, REMOVE_VNODE_REPLY); + RemoveVNodeReply* reply; + error = port->SendRequest(&allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// unremove_vnode +_EXPORT +int +unremove_vnode(nspace_id nsid, vnode_id vnid) +{ + // get the request port and the file system + RequestPort* port; + UserFileSystem* fileSystem; + status_t error = get_port_and_fs(&port, &fileSystem); + if (error != B_OK) + return error; + // prepare the request + RequestAllocator allocator(port->GetPort()); + UnremoveVNodeRequest* request; + error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->nsid = nsid; + request->vnid = vnid; + // send the request + UserlandRequestHandler handler(fileSystem, UNREMOVE_VNODE_REPLY); + UnremoveVNodeReply* reply; + error = port->SendRequest(&allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return error; +} + +// is_vnode_removed +_EXPORT +int +is_vnode_removed(nspace_id nsid, vnode_id vnid) +{ + // get the request port and the file system + RequestPort* port; + UserFileSystem* fileSystem; + status_t error = get_port_and_fs(&port, &fileSystem); + if (error != B_OK) + return error; + // prepare the request + RequestAllocator allocator(port->GetPort()); + IsVNodeRemovedRequest* request; + error = AllocateRequest(allocator, &request); + if (error != B_OK) + return error; + request->nsid = nsid; + request->vnid = vnid; + // send the request + UserlandRequestHandler handler(fileSystem, IS_VNODE_REMOVED_REPLY); + IsVNodeRemovedReply* reply; + error = port->SendRequest(&allocator, &handler, (Request**)&reply); + if (error != B_OK) + return error; + RequestReleaser requestReleaser(port, reply); + // process the reply + if (reply->error != B_OK) + return reply->error; + return reply->result; +} + +// #pragma mark - + +// kernel_debugger +_EXPORT +void +kernel_debugger(const char *message) +{ + debugger(message); +} + +// panic +_EXPORT +void +panic(const char *format, ...) +{ + char buffer[1024]; + strcpy(buffer, "PANIC: "); + int32 prefixLen = strlen(buffer); + int bufferSize = sizeof(buffer) - prefixLen; + va_list args; + va_start(args, format); + // no vsnprintf() on PPC + #if defined(__INTEL__) + vsnprintf(buffer + prefixLen, bufferSize - 1, format, args); + #else + vsprintf(buffer + prefixLen, format, args); + #endif + va_end(args); + buffer[sizeof(buffer) - 1] = '\0'; + debugger(buffer); +} + +// parse_expression +_EXPORT +ulong +parse_expression(char *str) +{ + return 0; +} + +// add_debugger_command +_EXPORT +int +add_debugger_command(char *name, int (*func)(int argc, char **argv), + char *help) +{ + return B_OK; +} + +// remove_debugger_command +_EXPORT +int +remove_debugger_command(char *name, int (*func)(int argc, char **argv)) +{ + return B_OK; +} + +// kprintf +_EXPORT +void +kprintf(const char *format, ...) +{ +} + +// spawn_kernel_thread +thread_id +spawn_kernel_thread(thread_entry function, const char *threadName, + long priority, void *arg) +{ + return spawn_thread(function, threadName, priority, arg); +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/main.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/main.cpp new file mode 100644 index 0000000000..4a7a5db33d --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/main.cpp @@ -0,0 +1,113 @@ +// main.cpp + +#include +#include + +#include "Debug.h" +#include "ServerDefs.h" +#include "UserlandFSDispatcher.h" +#include "UserlandFSServer.h" + +// server signature +static const char* kServerSignature + = "application/x-vnd.bonefish.userlandfs-server"; + +// usage +static const char* kUsage = +"Usage: %s \n" +" %s \n" +"\n" +"The first version runs the server as the dispatcher, i.e. as the singleton\n" +"app the kernel add-on contacts when it is looking for a file system.\n" +"The dispatcher uses the second version to start a server for a specific file\n" +"system.\n" +"\n" +"Options:\n" +" --debug - the file system server enters the debugger after the\n" +" userland file system add-on has been loaded and is\n" +" ready to be used. If specified for the dispatcher, it\n" +" passes the flag to all file system servers it starts.\n" +" -h, --help - print this text\n" +; + +static int kArgC; +static char** kArgV; + +// print_usage +void +print_usage(bool toStdErr = true) +{ + fprintf((toStdErr ? stderr : stdout), kUsage, kArgV[0], kArgV[0]); +} + +// main +int +main(int argc, char** argv) +{ + kArgC = argc; + kArgV = argv; + // init debugging + init_debugging(); + struct DebuggingExiter { + DebuggingExiter() {} + ~DebuggingExiter() { exit_debugging(); } + } _; + // parse arguments + int argi = 1; + // parse options + for (; argi < argc; argi++) { + const char* arg = argv[argi]; + int32 argLen = strlen(arg); + if (argLen == 0) { + print_usage(); + return 1; + } + if (arg[0] != '-') + break; + if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) { + print_usage(false); + return 0; + } else if (strcmp(arg, "--debug") == 0) { + gServerSettings.SetEnterDebugger(true); + } + } + // get file system, if any + bool dispatcher = true; + const char* fileSystem = NULL; + if (argi < argc) { + fileSystem = argv[argi++]; + dispatcher = false; + } + if (argi < argc) { + print_usage(); + return 1; + } + // create and init the application + BApplication* app = NULL; + status_t error = B_OK; + if (dispatcher) { + UserlandFSDispatcher* dispatcher + = new(nothrow) UserlandFSDispatcher(kServerSignature); + if (!dispatcher) { + fprintf(stderr, "Failed to create dispatcher.\n"); + return 1; + } + error = dispatcher->Init(); + app = dispatcher; + } else { + UserlandFSServer* server + = new(nothrow) UserlandFSServer(kServerSignature); + if (!server) { + fprintf(stderr, "Failed to create server.\n"); + return 1; + } + error = server->Init(fileSystem); + app = server; + } + // run it, if everything went fine + if (error == B_OK) + app->Run(); + delete app; + return 0; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/sysdep.c b/src/add-ons/kernel/file_systems/userlandfs/server/sysdep.c new file mode 100644 index 0000000000..8362d7ef24 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/sysdep.c @@ -0,0 +1,350 @@ +/* + This file contains some routines that are #ifdef'ed based on what + system you're on. Currently it supports the BeOS and Unix. It + could be extended to support Windows NT but their posix support + is such a joke that it would probably be a real pain in the arse. + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com +*/ +#include +#include +#include +#include + +#include "compat.h" + + +int +device_is_read_only(const char *device) +{ +#ifdef unix + return 0; /* XXXdbg should do an ioctl or something */ +#else + int fd; + device_geometry dg; + + fd = open(device, O_RDONLY); + if (ioctl(fd, B_GET_GEOMETRY, &dg) < 0) + return 0; + + close(fd); + + return dg.read_only; +#endif +} + +int +get_device_block_size(int fd) +{ +#ifdef unix + return 512; /* XXXdbg should do an ioctl or something */ +#else + struct stat st; + device_geometry dg; + + if (ioctl(fd, B_GET_GEOMETRY, &dg) < 0) { + if (fstat(fd, &st) < 0 || S_ISDIR(st.st_mode)) + return 0; + + return 512; /* just assume it's a plain old file or something */ + } + + return dg.bytes_per_sector; +#endif +} + +fs_off_t +get_num_device_blocks(int fd) +{ +#ifdef unix + struct stat st; + + fstat(fd, &st); /* XXXdbg should be an ioctl or something */ + + return st.st_size / get_device_block_size(fd); +#else + struct stat st; + device_geometry dg; + + if (ioctl(fd, B_GET_GEOMETRY, &dg) >= 0) { + return (fs_off_t)dg.cylinder_count * + (fs_off_t)dg.sectors_per_track * + (fs_off_t)dg.head_count; + } + + /* if the ioctl fails, try just stat'ing in case it's a regular file */ + if (fstat(fd, &st) < 0) + return 0; + + return st.st_size / get_device_block_size(fd); +#endif +} + +int +device_is_removeable(int fd) +{ +#ifdef unix + return 0; /* XXXdbg should do an ioctl or something */ +#else + device_geometry dg; + + if (ioctl(fd, B_GET_GEOMETRY, &dg) < 0) { + return 0; + } + + return dg.removable; +#endif +} + +#if defined(__BEOS__) && !defined(USER) +#include "scsi.h" +#endif + +int +lock_removeable_device(int fd, bool on_or_off) +{ +#if defined(unix) || defined(USER) + return 0; /* XXXdbg should do an ioctl or something */ +#else + return ioctl(fd, B_SCSI_PREVENT_ALLOW, &on_or_off); +#endif +} + + + + +#ifndef __BEOS__ +ssize_t +read_pos(int fd, fs_off_t _pos, void *data, size_t nbytes) +{ + off_t pos = (off_t)_pos; + size_t ret; + + if (lseek(fd, pos, SEEK_SET) < 0) { + perror("read lseek"); + return EINVAL; + } + + ret = read(fd, data, nbytes); + + if (ret != nbytes) { + printf("read_pos: wanted %d, got %d\n", nbytes, ret); + return -1; + } + + return ret; +} + +ssize_t +write_pos(int fd, fs_off_t _pos, const void *data, size_t nbytes) +{ + off_t pos = (off_t)_pos; + size_t ret; + + if (lseek(fd, pos, SEEK_SET) < 0) { + perror("read lseek"); + return EINVAL; + } + + ret = write(fd, data, nbytes); + + if (ret != nbytes) { + printf("write_pos: wanted %d, got %d\n", nbytes, ret); + return -1; + } + + return ret; +} + + +#ifdef sun /* bloody wankers */ +#include +#ifdef DEF_IOV_MAX +#define MAX_IOV DEF_IOV_MAX +#else +#define MAX_IOV 16 +#endif +#else /* the rest of the world... */ +#define MAX_IOV 8192 /* something way bigger than we'll ever use */ +#endif + +ssize_t +readv_pos(int fd, fs_off_t _pos, struct iovec *iov, int count) +{ + off_t pos = (off_t)_pos; + size_t amt = 0; + ssize_t ret; + struct iovec *tmpiov; + int i, n; + + if (lseek(fd, pos, SEEK_SET) < 0) { + perror("read lseek"); + return EINVAL; + } + + i = 0; + tmpiov = iov; + while (i < count) { + if (i + MAX_IOV < count) + n = MAX_IOV; + else + n = (count - i); + + ret = readv(fd, tmpiov, n); + amt += ret; + + if (ret < 0) + break; + + i += n; + tmpiov += n; + } + + return amt; +} + +ssize_t +writev_pos(int fd, fs_off_t _pos, struct iovec *iov, int count) +{ + off_t pos = (off_t)_pos; + size_t amt = 0; + ssize_t ret; + struct iovec *tmpiov; + int i, n; + + if (lseek(fd, pos, SEEK_SET) < 0) { + perror("read lseek"); + return EINVAL; + } + + i = 0; + tmpiov = iov; + while (i < count) { + if (i + MAX_IOV < count) + n = MAX_IOV; + else + n = (count - i); + + ret = writev(fd, tmpiov, n); + amt += ret; + + if (ret < 0) + break; + + i += n; + tmpiov += n; + } + + return amt; +} + + + +#endif /* __BEOS__ */ + + +#include + + +#if 0 // bonefish +void +panic(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + + while (TRUE) + ; +} +#endif + + +#include "lock.h" + +int +new_lock(lock *l, const char *name) +{ + l->c = 1; + l->s = create_sem(0, (char *)name); + if (l->s <= 0) + return l->s; + return 0; +} + +int +free_lock(lock *l) +{ + delete_sem(l->s); + + return 0; +} + +int +new_mlock(mlock *l, long c, const char *name) +{ + l->s = create_sem(c, (char *)name); + if (l->s <= 0) + return l->s; + return 0; +} + +int +free_mlock(mlock *l) +{ + delete_sem(l->s); + + return 0; +} + + +#ifdef unix +#include + +bigtime_t +system_time(void) +{ + bigtime_t t; + struct timeval tv; + + gettimeofday(&tv, NULL); + + t = ((bigtime_t)tv.tv_sec * 1000000) + (bigtime_t)tv.tv_usec; + return t; +} + +/* + If you're compiler/system can't deal with the version of system_time() + as defined above, use this one instead +bigtime_t +system_time(void) +{ + return (bigtime_t)time(NULL); +} +*/ + +#endif /* unix */ + +#ifdef __BEOS__ +#include + +void +dprintf(const char *format, ...) +{ + va_list args; + + va_start(args, format); + vprintf(format, args); + va_end(args); +} + +#endif diff --git a/src/add-ons/kernel/file_systems/userlandfs/shared/Debug.cpp b/src/add-ons/kernel/file_systems/userlandfs/shared/Debug.cpp new file mode 100644 index 0000000000..77c0f320b8 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/shared/Debug.cpp @@ -0,0 +1,170 @@ +// Debug.cpp +// +// Copyright (c) 2003-2004, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#include +#include +#include +#include +#include +#include + +#include + +#include "Debug.h" + +/*! + \file Debug.cpp + \brief Defines debug output function with printf() signature printing + into a file. + + \note The initialization is not thread safe! +*/ + +// locking support +static int32 init_counter = 0; +static sem_id dbg_printf_sem = -1; +static thread_id dbg_printf_thread = -1; +static int dbg_printf_nesting = 0; + +#if DEBUG_PRINT +static int out = -1; +#endif + +// init_debugging +status_t +init_debugging() +{ + status_t error = B_OK; + if (init_counter++ == 0) { + // open the file + #if DEBUG_PRINT + out = open(DEBUG_PRINT_FILE, O_RDWR | O_CREAT | O_TRUNC); + if (out < 0) { + error = errno; + init_counter--; + } + #endif // DEBUG_PRINT + // allocate the semaphore + if (error == B_OK) { + dbg_printf_sem = create_sem(1, "dbg_printf"); + if (dbg_printf_sem < 0) + error = dbg_printf_sem; + } + if (error == B_OK) { + #if DEBUG + __out("##################################################\n"); + #endif + } else + exit_debugging(); + } + return error; +} + +// exit_debugging +status_t +exit_debugging() +{ + status_t error = B_OK; + if (--init_counter == 0) { + #if DEBUG_PRINT + close(out); + out = -1; + #endif // DEBUG_PRINT + delete_sem(dbg_printf_sem); + } else + error = B_NO_INIT; + return error; +} + +// dbg_printf_lock +static inline +bool +dbg_printf_lock() +{ + thread_id thread = find_thread(NULL); + if (thread != dbg_printf_thread) { + if (acquire_sem(dbg_printf_sem) != B_OK) + return false; + dbg_printf_thread = thread; + } + dbg_printf_nesting++; + return true; +} + +// dbg_printf_unlock +static inline +void +dbg_printf_unlock() +{ + thread_id thread = find_thread(NULL); + if (thread != dbg_printf_thread) + return; + dbg_printf_nesting--; + if (dbg_printf_nesting == 0) { + dbg_printf_thread = -1; + release_sem(dbg_printf_sem); + } +} + +// dbg_printf_begin +void +dbg_printf_begin() +{ + dbg_printf_lock(); +} + +// dbg_printf_end +void +dbg_printf_end() +{ + dbg_printf_unlock(); +} + +#if DEBUG_PRINT + +// dbg_printf +void +dbg_printf(const char *format,...) +{ + if (!dbg_printf_lock()) + return; + char buffer[1024]; + va_list args; + va_start(args, format); + // no vsnprintf() on PPC and in kernel + #if defined(__INTEL__) && USER + vsnprintf(buffer, sizeof(buffer) - 1, format, args); + #else + vsprintf(buffer, format, args); + #endif + va_end(args); + buffer[sizeof(buffer) - 1] = '\0'; + write(out, buffer, strlen(buffer)); + dbg_printf_unlock(); +} + +#endif // DEBUG_PRINT diff --git a/src/add-ons/kernel/file_systems/userlandfs/shared/DriverSettings.cpp b/src/add-ons/kernel/file_systems/userlandfs/shared/DriverSettings.cpp new file mode 100644 index 0000000000..2ef4b6caec --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/shared/DriverSettings.cpp @@ -0,0 +1,492 @@ +// DriverSettings.cpp + +#include +#include +#include + +#include + +#include "DriverSettings.h" +#include "Referencable.h" +#include "String.h" + +// The parameter values that shall be evaluated to true. +static const char* kTrueValueStrings[] + = { "1", "true", "yes", "on", "enable", "enabled" }; +static const int32 kTrueValueStringCount + = sizeof(kTrueValueStrings) / sizeof(const char*); + + +// #pragma mark - +// #pragma mark ----- DriverParameterIterator ----- + +// Delegate +class DriverParameterIterator::Delegate : public Referencable { +public: + Delegate() : Referencable(true) {} + virtual ~Delegate() {} + + virtual Delegate* Clone() const = 0; + + virtual bool HasNext() const = 0; + virtual bool GetNext(DriverParameter* parameter) = 0; +}; + +// constructor +DriverParameterIterator::DriverParameterIterator() + : fDelegate(NULL) +{ +} + +// constructor +DriverParameterIterator::DriverParameterIterator(Delegate* delegate) + : fDelegate(delegate) +{ +} + +// copy constructor +DriverParameterIterator::DriverParameterIterator( + const DriverParameterIterator& other) + : fDelegate(NULL) +{ + _SetTo(other.fDelegate, true); +} + +// destructor +DriverParameterIterator::~DriverParameterIterator() +{ + _SetTo(NULL, false); +} + +// HasNext +bool +DriverParameterIterator::HasNext() const +{ + return (fDelegate ? fDelegate->HasNext() : false); +} + +// GetNext +bool +DriverParameterIterator::GetNext(DriverParameter* parameter) +{ + if (!fDelegate) + return false; + if (fDelegate->CountReferences() > 1) { + Delegate* clone = fDelegate->Clone(); + if (!clone) + return false; + _SetTo(clone, false); + } + return fDelegate->GetNext(parameter); +} + +// = +DriverParameterIterator& +DriverParameterIterator::operator=(const DriverParameterIterator& other) +{ + _SetTo(other.fDelegate, true); + return *this; +} + +// _SetTo +void +DriverParameterIterator::_SetTo(Delegate* delegate, bool addReference) +{ + if (fDelegate) + fDelegate->RemoveReference(); + fDelegate = delegate; + if (fDelegate && addReference) + fDelegate->AddReference(); +} + + +// #pragma mark - +// #pragma mark ----- DriverParameterContainer ----- + +// Iterator +class DriverParameterContainer::Iterator + : public DriverParameterIterator::Delegate { +public: + Iterator(const driver_parameter* parameters, int32 count) + : Delegate(), + fParameters(parameters), + fCount(count) + { + } + + virtual ~Iterator() + { + } + + virtual Delegate* Clone() const + { + return new(nothrow) Iterator(fParameters, fCount); + } + + virtual bool HasNext() const + { + return (fParameters && fCount > 0); + } + + virtual bool GetNext(DriverParameter* parameter) + { + if (fParameters && fCount > 0) { + if (parameter) + parameter->SetTo(fParameters); + fParameters++; + fCount--; + return true; + } + return false; + } + +private: + const driver_parameter* fParameters; + int32 fCount; +}; + +// NameIterator +class DriverParameterContainer::NameIterator + : public DriverParameterIterator::Delegate { +public: + NameIterator(const driver_parameter* parameters, int32 count, + const char* name) + : Delegate(), + fParameters(parameters), + fCount(count), + fName(name) + { + _FindNext(false); + } + + virtual ~NameIterator() + { + } + + virtual Delegate* Clone() const + { + return new(nothrow) NameIterator(fParameters, fCount, + fName.GetString()); + } + + virtual bool HasNext() const + { + return (fParameters && fCount > 0); + } + + virtual bool GetNext(DriverParameter* parameter) + { + if (fParameters && fCount > 0) { + if (parameter) + parameter->SetTo(fParameters); + _FindNext(true); + return true; + } + return false; + } + +private: + void _FindNext(bool skipCurrent) + { + if (!fParameters || fCount < 1) + return; + if (skipCurrent) { + fParameters++; + fCount--; + } + while (fCount > 0 && fName != fParameters->name) { + fParameters++; + fCount--; + } + } + +private: + const driver_parameter* fParameters; + int32 fCount; + String fName; +}; + +// constructor +DriverParameterContainer::DriverParameterContainer() +{ +} + +// destructor +DriverParameterContainer::~DriverParameterContainer() +{ +} + +// CountParameters +int32 +DriverParameterContainer::CountParameters() const +{ + int32 count; + return (GetParametersAndCount(&count) ? count : 0); + +} + +// GetParameters +const driver_parameter* +DriverParameterContainer::GetParameters() const +{ + int32 count; + return GetParametersAndCount(&count); +} + +// GetParameterAt +bool +DriverParameterContainer::GetParameterAt(int32 index, + DriverParameter* parameter) const +{ + int32 count; + if (const driver_parameter* parameters = GetParametersAndCount(&count)) { + if (index >= 0 && index < count) { + if (parameter) + parameter->SetTo(parameters + index); + return true; + } + } + return false; +} + +// FindParameter +bool +DriverParameterContainer::FindParameter(const char* name, + DriverParameter* parameter) const +{ + if (!name) + return false; + int32 count; + if (const driver_parameter* parameters = GetParametersAndCount(&count)) { + for (int32 i = 0; i < count; i++) { + if (strcmp(name, parameters[i].name) == 0) { + if (parameter) + parameter->SetTo(parameters + i); + return true; + } + } + } + return false; +} + +// GetParameterIterator +DriverParameterIterator +DriverParameterContainer::GetParameterIterator() const +{ + int32 count; + if (const driver_parameter* parameters = GetParametersAndCount(&count)) { + if (Iterator* iterator = new(nothrow) Iterator(parameters, count)) + return DriverParameterIterator(iterator); + } + return DriverParameterIterator(); +} + +// GetParameterIterator +DriverParameterIterator +DriverParameterContainer::GetParameterIterator(const char* name) const +{ + int32 count; + if (const driver_parameter* parameters = GetParametersAndCount(&count)) { + NameIterator* iterator = new(nothrow) NameIterator(parameters, count, + name); + if (iterator) + return DriverParameterIterator(iterator); + } + return DriverParameterIterator(); +} + +// GetParameterValue +const char* +DriverParameterContainer::GetParameterValue(const char* name, + const char* unknownValue, const char* noValue) const +{ + DriverParameter parameter; + if (!FindParameter(name, ¶meter)) + return unknownValue; + return parameter.ValueAt(0, noValue); +} + +// GetBoolParameterValue +bool +DriverParameterContainer::GetBoolParameterValue(const char* name, + bool unknownValue, bool noValue) const +{ + DriverParameter parameter; + if (!FindParameter(name, ¶meter)) + return unknownValue; + return parameter.BoolValueAt(0, noValue); +} + +// GetInt32ParameterValue +int32 +DriverParameterContainer::GetInt32ParameterValue(const char* name, + int32 unknownValue, int32 noValue) const +{ + DriverParameter parameter; + if (!FindParameter(name, ¶meter)) + return unknownValue; + return parameter.Int32ValueAt(0, noValue); +} + +// GetInt64ParameterValue +int64 +DriverParameterContainer::GetInt64ParameterValue(const char* name, + int64 unknownValue, int64 noValue) const +{ + DriverParameter parameter; + if (!FindParameter(name, ¶meter)) + return unknownValue; + return parameter.Int64ValueAt(0, noValue); +} + + +// #pragma mark - +// #pragma mark ----- DriverSettings ----- + +// constructor +DriverSettings::DriverSettings() + : DriverParameterContainer(), + fSettingsHandle(NULL), + fSettings(NULL) +{ +} + +// destructor +DriverSettings::~DriverSettings() +{ + Unset(); +} + +// Load +status_t +DriverSettings::Load(const char* driverName) +{ + Unset(); + fSettingsHandle = load_driver_settings(driverName); + if (!fSettingsHandle) + return B_ENTRY_NOT_FOUND; + fSettings = get_driver_settings(fSettingsHandle); + if (!fSettings) { + Unset(); + return B_ERROR; + } + return B_OK; +} + +// Unset +void +DriverSettings::Unset() +{ + if (fSettingsHandle) + unload_driver_settings(fSettingsHandle); + fSettingsHandle = NULL; + fSettings = NULL; +} + +// GetParametersAndCount +const driver_parameter* +DriverSettings::GetParametersAndCount(int32* count) const +{ + if (!fSettings) + return NULL; + *count = fSettings->parameter_count; + return fSettings->parameters; +} + + +// #pragma mark - +// #pragma mark ----- DriverParameter ----- + +// constructor +DriverParameter::DriverParameter() + : DriverParameterContainer(), + fParameter(NULL) +{ +} + +// destructor +DriverParameter::~DriverParameter() +{ +} + +// SetTo +void +DriverParameter::SetTo(const driver_parameter* parameter) +{ + fParameter = parameter; +} + +// GetName +const char* +DriverParameter::GetName() const +{ + return (fParameter ? fParameter->name : NULL); +} + +// CountValues +int32 +DriverParameter::CountValues() const +{ + return (fParameter ? fParameter->value_count : 0); +} + +// GetValues +const char* const* +DriverParameter::GetValues() const +{ + return (fParameter ? fParameter->values : 0); +} + +// ValueAt +const char* +DriverParameter::ValueAt(int32 index, const char* noValue) const +{ + if (!fParameter || index < 0 || index >= fParameter->value_count) + return noValue; + return fParameter->values[index]; +} + +// BoolValueAt +bool +DriverParameter::BoolValueAt(int32 index, bool noValue) const +{ + const char* value = ValueAt(index, NULL); + if (!value) + return noValue; + for (int32 i = 0; i < kTrueValueStringCount; i++) { + if (strcmp(value, kTrueValueStrings[i]) == 0) + return true; + } + return false; +} + +// Int32ValueAt +int32 +DriverParameter::Int32ValueAt(int32 index, int32 noValue) const +{ + const char* value = ValueAt(index, NULL); + if (!value) + return noValue; + return atol(value); +} + +// Int64ValueAt +int64 +DriverParameter::Int64ValueAt(int32 index, int64 noValue) const +{ + const char* value = ValueAt(index, NULL); + if (!value) + return noValue; + return strtoll(value, NULL, 10); +} + +// GetParametersAndCount +const driver_parameter* +DriverParameter::GetParametersAndCount(int32* count) const +{ + if (!fParameter) + return NULL; + *count = fParameter->parameter_count; + return fParameter->parameters; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/shared/LazyInitializable.cpp b/src/add-ons/kernel/file_systems/userlandfs/shared/LazyInitializable.cpp new file mode 100644 index 0000000000..730fe93603 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/shared/LazyInitializable.cpp @@ -0,0 +1,60 @@ +// LazyInitializable.cpp + +#include "LazyInitializable.h" + +// constructor +LazyInitializable::LazyInitializable() + : fInitStatus(B_NO_INIT), + fInitSemaphore(-1) +{ + fInitSemaphore = create_sem(1, "init semaphore"); + if (fInitSemaphore < 0) + fInitStatus = fInitSemaphore; +} + +// constructor +LazyInitializable::LazyInitializable(bool init) + : fInitStatus(B_NO_INIT), + fInitSemaphore(-1) +{ + if (init) { + fInitSemaphore = create_sem(1, "init semaphore"); + if (fInitSemaphore < 0) + fInitStatus = fInitSemaphore; + } else + fInitStatus = B_OK; +} + +// destructor +LazyInitializable::~LazyInitializable() +{ + if (fInitSemaphore >= 0) + delete_sem(fInitSemaphore); +} + +// Access +status_t +LazyInitializable::Access() +{ + if (fInitSemaphore >= 0) { + status_t error = B_OK; + do { + error = acquire_sem(fInitSemaphore); + } while (error == B_INTERRUPTED); + if (error == B_OK) { + // we are the first: initialize + fInitStatus = FirstTimeInit(); + delete_sem(fInitSemaphore); + fInitSemaphore = -1; + } + } + return fInitStatus; +} + +// InitCheck +status_t +LazyInitializable::InitCheck() const +{ + return fInitStatus; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/shared/Locker.cpp b/src/add-ons/kernel/file_systems/userlandfs/shared/Locker.cpp new file mode 100644 index 0000000000..16de04c9b4 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/shared/Locker.cpp @@ -0,0 +1,303 @@ +// +// $Id: Locker.cpp,v 1.1 2002/07/09 12:24:49 ejakowatz Exp $ +// +// This file contains the OpenBeOS implementation of Locker. +// + + +#include "Debug.h" +#include "Locker.h" +#include +#include + + +#ifdef USE_OPENBEOS_NAMESPACE +namespace OpenBeOS { +#endif + + +// +// Data Member Documentation: +// +// The "fBenaphoreCount" member is set to 1 if the Locker style is +// semaphore. If the style is benaphore, it is initialized to 0 and +// is incremented atomically when it is acquired, decremented when it +// is released. By setting the benaphore count to 1 when the style is +// semaphore, the benaphore effectively becomes a semaphore. I was able +// to determine this is what Be's implementation does by testing the +// result of the CountLockRequests() member. +// +// The "fSemaphoreID" member holds the sem_id returned from create_sem() +// when the Locker is constructed. It is used to acquire and release +// the lock regardless of the lock style (semaphore or benaphore). +// +// The "fLockOwner" member holds the thread_id of the thread which +// currently holds the lock. If no thread holds the lock, it is set to +// B_ERROR. +// +// The "fRecursiveCount" member holds a count of the number of times the +// thread holding the lock has acquired the lock without a matching unlock. +// It is basically the number of times the thread must call Unlock() before +// the lock can be acquired by a different thread. +// + + +// +// Constructors: +// +// All constructors just pass their arguments to InitLocker(). Note that +// the default for "name" is "some Locker" and "benaphore_style" is true. +// + +Locker::Locker() +{ + InitLocker("some Locker", true); +} + + +Locker::Locker(const char *name) +{ + InitLocker(name, true); +} + + +Locker::Locker(bool benaphore_style) +{ + InitLocker("some Locker", benaphore_style); +} + + +Locker::Locker(const char *name, + bool benaphore_style) +{ + InitLocker(name, benaphore_style); +} + + +// +// This constructor is not documented. The final argument is ignored for +// now. In Be's headers, its called "for_IPC". DO NOT USE THIS +// CONSTRUCTOR! +// +Locker::Locker(const char *name, + bool benaphore_style, + bool) +{ + InitLocker(name, benaphore_style); +} + + +// +// The destructor just deletes the semaphore. By deleting the semaphore, +// any threads waiting to acquire the Locker will be unblocked. +// +Locker::~Locker() +{ + delete_sem(fSemaphoreID); +} + + +bool +Locker::Lock(void) +{ + status_t result; + + return (AcquireLock(B_INFINITE_TIMEOUT, &result)); +} + + +status_t +Locker::LockWithTimeout(bigtime_t timeout) +{ + status_t result; + + AcquireLock(timeout, &result); + return result; +} + + + +void +Locker::Unlock(void) +{ + // If the thread currently holds the lockdecrement + if (IsLocked()) { + + // Decrement the number of outstanding locks this thread holds + // on this Locker. + fRecursiveCount--; + + // If the recursive count is now at 0, that means the Locker has + // been released by the thread. + if (fRecursiveCount == 0) { + + // The Locker is no longer owned by any thread. + fLockOwner = B_ERROR; + + // Decrement the benaphore count and store the undecremented + // value in oldBenaphoreCount. + int32 oldBenaphoreCount = atomic_add(&fBenaphoreCount, -1); + + // If the oldBenaphoreCount is greater than 1, then there is + // at lease one thread waiting for the lock in the case of a + // benaphore. + if (oldBenaphoreCount > 1) { + + // Since there are threads waiting for the lock, it must + // be released. Note, the old benaphore count will always be + // greater than 1 for a semaphore so the release is always done. + release_sem(fSemaphoreID); + } + } + } +} + + +thread_id +Locker::LockingThread(void) const +{ + return fLockOwner; +} + + +bool +Locker::IsLocked(void) const +{ + // This member returns true if the calling thread holds the lock. + // The easiest way to determine this is to compare the result of + // find_thread() to the fLockOwner. + return (find_thread(NULL) == fLockOwner); +} + + +int32 +Locker::CountLocks(void) const +{ + return fRecursiveCount; +} + + +int32 +Locker::CountLockRequests(void) const +{ + return fBenaphoreCount; +} + + +sem_id +Locker::Sem(void) const +{ + return fSemaphoreID; +} + + +void +Locker::InitLocker(const char *name, + bool benaphore) +{ + if (benaphore) { + // Because this is a benaphore, initialize the benaphore count and + // create the semaphore. Because this is a benaphore, the semaphore + // count starts at 0 (ie acquired). + fBenaphoreCount = 0; + fSemaphoreID = create_sem(0, name); + } else { + // Because this is a semaphore, initialize the benaphore count to -1 + // and create the semaphore. Because this is semaphore style, the + // semaphore count starts at 1 so that one thread can acquire it and + // the next thread to acquire it will block. + fBenaphoreCount = 1; + fSemaphoreID = create_sem(1, name); + } + +// bonefish: make kernel safe +#if !USER + set_sem_owner(fSemaphoreID, B_SYSTEM_TEAM); +#endif + + // The lock is currently not acquired so there is no owner. + fLockOwner = B_ERROR; + + // The lock is currently not acquired so the recursive count is zero. + fRecursiveCount = 0; +} + + +bool +Locker::AcquireLock(bigtime_t timeout, + status_t *error) +{ + // By default, return no error. + *error = B_NO_ERROR; + + // Only try to acquire the lock if the thread doesn't already own it. + if (!IsLocked()) { + + // Increment the benaphore count and test to see if it was already greater + // than 0. If it is greater than 0, then some thread already has the + // benaphore or the style is a semaphore. Either way, we need to acquire + // the semaphore in this case. + int32 oldBenaphoreCount = atomic_add(&fBenaphoreCount, 1); + if (oldBenaphoreCount > 0) { + + *error = acquire_sem_etc(fSemaphoreID, 1, B_RELATIVE_TIMEOUT, + timeout); + // Note, if the lock here does time out, the benaphore count + // is not decremented. By doing this, the benaphore count will + // never go back to zero. This means that the locking essentially + // changes to semaphore style if this was a benaphore. + // + // Doing the decrement of the benaphore count when the acquisition + // fails is a risky thing to do. If you decrement the counter at + // the same time the thread which holds the benaphore does an + // Unlock(), there is serious risk of a race condition. + // + // If the Unlock() sees a positive count and releases the semaphore + // and then the timed out thread decrements the count to 0, there + // is no one to take the semaphore. The next two threads will be + // able to acquire the benaphore at the same time! The first will + // increment the counter and acquire the lock. The second will + // acquire the semaphore and therefore the lock. Not good. + // + // This has been discussed on the becodetalk mailing list and + // Trey from Be had this to say: + // + // I looked at the LockWithTimeout() code, and it does not have + // _this_ (ie the race condition) problem. It circumvents it by + // NOT doing the atomic_add(&count, -1) if the semaphore + // acquisition fails. This means that if a + // Locker::LockWithTimeout() times out, all other Lock*() attempts + // turn into guaranteed semaphore grabs, _with_ the overhead of a + // (now) useless atomic_add(). + // + // Given Trey's comments, it looks like Be took the same approach + // I did. The output of CountLockRequests() of Be's implementation + // confirms Trey's comments also. + // + // Finally some thoughts for the future with this code: + // - If 2^31 timeouts occur on a 32-bit machine (ie today), + // the benaphore count will wrap to a negative number. This + // would have unknown consequences on the ability of the Locker + // to continue to function. + // + } + } + + // If the lock has successfully been acquired. + if (*error == B_NO_ERROR) { + + // Set the lock owner to this thread and increment the recursive count + // by one. The recursive count is incremented because one more Unlock() + // is now required to release the lock (ie, 0 => 1, 1 => 2 etc). + fLockOwner = find_thread(NULL); + fRecursiveCount++; + } + + // Return true if the lock has been acquired. + return (*error == B_NO_ERROR); +} + + +#ifdef USE_OPENBEOS_NAMESPACE +} +#endif diff --git a/src/add-ons/kernel/file_systems/userlandfs/shared/ObjectTracker.cpp b/src/add-ons/kernel/file_systems/userlandfs/shared/ObjectTracker.cpp new file mode 100644 index 0000000000..999379ee8c --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/shared/ObjectTracker.cpp @@ -0,0 +1,101 @@ +// ObjectTracker.h + +#include +#include + +#include "AutoLocker.h" +#include "Debug.h" +#include "ObjectTracker.h" + +static char sTrackerBuffer[sizeof(ObjectTracker)]; + +// constructor +ObjectTrackable::ObjectTrackable() +{ + ObjectTracker::GetDefault()->AddTrackable(this); +} + +// destructor +ObjectTrackable::~ObjectTrackable() +{ + ObjectTracker::GetDefault()->RemoveTrackable(this); +} + + +// #pragma mark - + +// constructor +ObjectTracker::ObjectTracker() + : fLock("object tracker"), + fTrackables() +{ +} + +// destructor +ObjectTracker::~ObjectTracker() +{ + ObjectTrackable* trackable = fTrackables.GetFirst(); + if (trackable) { + WARN(("ObjectTracker: WARNING: There are still undeleted objects:\n")); + for (; trackable; trackable = fTrackables.GetNext(trackable)) { + WARN((" trackable: %p: type: `%s'\n", trackable, + typeid(*trackable).name())); + } + } +} + +// InitDefault +ObjectTracker* +ObjectTracker::InitDefault() +{ + if (!sTracker) + sTracker = new(sTrackerBuffer) ObjectTracker; + return sTracker; +} + +// ExitDefault +void +ObjectTracker::ExitDefault() +{ + if (sTracker) { + sTracker->~ObjectTracker(); + sTracker = NULL; + } +} + +// GetDefault +ObjectTracker* +ObjectTracker::GetDefault() +{ + return sTracker; +} + +// AddTrackable +void +ObjectTracker::AddTrackable(ObjectTrackable* trackable) +{ + if (!this) + return; + + if (trackable) { + AutoLocker _(fLock); + fTrackables.Insert(trackable); + } +} + +// RemoveTrackable +void +ObjectTracker::RemoveTrackable(ObjectTrackable* trackable) +{ + if (!this) + return; + + if (trackable) { + AutoLocker _(fLock); + fTrackables.Remove(trackable); + } +} + +// sTracker +ObjectTracker* ObjectTracker::sTracker = NULL; + diff --git a/src/add-ons/kernel/file_systems/userlandfs/shared/Referencable.cpp b/src/add-ons/kernel/file_systems/userlandfs/shared/Referencable.cpp new file mode 100644 index 0000000000..065ebb70b9 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/shared/Referencable.cpp @@ -0,0 +1,41 @@ +// Referencable.cpp + +#include "Debug.h" +#include "Referencable.h" + +// constructor +Referencable::Referencable(bool deleteWhenUnreferenced) + : fReferenceCount(1), + fDeleteWhenUnreferenced(deleteWhenUnreferenced) +{ +} + +// destructor +Referencable::~Referencable() +{ +} + +// AddReference +void +Referencable::AddReference() +{ + atomic_add(&fReferenceCount, 1); +} + +// RemoveReference +bool +Referencable::RemoveReference() +{ + bool unreferenced = (atomic_add(&fReferenceCount, -1) == 1); + if (fDeleteWhenUnreferenced && unreferenced) + delete this; + return unreferenced; +} + +// CountReferences +int32 +Referencable::CountReferences() const +{ + return fReferenceCount; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/shared/String.cpp b/src/add-ons/kernel/file_systems/userlandfs/shared/String.cpp new file mode 100644 index 0000000000..5b9af20d7b --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/shared/String.cpp @@ -0,0 +1,145 @@ +// String.cpp + +#include +#include + +#include "String.h" + +// strnlen +size_t +strnlen(const char *str, size_t maxLen) +{ + if (str) { + size_t origMaxLen = maxLen; + while (maxLen > 0 && *str != '\0') { + maxLen--; + str++; + } + return origMaxLen - maxLen; + } + return 0; +} + + +/*! + \class String + \brief A very simple string class. +*/ + +// constructor +String::String() + : fLength(0), + fString(NULL) +{ +} + +// copy constructor +String::String(const String &string) + : fLength(0), + fString(NULL) +{ + *this = string; +} + +// constructor +String::String(const char *string, int32 length) + : fLength(0), + fString(NULL) +{ + SetTo(string, length); +} + +// destructor +String::~String() +{ + Unset(); +} + +// SetTo +bool +String::SetTo(const char *string, int32 maxLength) +{ + if (string) { + if (maxLength > 0) + maxLength = strnlen(string, maxLength); + else if (maxLength < 0) + maxLength = strlen(string); + } + return _SetTo(string, maxLength); +} + +// Unset +void +String::Unset() +{ + if (fString) { + delete[] fString; + fString = NULL; + } + fLength = 0; +} + +// Truncate +void +String::Truncate(int32 newLength) +{ + if (newLength < 0) + newLength = 0; + if (newLength < fLength) { + char *string = fString; + int32 len = fLength; + fString = NULL; + len = 0; + if (!_SetTo(string, newLength)) { + fString = string; + fLength = newLength; + fString[fLength] = '\0'; + } else + delete[] string; + } +} + +// GetString +const char * +String::GetString() const +{ + if (fString) + return fString; + return ""; +} + +// = +String & +String::operator=(const String &string) +{ + if (&string != this) + _SetTo(string.fString, string.fLength); + return *this; +} + +// == +bool +String::operator==(const String &string) const +{ + return (fLength == string.fLength + && (fLength == 0 || !strcmp(fString, string.fString))); +} + +// _SetTo +bool +String::_SetTo(const char *string, int32 length) +{ + bool result = true; + Unset(); + if (string && length > 0) { + fString = new(nothrow) char[length + 1]; + if (fString) { + memcpy(fString, string, length); + fString[length] = '\0'; + fLength = length; + } else + result = false; + } + return result; +} + diff --git a/src/add-ons/kernel/file_systems/userlandfs/shared/driver_settings.c b/src/add-ons/kernel/file_systems/userlandfs/shared/driver_settings.c new file mode 100644 index 0000000000..49143a5310 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/shared/driver_settings.c @@ -0,0 +1,768 @@ +/* driver_settings - implements the driver settings API +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + + +#include +#include + +#include +#include +#include +#include +#include + +#include "Compatibility.h" +#include "String.h" + +// strlcat +size_t +strlcat(char *dst, char const *src, size_t s) +{ + size_t i, j = strnlen(dst, s); + + if (!s) + return j + strlen(src); + + dst += j; + + for (i = 0; ((i < s-1) && src[i]); i++) { + dst[i] = src[i]; + } + + dst[i] = 0; + + return j + i + strlen(src + i); +} + +#define SETTINGS_DIRECTORY "/boot/home/config/settings/kernel/drivers/" +#define SETTINGS_MAGIC 'DrvS' + +// Those maximum values are independent from the implementation - they +// have been chosen to make the code more robust against bad files +#define MAX_SETTINGS_SIZE 32768 +#define MAX_SETTINGS_LEVEL 8 + +#define CONTINUE_PARAMETER 1 +#define NO_PARAMETER 2 + + +typedef struct settings_handle { + void *first_buffer; + int32 magic; + struct driver_settings settings; + char *text; +} settings_handle; + + +enum assignment_mode { + NO_ASSIGNMENT, + ALLOW_ASSIGNMENT, + IGNORE_ASSIGNMENT +}; + +// Functions not part of the public API + + +/** Returns true for any characters that separate parameters - + * those are ignored in the input stream and won't be added + * to any words. + */ + +static inline bool +is_parameter_separator(char c) +{ + return c == '\n' || c == ';'; +} + + +/** Indicates if "c" begins a new word or not. + */ + +static inline bool +is_word_break(char c) +{ + return isspace(c) || is_parameter_separator(c); +} + + +static inline bool +check_handle(settings_handle *handle) +{ + if (handle == NULL + || handle->magic != SETTINGS_MAGIC) + return false; + + return true; +} + + +static driver_parameter * +get_parameter(settings_handle *handle, const char *name) +{ + int32 i; + for (i = handle->settings.parameter_count; i-- > 0;) { + if (!strcmp(handle->settings.parameters[i].name, name)) + return &handle->settings.parameters[i]; + } + return NULL; +} + + +/** Returns the next word in the input buffer passed in via "_pos" - if + * this function returns, it will bump the input position after the word. + * It automatically cares about quoted strings and escaped characters. + * If "allowNewLine" is true, it reads over comments to get to the next + * word. + * Depending on the "assignmentMode" parameter, the '=' sign is either + * used as a work break, or not. + * The input buffer will be changed to contain the word without quotes + * or escaped characters and adds a terminating NULL byte. The "_word" + * parameter will be set to the beginning of the word. + * If the word is followed by a newline it will return B_OK, if white + * spaces follows, it will return CONTINUE_PARAMETER. + */ + +static status_t +get_word(char **_pos, char **_word, int32 assignmentMode, bool allowNewLine) +{ + char *pos = *_pos; + char quoted = 0; + bool newLine = false, end = false; + int escaped = 0; + bool charEscaped = false; + + // Skip any white space and comments + while (pos[0] + && ((allowNewLine && (isspace(pos[0]) || is_parameter_separator(pos[0]) || pos[0] == '#')) + || (!allowNewLine && (pos[0] == '\t' || pos[0] == ' ')) + || (assignmentMode == ALLOW_ASSIGNMENT && pos[0] == '='))) { + // skip any comment lines + if (pos[0] == '#') { + while (pos[0] && pos[0] != '\n') + pos++; + } + pos++; + } + + if (pos[0] == '}' || pos[0] == '\0') { + // if we just read some white space before an end of a + // parameter, this is just no parameter at all + *_pos = pos; + return NO_PARAMETER; + } + + // Read in a word - might contain escaped (\) spaces, or it + // might also be quoted (" or '). + + if (pos[0] == '"' || pos[0] == '\'') { + quoted = pos[0]; + pos++; + } + *_word = pos; + + while (pos[0]) { + if (charEscaped) + charEscaped = false; + else if (pos[0] == '\\') { + charEscaped = true; + escaped++; + } else if ((!quoted && (is_word_break(pos[0]) + || (assignmentMode != IGNORE_ASSIGNMENT && pos[0] == '='))) + || (quoted && pos[0] == quoted)) + break; + + pos++; + } + + // "String exceeds line" - missing end quote + if (quoted && pos[0] != quoted) + return B_BAD_DATA; + + // last character is a backslash + if (charEscaped) + return B_BAD_DATA; + + end = pos[0] == '\0'; + newLine = is_parameter_separator(pos[0]) || end; + pos[0] = '\0'; + + // Correct name if there were any escaped characters + if (escaped) { + char *word = *_word; + int offset = 0; + while (word <= pos) { + if (word[0] == '\\') { + offset--; + word++; + } + word[offset] = word[0]; + word++; + } + } + + if (end) { + *_pos = pos; + return B_OK; + } + + // Scan for next beginning word, open brackets, or comment start + + pos++; + while (true) { + *_pos = pos; + if (!pos[0]) + return B_NO_ERROR; + + if (is_parameter_separator(pos[0])) { + // an open bracket '{' could follow after the first + // newline, but not later + if (newLine) + return B_NO_ERROR; + + newLine = true; + } else if (pos[0] == '{' || pos[0] == '}' || pos[0] == '#') + return B_NO_ERROR; + else if (!isspace(pos[0])) + return newLine ? B_NO_ERROR : CONTINUE_PARAMETER; + + pos++; + } +} + + +static status_t +parse_parameter(struct driver_parameter *parameter, char **_pos, int32 level) +{ + char *pos = *_pos; + status_t status; + + // initialize parameter first + memset(parameter, 0, sizeof(struct driver_parameter)); + + status = get_word(&pos, ¶meter->name, NO_ASSIGNMENT, true); + if (status == CONTINUE_PARAMETER) { + while (status == CONTINUE_PARAMETER) { + char **newArray, *value; + status = get_word(&pos, &value, parameter->value_count == 0 ? ALLOW_ASSIGNMENT : IGNORE_ASSIGNMENT, false); + if (status < B_OK) + break; + + // enlarge value array and save the value + + newArray = realloc(parameter->values, (parameter->value_count + 1) * sizeof(char *)); + if (newArray == NULL) + return B_NO_MEMORY; + + parameter->values = newArray; + parameter->values[parameter->value_count++] = value; + } + } + + *_pos = pos; + return status; +} + + +static status_t +parse_parameters(struct driver_parameter **_parameters, int *_count, char **_pos, int32 level) +{ + if (level > MAX_SETTINGS_LEVEL) + return B_LINK_LIMIT; + + while (true) { + struct driver_parameter parameter; + struct driver_parameter *newArray; + status_t status; + + status = parse_parameter(¶meter, _pos, level); + if (status < B_OK) + return status; + + if (status != NO_PARAMETER) { + driver_parameter *newParameter; + + newArray = realloc(*_parameters, (*_count + 1) * sizeof(struct driver_parameter)); + if (newArray == NULL) + return B_NO_MEMORY; + + memcpy(&newArray[*_count], ¶meter, sizeof(struct driver_parameter)); + newParameter = &newArray[*_count]; + + *_parameters = newArray; + (*_count)++; + + // check for level beginning and end + if (**_pos == '{') { + // if we go a level deeper, just start all over again... + (*_pos)++; + status = parse_parameters(&newParameter->parameters, + &newParameter->parameter_count, _pos, level + 1); + if (status < B_OK) + return status; + } + } + + if ((**_pos == '}' && level > 0) + || (**_pos == '\0' && level == 0)) { + // take the closing bracket from the stack + (*_pos)++; + return B_OK; + } + + // obviously, something has gone wrong + if (**_pos == '}' || **_pos == '\0') + return B_ERROR; + } +} + + +static status_t +parse_settings(settings_handle *handle) +{ + char *text = handle->text; + + memset(&handle->settings, 0, sizeof(struct driver_settings)); + + // empty settings are allowed + if (text == NULL) + return B_OK; + + return parse_parameters(&handle->settings.parameters, &handle->settings.parameter_count, &text, 0); +} + + +static void +free_parameter(struct driver_parameter *parameter) +{ + int32 i; + for (i = parameter->parameter_count; i-- > 0;) + free_parameter(¶meter->parameters[i]); + + free(parameter->parameters); + free(parameter->values); +} + + +static void +free_settings(settings_handle *handle) +{ + int32 i; + for (i = handle->settings.parameter_count; i-- > 0;) + free_parameter(&handle->settings.parameters[i]); + + free(handle->settings.parameters); + + free(handle->text); + free(handle); +} + + +static settings_handle * +load_driver_settings_from_file(int file) +{ + struct stat stat; + + // Allocate a buffer and read the whole file into it. + // We will keep this buffer in memory, until the settings + // are unloaded. + // The driver_parameter::name field will point directly + // to this buffer. + + if (fstat(file, &stat) < B_OK) + return NULL; + + if (stat.st_size > B_OK && stat.st_size < MAX_SETTINGS_SIZE) { + char *text = (char *)malloc(stat.st_size + 1); + if (text != NULL && read(file, text, stat.st_size) == stat.st_size) { + settings_handle *handle = malloc(sizeof(settings_handle)); + if (handle != NULL) { + text[stat.st_size] = '\0'; + + handle->magic = SETTINGS_MAGIC; + handle->text = text; + + if (parse_settings(handle) == B_OK) { + return handle; + } + + free(handle); + } + } + // "text" might be NULL here, but that's allowed + free(text); + } + + return NULL; +} + + +static bool +put_string(char **_buffer, size_t *_bufferSize, char *string) +{ + size_t length, reserved, quotes; + char *buffer = *_buffer, c; + bool quoted; + + if (string == NULL) + return true; + + for (length = reserved = quotes = 0; (c = string[length]) != '\0'; length++) { + if (c == '"') + quotes++; + else if (is_word_break(c)) + reserved++; + } + quoted = reserved || quotes; + + // update _bufferSize in any way, so that we can chain several + // of these calls without having to check the return value + // everytime + *_bufferSize -= length + (quoted ? 2 + quotes : 0); + + if (*_bufferSize <= 0) + return false; + + if (quoted) + *(buffer++) = '"'; + + for (;(c = string[0]) != '\0'; string++) { + if (c == '"') + *(buffer++) = '\\'; + + *(buffer++) = c; + } + + if (quoted) + *(buffer++) = '"'; + + buffer[0] = '\0'; + + // update the buffer position + *_buffer = buffer; + + return true; +} + + +static bool +put_chars(char **_buffer, size_t *_bufferSize, char *chars) +{ + char *buffer = *_buffer; + size_t length; + + if (chars == NULL) + return true; + + length = strlen(chars); + *_bufferSize -= length; + + if (*_bufferSize <= 0) + return false; + + memcpy(buffer, chars, length); + buffer += length; + buffer[0] = '\0'; + + // update the buffer position + *_buffer = buffer; + + return true; +} + + +static bool +put_char(char **_buffer, size_t *_bufferSize, char c) +{ + char *buffer = *_buffer; + + *_bufferSize -= 1; + + if (*_bufferSize <= 0) + return false; + + buffer[0] = c; + buffer[1] = '\0'; + + // update the buffer position + *_buffer = buffer + 1; + + return true; +} + + +static void +put_level_space(char **_buffer, size_t *_bufferSize, int32 level) +{ + while (level-- > 0) + put_char(_buffer, _bufferSize, '\t'); +} + + +static bool +put_parameter(char **_buffer, size_t *_bufferSize, struct driver_parameter *parameter, int32 level, bool flat) +{ + int32 i; + + if (!flat) + put_level_space(_buffer, _bufferSize, level); + + put_string(_buffer, _bufferSize, parameter->name); + if (flat && parameter->value_count > 0) + put_chars(_buffer, _bufferSize, " ="); + + for (i = 0; i < parameter->value_count; i++) { + put_char(_buffer, _bufferSize, ' '); + put_string(_buffer, _bufferSize, parameter->values[i]); + } + + if (parameter->parameter_count > 0) { + put_chars(_buffer, _bufferSize, " {"); + if (!flat) + put_char(_buffer, _bufferSize, '\n'); + + for (i = 0; i < parameter->parameter_count; i++) { + put_parameter(_buffer, _bufferSize, ¶meter->parameters[i], level + 1, flat); + + if (parameter->parameters[i].parameter_count == 0) + put_chars(_buffer, _bufferSize, flat ? "; " : "\n"); + } + + if (!flat) + put_level_space(_buffer, _bufferSize, level); + put_chars(_buffer, _bufferSize, flat ? "}" : "}\n"); + } + + return *_bufferSize >= 0; +} + + +// ToDo: the API to add an item to the driver_settings is obviously accessable +// to the kernel, so we should provide it, too (in BeOS this is used to add +// driver settings at boot time, using the safe boot menu). + +//static status_t +//add_driver_parameter(const char *name, ) +//{ +//} + + +// #pragma mark - +// The public API implementation + + +status_t +unload_driver_settings(void *handle) +{ + if (!check_handle(handle)) + return B_BAD_VALUE; + + free_settings(handle); + return B_OK; +} + + +void * +load_driver_settings(const char *driverName) +{ + settings_handle *handle; + int file; + + if (driverName == NULL) + return NULL; + + // open the settings from the standardized location + { + char path[B_FILE_NAME_LENGTH + 64]; + + // ToDo: use the kernel's find_directory for this + strcpy(path, SETTINGS_DIRECTORY); + strlcat(path, driverName, sizeof(path)); + + file = open(path, O_RDONLY); + } + if (file < B_OK) + return NULL; + + handle = load_driver_settings_from_file(file); + + close(file); + return (void *)handle; +} + + +/** Loads a driver settings file using the full path, instead of + * only defining the leaf name (as load_driver_settings() does). + * I am not sure if this function is really necessary - I would + * probably prefer something like a search order (if it's not + * an absolute path): + * ~/config/settings/kernel/driver + * current directory + * That would render this function useless. + */ + +#if 0 +void * +load_driver_settings_from_path(const char *path) +{ + settings_handle *handle; + int file; + + if (path == NULL) + return NULL; + + file = open(path, O_RDONLY); + if (file < B_OK) + return NULL; + + handle = load_driver_settings_from_file(file); + + close(file); + return (void *)handle; +} +#endif + +/** Returns a new driver_settings handle that has the parsed contents + * of the passed string. + * You can get an empty driver_settings object when you pass NULL as + * the "settingsString" parameter. + */ + +void * +parse_driver_settings_string(const char *settingsString) +{ + // we simply copy the whole string to use it as our internal buffer + char *text = strdup(settingsString); + if (settingsString == NULL || text != NULL) { + settings_handle *handle = malloc(sizeof(settings_handle)); + if (handle != NULL) { + handle->magic = SETTINGS_MAGIC; + handle->text = text; + + if (parse_settings(handle) == B_OK) + return handle; + + free(handle); + } + free(text); + } + + return NULL; +} + + +/** This function prints out a driver settings structure to a human + * readable string. + * It's either in standard style or the single line style speficied + * by the "flat" parameter. + * If the buffer is too small to hold the string, B_BUFFER_OVERFLOW + * is returned, and the needed amount of bytes if placed in the + * "_bufferSize" parameter. + * If the "handle" parameter is not a valid driver settings handle, or + * the "buffer" parameter is NULL, B_BAD_VALUE is returned. + */ + +status_t +get_driver_settings_string(void *_handle, char *buffer, size_t *_bufferSize, bool flat) +{ + settings_handle *handle = (settings_handle *)_handle; + size_t bufferSize = *_bufferSize; + int32 i; + + if (!check_handle(handle) || !buffer || *_bufferSize == 0) + return B_BAD_VALUE; + + for (i = 0; i < handle->settings.parameter_count; i++) { + put_parameter(&buffer, &bufferSize, &handle->settings.parameters[i], 0, flat); + } + + *_bufferSize -= bufferSize; + return bufferSize >= 0 ? B_OK : B_BUFFER_OVERFLOW; +} + + +/** Matches the first value of the parameter matching "keyName" with a set + * of boolean values like 1/true/yes/on/enabled/... + * Returns "unknownValue" if the parameter could not be found or doesn't + * have any valid boolean setting, and "noArgValue" if the parameter + * doesn't have any values. + * Also returns "unknownValue" if the handle passed in was not valid. + */ + +bool +get_driver_boolean_parameter(void *handle, const char *keyName, bool unknownValue, bool noArgValue) +{ + driver_parameter *parameter; + char *boolean; + + if (!check_handle(handle)) + return unknownValue; + + // check for the parameter + if ((parameter = get_parameter(handle, keyName)) == NULL) + return unknownValue; + + // check for the argument + if (parameter->value_count <= 0) + return noArgValue; + + boolean = parameter->values[0]; + if (!strcmp(boolean, "1") + || !strcasecmp(boolean, "true") + || !strcasecmp(boolean, "yes") + || !strcasecmp(boolean, "on") + || !strcasecmp(boolean, "enable") + || !strcasecmp(boolean, "enabled")) + return true; + + if (!strcmp(boolean, "0") + || !strcasecmp(boolean, "false") + || !strcasecmp(boolean, "no") + || !strcasecmp(boolean, "off") + || !strcasecmp(boolean, "disable") + || !strcasecmp(boolean, "disabled")) + return false; + + // if no known keyword is found, "unknownValue" is returned + return unknownValue; +} + + +const char * +get_driver_parameter(void *handle, const char *keyName, const char *unknownValue, const char *noArgValue) +{ + struct driver_parameter *parameter; + + if (!check_handle(handle)) + return unknownValue; + + // check for the parameter + if ((parameter = get_parameter(handle, keyName)) == NULL) + return unknownValue; + + // check for the argument + if (parameter->value_count <= 0) + return noArgValue; + + return parameter->values[0]; +} + + +const driver_settings * +get_driver_settings(void *handle) +{ + if (!check_handle(handle)) + return NULL; + + return &((settings_handle *)handle)->settings; +} + + +// this creates an alias of the above function +// unload_driver_settings() is the same as delete_driver_settings() +#ifndef __MWERKS__ +extern __typeof(unload_driver_settings) delete_driver_settings __attribute__ ((alias ("unload_driver_settings"))); +#endif + diff --git a/src/add-ons/kernel/file_systems/userlandfs/ufs_mount/Jamfile b/src/add-ons/kernel/file_systems/userlandfs/ufs_mount/Jamfile new file mode 100644 index 0000000000..dd86cd3cfe --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/ufs_mount/Jamfile @@ -0,0 +1,15 @@ +SubDir HAIKU_TOP src tests add-ons kernel file_systems userlandfs r5 src + ufs_mount ; + +SetSubDirSupportedPlatforms r5 bone dano ; + +local userlandFSTop = [ FDirName $(HAIKU_TOP) src tests add-ons kernel + file_systems userlandfs r5 ] ; +local userlandFSIncludes = [ FDirName $(userlandFSTop) headers ] ; + +SubDirHdrs [ FDirName $(userlandFSIncludes) private ] ; + +SEARCH_SOURCE += [ FDirName $(userlandFSTop) src shared ] ; + +Application ufs_mount : ufs_mount.cpp : be ; + diff --git a/src/add-ons/kernel/file_systems/userlandfs/ufs_mount/ufs_mount.cpp b/src/add-ons/kernel/file_systems/userlandfs/ufs_mount/ufs_mount.cpp new file mode 100644 index 0000000000..4f8bdb58b6 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/ufs_mount/ufs_mount.cpp @@ -0,0 +1,50 @@ +// ufs_mount.cpp + +#include +#include +#include +#include + +#include + +const char* kUsage = +"Usage: ufs_mount [ ]\n" +; + +// print_usage +void +print_usage(bool error = true) +{ + fprintf((error ? stderr : stdout), kUsage); +} + +// main +int +main(int argc, char** argv) +{ + // check and get the parameters + if (argc < 4 || argc > 5) { + print_usage(); + return 1; + } + const char* fileSystem = argv[1]; + const char* device = argv[2]; + const char* mountPoint = argv[3]; + const char* fsParameters = (argc >= 5 ? argv[4] : NULL); + // get prepare the parameters for the mount() call + if (strlen(device) == 0) + device = NULL; + BString parameters(fileSystem); + if (fsParameters) + parameters << ' ' << fsParameters; + // mount + ulong flags = 0; +printf("mount('userlandfs', '%s', '%s', %lu, '%s', %ld)\n", mountPoint, device, +flags, parameters.String(), parameters.Length() + 1); + if (mount("userlandfs", mountPoint, device, flags, + (void*)parameters.String(), parameters.Length() + 1) < 0) { + fprintf(stderr, "mounting failed: %s\n", strerror(errno)); + return 1; + } + return 0; +}