From 18128d58dca3e03fb850fc52d1b5f7992d6dd02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Sun, 5 Jul 2009 10:36:30 +0000 Subject: [PATCH] Patch by Raghuram Nagireddy: Re-use the kernel emulation of the FS shell to implement a FUSE module for BFS. In theory, it could use any Haiku file system implementation just like the FS shell itself, but currently only the BFS code supports being compiled as an FS shell module. The build target is bfs_fuse On Ubuntu, you would do this: sudo apt-get install libfuse-dev jam \bfs_fuse mkdir path/to/moundPoint path/to/bfs_fuse /dev/sdaX path/to/mountPoint And that will mount your BFS volume at /dev/sdaX read/write. As this is new code, you should be careful with your data and expect potential data loss. Not that I don't trust Raghu's code, but a warning is always better with such things. So far, my tests have been successful and it is great to finally have write support for BFS in Linux! Thanks a lot, Raghu, for your great work! git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31409 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/tools/bfs_shell/Jamfile | 16 +- src/tools/fs_shell/Jamfile | 12 +- src/tools/fs_shell/fuse.cpp | 438 ++++++++++++++++++++++++++++++++++ src/tools/fs_shell/string.cpp | 10 +- 4 files changed, 468 insertions(+), 8 deletions(-) create mode 100644 src/tools/fs_shell/fuse.cpp diff --git a/src/tools/bfs_shell/Jamfile b/src/tools/bfs_shell/Jamfile index 4783cf02f2..e6cf19ba33 100644 --- a/src/tools/bfs_shell/Jamfile +++ b/src/tools/bfs_shell/Jamfile @@ -32,8 +32,7 @@ if ! $(HOST_PLATFORM_BEOS_COMPATIBLE) { UsePrivateHeaders fs_shell ; -BuildPlatformMain bfs_shell - : +local bfsSource = bfs_disk_system.cpp BlockAllocator.cpp BPlusTree.cpp @@ -47,7 +46,20 @@ BuildPlatformMain bfs_shell Volume.cpp kernel_interface.cpp +; + +BuildPlatformMain bfs_shell + : + $(bfsSource) : fs_shell.a $(libHaikuCompat) $(HOST_LIBSUPC++) $(HOST_LIBSTDC++) $(HOST_LIBROOT) $(fsShellCommandLibs) ; + +BuildPlatformMain bfs_fuse + : + $(bfsSource) + + : fuse_module.a $(libHaikuCompat) $(HOST_LIBSUPC++) $(HOST_LIBSTDC++) + $(HOST_LIBROOT) $(fsShellCommandLibs) fuse +; diff --git a/src/tools/fs_shell/Jamfile b/src/tools/fs_shell/Jamfile index 72c0e01f4b..b084796071 100644 --- a/src/tools/fs_shell/Jamfile +++ b/src/tools/fs_shell/Jamfile @@ -31,7 +31,7 @@ if $(HOST_PLATFORM_BEOS_COMPATIBLE) { fsShellCommandLibs = $(HOST_NETWORK_LIBS) ; } -BuildPlatformStaticLibrary fs_shell.a : +local kernelEmulationSources = atomic.cpp block_cache.cpp byte_order.cpp @@ -63,7 +63,17 @@ BuildPlatformStaticLibrary fs_shell.a : uio.cpp unistd.cpp vfs.cpp +; +BuildPlatformStaticLibrary fuse_module.a : + $(kernelEmulationSources) + $(externalCommandsSources) + + fuse.cpp +; + +BuildPlatformStaticLibrary fs_shell.a : + $(kernelEmulationSources) $(externalCommandsSources) fssh.cpp diff --git a/src/tools/fs_shell/fuse.cpp b/src/tools/fs_shell/fuse.cpp new file mode 100644 index 0000000000..b31365dbe1 --- /dev/null +++ b/src/tools/fs_shell/fuse.cpp @@ -0,0 +1,438 @@ +/* + * Copyright 2009, Raghuram Nagireddy . + * Distributed under the terms of the MIT License. + */ + +#define FUSE_USE_VERSION 27 + +#include +#include +#include + +#include "fssh.h" + +#include "driver_settings.h" +#include "external_commands.h" +#include "fd.h" +#include "fssh_dirent.h" +#include "fssh_errno.h" +#include "fssh_errors.h" +#include "fssh_fcntl.h" +#include "fssh_fs_info.h" +#include "fssh_module.h" +#include "fssh_node_monitor.h" +#include "fssh_stat.h" +#include "fssh_string.h" +#include "fssh_type_constants.h" +#include "module.h" +#include "syscalls.h" +#include "vfs.h" + + +extern fssh_module_info *modules[]; + +extern fssh_file_system_module_info gRootFileSystem; + +namespace FSShell { + +const char* kMountPoint = "/myfs"; + +static mode_t sUmask = 0022; + +#define PRINTD(x) fprintf(stderr, x) + + +static fssh_status_t +init_kernel() +{ + fssh_status_t error; + + // init module subsystem + error = module_init(NULL); + if (error != FSSH_B_OK) { + fprintf(stderr, "module_init() failed: %s\n", fssh_strerror(error)); + return error; + } + + // init driver settings + error = driver_settings_init(); + if (error != FSSH_B_OK) { + fprintf(stderr, "initializing driver settings failed: %s\n", + fssh_strerror(error)); + return error; + } + + // register built-in modules, i.e. the rootfs and the client FS + register_builtin_module(&gRootFileSystem.info); + for (int i = 0; modules[i]; i++) + register_builtin_module(modules[i]); + + // init VFS + error = vfs_init(NULL); + if (error != FSSH_B_OK) { + fprintf(stderr, "initializing VFS failed: %s\n", fssh_strerror(error)); + return error; + } + + // init kernel IO context + gKernelIOContext = (io_context*)vfs_new_io_context(NULL); + if (!gKernelIOContext) { + fprintf(stderr, "creating IO context failed!\n"); + return FSSH_B_NO_MEMORY; + } + + // mount root FS + fssh_dev_t rootDev = _kern_mount("/", NULL, "rootfs", 0, NULL, 0); + if (rootDev < 0) { + fprintf(stderr, "mounting rootfs failed: %s\n", fssh_strerror(rootDev)); + return rootDev; + } + + // set cwd to "/" + error = _kern_setcwd(-1, "/"); + if (error != FSSH_B_OK) { + fprintf(stderr, "setting cwd failed: %s\n", fssh_strerror(error)); + return error; + } + + // create mount point for the client FS + error = _kern_create_dir(-1, kMountPoint, 0775); + if (error != FSSH_B_OK) { + fprintf(stderr, "creating mount point failed: %s\n", + fssh_strerror(error)); + return error; + } + + return FSSH_B_OK; +} + +static void fromFsshStatToStat (struct fssh_stat *f_stbuf, struct stat *stbuf) +{ + stbuf->st_dev = f_stbuf->fssh_st_dev; + stbuf->st_ino = f_stbuf->fssh_st_ino; + stbuf->st_mode = f_stbuf->fssh_st_mode; + stbuf->st_nlink = f_stbuf->fssh_st_nlink; + stbuf->st_uid = f_stbuf->fssh_st_uid; + stbuf->st_gid = f_stbuf->fssh_st_gid; + stbuf->st_rdev = f_stbuf->fssh_st_rdev; + stbuf->st_size = f_stbuf->fssh_st_size; + stbuf->st_blksize = f_stbuf->fssh_st_blksize; + stbuf->st_blocks = f_stbuf->fssh_st_blocks; + stbuf->st_atime = f_stbuf->fssh_st_atime; + stbuf->st_mtime = f_stbuf->fssh_st_mtime; + stbuf->st_ctime = f_stbuf->fssh_st_ctime; +} + +#define _ERR(x) (-1*fssh_to_host_error(x)) + + +// pragma mark - FUSE functions + + +//extern "C" { + + +int +fuse_getattr(const char* path, struct stat* stbuf) +{ + PRINTD("##getattr\n"); + struct fssh_stat f_stbuf; + fssh_status_t status = _kern_read_stat(-1, path, false, &f_stbuf, + sizeof(f_stbuf)); + fromFsshStatToStat(&f_stbuf, stbuf); + printf("GETATTR returned: %d\n", status); + return _ERR(status); +} + + +static int +fuse_access(const char* path, int mask) +{ + PRINTD("##access\n"); + return _ERR(_kern_access(path, mask)); +} + + +static int +fuse_readlink(const char* path, char* buffer, size_t size) +{ + PRINTD("##readlink\n"); + size_t n_size = size - 1; + fssh_status_t st = _kern_read_link(-1, path, buffer, &n_size); + if (st >= FSSH_B_OK) + buffer[n_size] = '\0'; + return _ERR(st); +} + + +static int +fuse_readdir(const char* path, void* buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info* fi) +{ + PRINTD("##readdir\n"); + int dfp = _kern_open_dir(-1, path); + if (dfp < FSSH_B_OK) + return _ERR(dfp); + + fssh_ssize_t entriesRead = 0; + struct fssh_stat f_st; + struct stat st; + char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH]; + fssh_dirent* dirEntry = (fssh_dirent*)buffer; + while ((entriesRead = _kern_read_dir(dfp, dirEntry, + sizeof(buffer), 1)) == 1) { + fssh_memset(&st, 0, sizeof(st)); + fssh_memset(&f_st, 0, sizeof(f_st)); + fssh_status_t status = _kern_read_stat(dfp, dirEntry->d_name, + false, &f_st, sizeof(f_st)); + if (status >= FSSH_B_OK) { + fromFsshStatToStat(&f_st, &st); + if (filler(buf, dirEntry->d_name, &st, 0)) + break; + } + } + _kern_close(dfp); + //TODO: check _kern_close + return 0; +} + + +static int +fuse_mknod(const char* path, mode_t mode, dev_t rdev) +{ + PRINTD("##mknod\n"); + if (S_ISREG(mode)) { + int fd = _kern_open(-1, path, + FSSH_O_CREAT | FSSH_O_EXCL | FSSH_O_WRONLY, mode); + if (fd >= FSSH_B_OK) + return _ERR(_kern_close(fd)); + return _ERR(fd); + } else if (S_ISFIFO(mode)) + return _ERR(FSSH_EINVAL); + else + return _ERR(FSSH_EINVAL); +} + + +static int +fuse_mkdir(const char* path, mode_t mode) +{ + PRINTD("##mkdir\n"); + return _ERR(_kern_create_dir(-1, path, mode)); +} + + +static int +fuse_symlink(const char* from, const char* to) +{ + PRINTD("##symlink\n"); + return _ERR(_kern_create_symlink(-1, from, to, + FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO)); +} + + +static int +fuse_unlink(const char* path) +{ + PRINTD("##unlink\n"); + return _ERR(_kern_unlink(-1, path)); +} + + +static int +fuse_rmdir(const char* path) +{ + PRINTD("##rmdir\n"); + return _ERR(_kern_remove_dir(-1, path)); +} + + +static int +fuse_rename(const char* from, const char* to) +{ + PRINTD("##rename\n"); + return _ERR(_kern_rename(-1, from, -1, to)); +} + + +static int +fuse_link(const char* from, const char* to) +{ + PRINTD("##link\n"); + return _ERR(_kern_create_link(from, to)); +} + + +static int +fuse_chmod(const char* path, mode_t mode) +{ + PRINTD("##chmod\n"); + fssh_struct_stat st; + st.fssh_st_mode = mode; + return _ERR(_kern_write_stat(-1, path, false, &st, sizeof(st), + FSSH_B_STAT_MODE)); +} + + +static int +fuse_chown(const char* path, uid_t uid, gid_t gid) +{ + PRINTD("##chown\n"); + fssh_struct_stat st; + st.fssh_st_uid = uid; + st.fssh_st_gid = gid; + return _ERR(_kern_write_stat(-1, path, false, &st, sizeof(st), + FSSH_B_STAT_UID|FSSH_B_STAT_GID)); +} + + +static int +fuse_open(const char* path, struct fuse_file_info* fi) +{ + PRINTD("##open\n"); + // TODO: Do we have a syscall similar to the open syscall in linux which + // takes only two args: path and flags with no mask/perms? + int fd = _kern_open(-1, path, fi->flags, + (FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) & ~sUmask); + _kern_close(fd); + if (fd < FSSH_B_OK) + return _ERR(fd); + else + return 0; +} + + +static int +fuse_read(const char* path, char* buf, size_t size, off_t offset, + struct fuse_file_info* fi) +{ + PRINTD("##read\n"); + int fd = _kern_open(-1, path, FSSH_O_RDONLY, + (FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) & ~sUmask); + if (fd < FSSH_B_OK) + return _ERR(fd); + + int res = _kern_read(fd, offset, buf, size); + _kern_close(fd); + if (res < FSSH_B_OK) + res = _ERR(res); + return res; +} + + +static int +fuse_write(const char* path, const char* buf, size_t size, off_t offset, + struct fuse_file_info* fi) +{ + PRINTD("##write\n"); + int fd = _kern_open(-1, path, FSSH_O_WRONLY, + (FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) & ~sUmask); + if (fd < FSSH_B_OK) + return _ERR(fd); + + int res = _kern_write(fd, offset, buf, size); + _kern_close(fd); + if (res < FSSH_B_OK) + res = _ERR(res); + return res; +} + + +static void +fuse_destroy(void* priv_data) +{ + _kern_sync(); +} + + +//} // extern "C" { + + +struct fuse_operations fuse_cmds; + + +static void +initialiseFuseOps(struct fuse_operations* fuse_cmds) +{ + fuse_cmds->getattr = fuse_getattr; + fuse_cmds->access = fuse_access; + fuse_cmds->readlink = fuse_readlink; + fuse_cmds->readdir = fuse_readdir; + fuse_cmds->mknod = fuse_mknod; + fuse_cmds->mkdir = fuse_mkdir; + fuse_cmds->symlink = fuse_symlink; + fuse_cmds->unlink = fuse_unlink; + fuse_cmds->rmdir = fuse_rmdir; + fuse_cmds->rename = fuse_rename; + fuse_cmds->link = fuse_link; + fuse_cmds->chmod = fuse_chmod; + fuse_cmds->chown = fuse_chown; + fuse_cmds->truncate = NULL; + fuse_cmds->utimens = NULL; + fuse_cmds->open = fuse_open; + fuse_cmds->read = fuse_read; + fuse_cmds->write = fuse_write; + fuse_cmds->statfs = NULL; + fuse_cmds->release = NULL; + fuse_cmds->fsync = NULL; + fuse_cmds->destroy = fuse_destroy; +} + + +static int +fssh_fuse_session(const char* device, const char* mntPoint, const char* fsName) +{ + fssh_dev_t fsDev = _kern_mount(kMountPoint, device, fsName, 0, NULL, 0); + if (fsDev < 0) { + fprintf(stderr, "Error: Mounting FS failed: %s\n", + fssh_strerror(fsDev)); + return 1; + } + const char* argv[5]; + argv[0] = (const char*)"bfs_shell"; + argv[1] = mntPoint; + argv[2] = (const char*)"-d"; + argv[3] = (const char*)"-s"; + initialiseFuseOps(&fuse_cmds); + return fuse_main(4, (char**)argv, &fuse_cmds, NULL); +} + + +} // namespace FSShell + + +using namespace FSShell; + + +static void +print_usage_and_exit(const char* binName) +{ + fprintf(stderr,"Usage: %s \n", binName); + exit(1); +} + + +int +main(int argc, const char* const* argv) +{ + if (argc < 2) + print_usage_and_exit(argv[0]); + + const char* device = argv[1]; + const char* mntPoint = argv[2]; + + if (!modules[0]) { + fprintf(stderr, "Error: Couldn't find FS module!\n"); + return 1; + } + + fssh_status_t error = init_kernel(); + if (error != FSSH_B_OK) { + fprintf(stderr, "Error: Initializing kernel failed: %s\n", + fssh_strerror(error)); + return error; + } + const char* fsName = modules[0]->name; + return fssh_fuse_session(device, mntPoint, fsName); +} + diff --git a/src/tools/fs_shell/string.cpp b/src/tools/fs_shell/string.cpp index 3194dba634..fcce045f46 100644 --- a/src/tools/fs_shell/string.cpp +++ b/src/tools/fs_shell/string.cpp @@ -13,7 +13,7 @@ void* fssh_memchr(const void *source, int value, fssh_size_t length) { - return memchr(source, value, length); + return memchr((void*)source, value, length); } @@ -106,21 +106,21 @@ fssh_strncmp(const char *string1, const char *string2, fssh_size_t length) char* fssh_strchr(const char *string, int character) { - return strchr(string, character); + return strchr((char*)string, character); } char* fssh_strrchr(const char *string, int character) { - return strrchr(string, character); + return strrchr((char*)string, character); } char* fssh_strstr(const char *string, const char *searchString) { - return strstr(string, searchString); + return strstr((char*)string, searchString); } @@ -135,7 +135,7 @@ fssh_strchrnul(const char *string, int character) char* fssh_strpbrk(const char *string, const char *set) { - return strpbrk(string, set); + return strpbrk((char*)string, set); }