diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.cpp index b4bf6430a6..c7085ae602 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.cpp +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.cpp @@ -118,13 +118,27 @@ FUSEFileSystem::FUSEFileSystem(const char* fsName, int (*mainFunction)(int, const char* const*)) : FileSystem(fsName), - fMainFunction(mainFunction) + fMainFunction(mainFunction), + fInitThread(-1), + fInitStatus(B_NO_INIT), + fInitSemaphore(-1), + fExitSemaphore(-1), + fInitParameters(NULL), + fFS(NULL) { } FUSEFileSystem::~FUSEFileSystem() { + if (fInitSemaphore >= 0) + delete_sem(fInitSemaphore); + + if (fExitSemaphore >= 0) + delete_sem(fExitSemaphore); + + if (fInitThread >= 0) + wait_for_thread(fInitThread, NULL); } @@ -132,6 +146,10 @@ status_t FUSEFileSystem::CreateVolume(Volume** _volume, dev_t id) { printf("FUSEFileSystem::CreateVolume()\n"); + // Only one volume is possible + if (!fVolumes.IsEmpty()) + RETURN_ERROR(B_BUSY); + // create the volume FUSEVolume* volume = new(std::nothrow) FUSEVolume(this, id); if (volume == NULL) @@ -152,17 +170,131 @@ FUSEFileSystem::DeleteVolume(Volume* volume) status_t FUSEFileSystem::InitClientFS(const char* parameters) +{ + // create the semaphores we need + fInitSemaphore = create_sem(0, "FUSE init sem"); + if (fInitSemaphore < 0) + RETURN_ERROR(fInitSemaphore); + + fExitSemaphore = create_sem(0, "FUSE exit sem"); + if (fExitSemaphore < 0) + RETURN_ERROR(fExitSemaphore); + + fInitStatus = 1; + fInitParameters = parameters; + + // Start the initialization thread -- it will call main() and won't return + // until unmounting. + fInitThread = spawn_thread(&_InitializationThreadEntry, + "FUSE init", B_NORMAL_PRIORITY, this); + if (fInitThread < 0) + RETURN_ERROR(fInitThread); + + resume_thread(fInitThread); + + // wait for the initialization to finish + while (acquire_sem(fInitSemaphore) == B_INTERRUPTED) { + } + + fInitSemaphore = -1; + + if (fInitStatus > 0) + RETURN_ERROR(B_ERROR); + if (fInitStatus != B_OK) + RETURN_ERROR(fInitStatus); + + // initialization went fine + return B_OK; +} + + +void +FUSEFileSystem::ExitClientFS(status_t status) +{ + // set the exit status and notify the initialization thread + fExitStatus = status; + if (fExitSemaphore >= 0) + delete_sem(fExitSemaphore); +} + + +status_t +FUSEFileSystem::FinishInitClientFS(const fuse_operations* ops, size_t opSize, + void* userData) +{ + fExitStatus = B_ERROR; + + // do the initialization + status_t error = _InitClientFS(ops, opSize, userData); + + // notify the mount thread + fInitStatus = error; + delete_sem(fInitSemaphore); + + // loop until unmounting + while (acquire_sem(fExitSemaphore) == B_INTERRUPTED) { + } + + fExitSemaphore = -1; + + if (error == B_OK) + fuse_fs_destroy(fFS); + + return error; +} + + +/*static*/ status_t +FUSEFileSystem::_InitializationThreadEntry(void* data) +{ + return ((FUSEFileSystem*)data)->_InitializationThread(); +} + + +status_t +FUSEFileSystem::_InitializationThread() { // parse the parameters ArgumentVector args; - status_t error = args.Init(GetName(), parameters); - if (error != B_OK) - RETURN_ERROR(error); + status_t error = args.Init(GetName(), fInitParameters); + if (error != B_OK) { + fInitStatus = error; + delete_sem(fInitSemaphore); + return B_OK; + } - // call main + // call main -- should not return until unmounting fMainFunction(args.ArgumentCount(), args.Arguments()); +printf("FUSEFileSystem::_InitializationThread(): main() returned!\n"); -// TODO: Check whether everything went fine! + if (fInitStatus > 0 && fInitSemaphore >= 0) { + // something went wrong early -- main() returned without calling + // fuse_main() + fInitStatus = B_ERROR; + delete_sem(fInitSemaphore); + } + + return B_OK; +} + + +status_t +FUSEFileSystem::_InitClientFS(const fuse_operations* ops, size_t opSize, + void* userData) +{ + // create a fuse_fs object + fFS = fuse_fs_new(ops, opSize, userData); + if (fFS == NULL) + return B_ERROR; + + // init connection info + fConnectionInfo.proto_major = 0; + fConnectionInfo.proto_minor = 0; + fConnectionInfo.async_read = false; + fConnectionInfo.max_write = 64 * 1024; + fConnectionInfo.max_readahead = 64 * 1024; + + fuse_fs_init(fFS, &fConnectionInfo); return B_OK; } diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.h b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.h index 83be64ab0e..b1fc151708 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.h +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.h @@ -7,6 +7,8 @@ #include "../FileSystem.h" +#include "fuse_api.h" + namespace UserlandFS { @@ -17,16 +19,38 @@ public: const char* const*)); virtual ~FUSEFileSystem(); + static FUSEFileSystem* GetInstance(); + virtual status_t CreateVolume(Volume** _volume, dev_t id); virtual status_t DeleteVolume(Volume* volume); status_t InitClientFS(const char* parameters); + void ExitClientFS(status_t status); + + status_t FinishInitClientFS(const fuse_operations* ops, + size_t opSize, void* userData); private: class ArgumentVector; +private: + static status_t _InitializationThreadEntry(void* data); + status_t _InitializationThread(); + + status_t _InitClientFS(const fuse_operations* ops, + size_t opSize, void* userData); + private: int (*fMainFunction)(int, const char* const*); + thread_id fInitThread; + status_t fInitStatus; + status_t fExitStatus; + sem_id fInitSemaphore; + sem_id fExitSemaphore; + fuse_operations fOps; + const char* fInitParameters; + fuse_fs* fFS; + fuse_conn_info fConnectionInfo; }; } // namespace UserlandFS @@ -34,4 +58,11 @@ private: using UserlandFS::FUSEFileSystem; +/*static*/ inline FUSEFileSystem* +FUSEFileSystem::GetInstance() +{ + return static_cast(sInstance); +} + + #endif // USERLAND_FS_FUSE_FILE_SYSTEM_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.cpp index 7974e9db58..4029855051 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.cpp +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.cpp @@ -39,6 +39,8 @@ printf("FUSEVolume::Mount()\n"); if (error != B_OK) RETURN_ERROR(error); + _FileSystem()->ExitClientFS(B_UNSUPPORTED); + return B_UNSUPPORTED; } diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/Jamfile b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/Jamfile index 68e15f4d26..c241dfe051 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/Jamfile +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/Jamfile @@ -20,6 +20,7 @@ DEFINES += _FILE_OFFSET_BITS=64 ; # the library providing the FUSE interface for add-ons SharedLibrary libuserlandfs_fuse.so : + fuse_fs.cpp fuse_main.cpp FUSEFileSystem.cpp FUSEVolume.cpp diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/fuse_api.h b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/fuse_api.h new file mode 100644 index 0000000000..cf554604e6 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/fuse_api.h @@ -0,0 +1,12 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef USERLAND_FS_FUSE_FUSE_API_H +#define USERLAND_FS_FUSE_FUSE_API_H + +#define FUSE_USE_VERSION FUSE_VERSION + +#include + +#endif // USERLAND_FS_FUSE_FUSE_API_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/fuse_fs.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/fuse_fs.cpp new file mode 100644 index 0000000000..d7524a14ad --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/fuse_fs.cpp @@ -0,0 +1,373 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "fuse_fs.h" + +#include + +#include + +#include "Debug.h" + + +int +fuse_fs_getattr(struct fuse_fs* fs, const char* path, struct stat* buf) +{ + if (fs->ops.getattr == NULL) + return -ENOSYS; + return fs->ops.getattr(path, buf); +} + + +int +fuse_fs_fgetattr(struct fuse_fs* fs, const char* path, struct stat* buf, + struct fuse_file_info* fi) +{ + if (fs->ops.fgetattr == NULL) + return -ENOSYS; + return fs->ops.fgetattr(path, buf, fi); +} + + +int +fuse_fs_rename(struct fuse_fs* fs, const char* oldpath, const char* newpath) +{ + if (fs->ops.rename == NULL) + return -ENOSYS; + return fs->ops.rename(oldpath, newpath); +} + + +int +fuse_fs_unlink(struct fuse_fs* fs, const char* path) +{ + if (fs->ops.unlink == NULL) + return -ENOSYS; + return fs->ops.unlink(path); +} + + +int +fuse_fs_rmdir(struct fuse_fs* fs, const char* path) +{ + if (fs->ops.rmdir == NULL) + return -ENOSYS; + return fs->ops.rmdir(path); +} + + +int +fuse_fs_symlink(struct fuse_fs* fs, const char* linkname, const char* path) +{ + if (fs->ops.symlink == NULL) + return -ENOSYS; + return fs->ops.symlink(linkname, path); +} + + +int +fuse_fs_link(struct fuse_fs* fs, const char* oldpath, const char* newpath) +{ + if (fs->ops.link == NULL) + return -ENOSYS; + return fs->ops.link(oldpath, newpath); +} + + +int +fuse_fs_release(struct fuse_fs* fs, const char* path, + struct fuse_file_info* fi) +{ + if (fs->ops.release == NULL) + return 0; + return fs->ops.release(path, fi); +} + + +int +fuse_fs_open(struct fuse_fs* fs, const char* path, struct fuse_file_info* fi) +{ + if (fs->ops.open == NULL) + return 0; + return fs->ops.open(path, fi); +} + + +int +fuse_fs_read(struct fuse_fs* fs, const char* path, char *buf, size_t size, + off_t off, struct fuse_file_info* fi) +{ + if (fs->ops.read == NULL) + return -ENOSYS; + return fs->ops.read(path, buf, size, off, fi); +} + + +int +fuse_fs_write(struct fuse_fs* fs, const char* path, const char* buf, + size_t size, off_t off, struct fuse_file_info* fi) +{ + if (fs->ops.write == NULL) + return -ENOSYS; + return fs->ops.write(path, buf, size, off, fi); +} + + +int +fuse_fs_fsync(struct fuse_fs* fs, const char* path, int datasync, + struct fuse_file_info* fi) +{ + if (fs->ops.fsync == NULL) + return -ENOSYS; + return fs->ops.fsync(path, datasync, fi); +} + + +int +fuse_fs_flush(struct fuse_fs* fs, const char* path, struct fuse_file_info* fi) +{ + if (fs->ops.flush == NULL) + return -ENOSYS; + return fs->ops.flush(path, fi); +} + + +int +fuse_fs_statfs(struct fuse_fs* fs, const char* path, struct statvfs* buf) +{ + if (fs->ops.statfs == NULL) + return 0; + return fs->ops.statfs(path, buf); +} + + +int +fuse_fs_opendir(struct fuse_fs* fs, const char* path, struct fuse_file_info* fi) +{ + if (fs->ops.opendir == NULL) + return 0; + return fs->ops.opendir(path, fi); +} + + +int +fuse_fs_readdir(struct fuse_fs* fs, const char* path, void* buf, + fuse_fill_dir_t filler, off_t off, struct fuse_file_info* fi) +{ + if (fs->ops.readdir == NULL) + return -ENOSYS; + return fs->ops.readdir(path, buf, filler, off, fi); +} + + +int +fuse_fs_fsyncdir(struct fuse_fs* fs, const char* path, int datasync, + struct fuse_file_info* fi) +{ + if (fs->ops.fsyncdir == NULL) + return -ENOSYS; + return fs->ops.fsyncdir(path, datasync, fi); +} + + +int +fuse_fs_releasedir(struct fuse_fs* fs, const char* path, + struct fuse_file_info* fi) +{ + if (fs->ops.releasedir == NULL) + return 0; + return fs->ops.releasedir(path, fi); +} + + +int +fuse_fs_create(struct fuse_fs* fs, const char* path, mode_t mode, + struct fuse_file_info* fi) +{ + if (fs->ops.create == NULL) + return -ENOSYS; + return fs->ops.create(path, mode, fi); +} + + +int +fuse_fs_lock(struct fuse_fs* fs, const char* path, struct fuse_file_info* fi, + int cmd, struct flock* lock) +{ + if (fs->ops.lock == NULL) + return -ENOSYS; + return fs->ops.lock(path, fi, cmd, lock); +} + + +int +fuse_fs_chmod(struct fuse_fs* fs, const char* path, mode_t mode) +{ + if (fs->ops.chmod == NULL) + return -ENOSYS; + return fs->ops.chmod(path, mode); +} + + +int +fuse_fs_chown(struct fuse_fs* fs, const char* path, uid_t uid, gid_t gid) +{ + if (fs->ops.chown == NULL) + return -ENOSYS; + return fs->ops.chown(path, uid, gid); +} + + +int +fuse_fs_truncate(struct fuse_fs* fs, const char* path, off_t size) +{ + if (fs->ops.truncate == NULL) + return -ENOSYS; + return fs->ops.truncate(path, size); +} + + +int +fuse_fs_ftruncate(struct fuse_fs* fs, const char* path, off_t size, + struct fuse_file_info* fi) +{ + if (fs->ops.ftruncate == NULL) + return -ENOSYS; + return fs->ops.ftruncate(path, size, fi); +} + + +int +fuse_fs_utimens(struct fuse_fs* fs, const char* path, + const struct timespec tv[2]) +{ + if (fs->ops.utimens == NULL) + return -ENOSYS; + return fs->ops.utimens(path, tv); +} + + +int +fuse_fs_access(struct fuse_fs* fs, const char* path, int mask) +{ + if (fs->ops.access == NULL) + return -ENOSYS; + return fs->ops.access(path, mask); +} + + +int +fuse_fs_readlink(struct fuse_fs* fs, const char* path, char* buf, size_t len) +{ + if (fs->ops.readlink == NULL) + return -ENOSYS; + return fs->ops.readlink(path, buf, len); +} + + +int +fuse_fs_mknod(struct fuse_fs* fs, const char* path, mode_t mode, dev_t rdev) +{ + if (fs->ops.mknod == NULL) + return -ENOSYS; + return fs->ops.mknod(path, mode, rdev); +} + + +int +fuse_fs_mkdir(struct fuse_fs* fs, const char* path, mode_t mode) +{ + if (fs->ops.mkdir == NULL) + return -ENOSYS; + return fs->ops.mkdir(path, mode); +} + + +int +fuse_fs_setxattr(struct fuse_fs* fs, const char* path, const char* name, + const char* value, size_t size, int flags) +{ + if (fs->ops.setxattr == NULL) + return -ENOSYS; + return fs->ops.setxattr(path, name, value, size, flags); +} + + +int +fuse_fs_getxattr(struct fuse_fs* fs, const char* path, const char* name, + char* value, size_t size) +{ + if (fs->ops.getxattr == NULL) + return -ENOSYS; + return fs->ops.getxattr(path, name, value, size); +} + + +int +fuse_fs_listxattr(struct fuse_fs* fs, const char* path, char* list, size_t size) +{ + if (fs->ops.listxattr == NULL) + return -ENOSYS; + return fs->ops.listxattr(path, list, size); +} + + +int +fuse_fs_removexattr(struct fuse_fs* fs, const char* path, const char* name) +{ + if (fs->ops.removexattr == NULL) + return -ENOSYS; + return fs->ops.removexattr(path, name); +} + + +int +fuse_fs_bmap(struct fuse_fs* fs, const char* path, size_t blocksize, + uint64_t* idx) +{ + if (fs->ops.bmap == NULL) + return -ENOSYS; + return fs->ops.bmap(path, blocksize, idx); +} + + +void +fuse_fs_init(struct fuse_fs* fs, struct fuse_conn_info* conn) +{ + if (fs->ops.init == NULL) + return; + fs->ops.init(conn); +} + + +void +fuse_fs_destroy(struct fuse_fs* fs) +{ + if (fs->ops.destroy != NULL) + fs->ops.destroy(fs->userData); + + delete fs; +} + + +struct fuse_fs* +fuse_fs_new(const struct fuse_operations* ops, size_t opSize, void* userData) +{ + if (sizeof(fuse_operations) < opSize) { + ERROR(("fuse_fs_new(): Client FS built with newer library version!\n")); + return NULL; + } + + fuse_fs* fs = new(std::nothrow) fuse_fs; + if (fs == NULL) + return NULL; + + memset(&fs->ops, 0, sizeof(fuse_operations)); + memcpy(&fs->ops, ops, opSize); + + fs->userData = userData; + + return fs; +} diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/fuse_fs.h b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/fuse_fs.h new file mode 100644 index 0000000000..51014023b8 --- /dev/null +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/fuse_fs.h @@ -0,0 +1,17 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef USERLAND_FS_FUSE_FUSE_FS_H +#define USERLAND_FS_FUSE_FUSE_FS_H + +#include "fuse_api.h" + + +struct fuse_fs { + void* userData; + fuse_operations ops; +}; + + +#endif // USERLAND_FS_FUSE_FUSE_FS_H diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/fuse_main.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/fuse_main.cpp index 9515485950..ed3f00f517 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/fuse_main.cpp +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/fuse_main.cpp @@ -7,22 +7,27 @@ #include -#include +#include "fuse_api.h" +#include "FUSEFileSystem.h" int fuse_main_real(int argc, char* argv[], const struct fuse_operations* op, - size_t op_size, void *user_data) + size_t opSize, void* userData) { -printf("fuse_main_real(%d, %p, %p, %ld, %p)\n", argc, argv, op, op_size, -user_data); - // TODO: Implement! - return 0; +printf("fuse_main_real(%d, %p, %p, %ld, %p)\n", argc, argv, op, opSize, +userData); + + // run the main loop + status_t error = FUSEFileSystem::GetInstance()->FinishInitClientFS(op, + opSize, userData); + + return error == B_OK ? 0 : 1; } int -fuse_is_lib_option(const char *opt) +fuse_is_lib_option(const char* opt) { printf("fuse_is_lib_option(\"%s\")\n", opt); // TODO: Implement!