From 1b43c4965dc5bcee898181f1c38ebc642e521fa8 Mon Sep 17 00:00:00 2001 From: Les De Ridder Date: Sun, 16 Oct 2016 05:30:52 +0200 Subject: [PATCH] Add dfuse --- dfuse | 1 - dfuse/.gitignore | 7 + dfuse/LICENSE | 28 +++ dfuse/Makefile | 82 ++++++++ dfuse/PATENTS | 23 +++ dfuse/README.md | 132 ++++++++++++ dfuse/dub.json | 20 ++ dfuse/example/simplefs.d | 63 ++++++ dfuse/source/c/fuse/common.d | 115 +++++++++++ dfuse/source/c/fuse/fuse.d | 104 ++++++++++ dfuse/source/dfuse/fuse.d | 386 +++++++++++++++++++++++++++++++++++ 11 files changed, 960 insertions(+), 1 deletion(-) delete mode 160000 dfuse create mode 100644 dfuse/.gitignore create mode 100644 dfuse/LICENSE create mode 100644 dfuse/Makefile create mode 100644 dfuse/PATENTS create mode 100644 dfuse/README.md create mode 100644 dfuse/dub.json create mode 100644 dfuse/example/simplefs.d create mode 100644 dfuse/source/c/fuse/common.d create mode 100644 dfuse/source/c/fuse/fuse.d create mode 100644 dfuse/source/dfuse/fuse.d diff --git a/dfuse b/dfuse deleted file mode 160000 index 8daf1d4..0000000 --- a/dfuse +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8daf1d4fffbcd1415b963ed5a43fae5e4bf06464 diff --git a/dfuse/.gitignore b/dfuse/.gitignore new file mode 100644 index 0000000..b43e5eb --- /dev/null +++ b/dfuse/.gitignore @@ -0,0 +1,7 @@ +*.o +*.so +*.dylib +*.a +simplefs +*.swp +*~ diff --git a/dfuse/LICENSE b/dfuse/LICENSE new file mode 100644 index 0000000..fcdfae1 --- /dev/null +++ b/dfuse/LICENSE @@ -0,0 +1,28 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +For dfuse software + +Copyright (c) 2014, Facebook, Inc. +All rights reserved. + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/dfuse/Makefile b/dfuse/Makefile new file mode 100644 index 0000000..daef3a7 --- /dev/null +++ b/dfuse/Makefile @@ -0,0 +1,82 @@ +# +# Copyright (c) 2014, Facebook, Inc. +# All rights reserved. +# +# This source code is licensed under the Boost-style license found in the +# LICENSE file in the root directory of this source tree. An additional grant +# of patent rights can be found in the PATENTS file in the same directory. +# +VERSION=0.4.0-dev +INTERNAL_VERSION=0004 + +product=libdfuse-$(VERSION) + +PREFIX=/usr/local +DMD=dmd +DMDFLAGS= + +sources = source/c/fuse/fuse.d source/c/fuse/common.d source/dfuse/fuse.d +uname := $(shell uname -s) + +# Define variables and buildmodes depending on ENABLE_DEBUG, ENABLE_64BIT and +# operating system. +buildmode=release +ifeq ($(ENABLE_DEBUG),1) + MODE=-debug -g + buildmode=debug +else + MODE=-release -O -inline + buildmode=release +endif + +bitmode=64 +ifeq ($(uname),Darwin) +ifeq ($(ENABLE_64BIT),1) + MODE+=-version=DARWIN_USE_64_BIT_INODE + LIBS=-L-losxfuse + bitmde=64 +else + LIBS=-L-losxfuse_i32 + bitmode=32 +endif +endif + +ifeq ($(uname),Linux) + LIBS=-L-lfuse + bitmode=64 +endif + +# Define build directories and main target for libs +builddir=build/$(buildmode)/$(bitmode) + +ifeq ($(uname),Linux) + artifact=$(builddir)/$(product).so +endif +ifeq ($(uname),Darwin) + artifact=$(builddir)/$(product).dylib +endif + +all:: dfuse + +$(builddir): + mkdir -p $(builddir) + +$(builddir)/$(product).so: $(sources) + $(DMD) -w $(MODE) -shared $(LIBS) -version=$(INTERNAL_VERSION) -of$@ $(sources) + +$(builddir)/$(product).dylib: $(sources) + $(DMD) -w $(MODE) -shared $(LIBS) -version=$(INTERNAL_VERSION) -of$@ $(sources) + +simplefs: example/simplefs.d $(sources) + $(DMD) -w -debug -g $(LIBS) -of$@ example/simplefs.d $(sources) + +examples: simplefs + +dfuse: $(artifact) + +clean: + @(rm simplefs 2>/dev/null || exit 0) + @(rm -r $(artifact) || exit 0) + @(rm -rf build/ || exit 0) + +.PHONY: dfuse all clean examples diff --git a/dfuse/PATENTS b/dfuse/PATENTS new file mode 100644 index 0000000..8495034 --- /dev/null +++ b/dfuse/PATENTS @@ -0,0 +1,23 @@ +Additional Grant of Patent Rights + +"Software" means the dfuse software distributed by Facebook, Inc. + +Facebook hereby grants you a perpetual, worldwide, royalty-free, +non-exclusive, irrevocable (subject to the termination provision below) +license under any rights in any patent claims owned by Facebook, to make, +have made, use, sell, offer to sell, import, and otherwise transfer the +Software. For avoidance of doubt, no license is granted under Facebook’s +rights in any patent claims that are infringed by (i) modifications to the +Software made by you or a third party, or (ii) the Software in combination +with any software or other technology provided by you or a third party. + +The license granted hereunder will terminate, automatically and without +notice, for anyone that makes any claim (including by filing any lawsuit, +assertion or other action) alleging (a) direct, indirect, or contributory +infringement or inducement to infringe any patent: (i) by Facebook or any +of its subsidiaries or affiliates, whether or not such claim is related +to the Software, (ii) by any party if such claim arises in whole or in +part from any software, product or service of Facebook or any of its +subsidiaries or affiliates, whether or not such claim is related to the +Software, or (iii) by any party relating to the Software; or (b) that +any right in any patent claim of Facebook is invalid or unenforceable. diff --git a/dfuse/README.md b/dfuse/README.md new file mode 100644 index 0000000..c279d87 --- /dev/null +++ b/dfuse/README.md @@ -0,0 +1,132 @@ +# dfuse +*dfuse* is a [D language binding](http://dlang.org) for the high level +[fuse](http://fuse.sourceforge.net) library. It allows to write a fuse +filesystem for Linux or Mac OS (using [osxfuse](http://osxfuse.github.io)) in D. + +Fuse is a library and kernel extension to write filesystems in userspace. These +filesystems are easy to implement and have access to userland components like +HTTP libraries, etc. For more information about fuse see: http://fuse.sourceforge.net. + +## Examples +A simple filesystems implementing a directory listing can be found in the [examples/](https://github.com/facebook/dfuse/tree/master/example) directory. +You can build the examples using: +```Shell +$ make examples +$ mkdir /mnt/simplefs +$ ./simplefs /mnt/simplefs +``` + +## Implementing a filesystem +dfuse provides a high level interface for libfuse. To implement a filesystem, extend the *Operations* class in the *dfuse.fuse* module: +```D +import dfuse.fuse; + +class MyFS : Operations +{ + override void getattr(const(char)[] path, ref stat_t s) + { + /* implementation */ + throw new FuseException(EOPNOTSUPP); + } + + override string[] readdir(const(char)[] path) + { + return [/*...list of files...*/]; + } + + override ulong read(const(char)[] path, ubyte[] buf, ulong offset) + { + /* implementation */ + throw new FuseException(EOPNOTSUPP); + } +} +``` + +A minimal filesystem implements `Operations.getattr()`, `Operations.readdir()`, `Operations.read()`. See [dfuse/fuse.d](https://github.com/facebook/dfuse/blob/master/dfuse/fuse.d) for implementation specific details. + +To mount a filesystem use a Fuse object and call mount: +```D +import dfuse.fuse; + +int main(string[] args) +{ + /* foreground=true, threading=false */ + auto fs = new Fuse("MyFS", true, false); + fs.mount(new MyFS(), "/mnt", ["allow_other"]); +} +``` + +Error conditions are handled by throwin a *FuseException* with the appropriate error number. See `man 3 errno` for more information about errno. + +## Requirements +dfuse requires: +* Mac OS X or Linux +* fuse >= 2.8.0 or [osxfuse](http://osxfuse.github.io/) >= 2.6.0 +* DMD/Druntime/Phobos >= 2.065 + +## Building dfuse +dfuse comes with a standard makefile that assumes that DMD (the D-compiler) is +in your $PATH. + +### Linux +In order to compile dfuse on Linux: +```Shell +$ make dfuse +or +$ make dfuse ENABLE_DEBUG=1 +to build a debug version +``` + +### MacOS +MacOS supports two inode sizes which are both supported by OSXfuse, however when +compiling dfuse you have to be aware which OSXfuse should be linked. + +By default dfuse is trying to build with a 32bit inode size and link against +osxfuse_i32 which is part of OSXfuse for compatibility. Please note that your +library itself will still be 64bit on a 64bit system. The setting only affects +the size of the inode. + +To build just run +```Shell +$ make dfuse +``` + +If you want to compile with 64bit inodes you need a at least DMD, Druntime, +Phobos in version 2.066: +```Shell +$ make dfuse ENABLE_64BIT=1 +``` + +### Dub +dfuse comes with experimental support for [dub](http://code.dlang.org/), a package manager for D. See the dub documentation how to build and use dub. + +## Installing dfuse +At the moment the dfuse makefile doesn't support an install target. It is +recommended to just include the library in a project at this point. + +## How dfuse works +dfuse is a simple D wrapper. It exposes a lowelevel interface to the libfuse C +functions in c/fuse/fuse.d. The lowlevel interface preserves C types. + +A highlevel interface is provided by fs/fuse.d. The D interface initializes fuse filsystems operations structure and installs it's own handlers. Every dfuse handler converts C +types to D types and is trapping FuseExceptions used for error handling. The +handlers keep track of the initialized Operations object and call the +appropriate method once types are converted and pass the result into the D +layer. + +The user facing interface is the *Operations* class in fs/fuse.d. It provides +default implementations for all handlers and every method can be invidually +overwritten to provide an interface. + +## Issues and Bugs +If you encounter issues or bugs with dfuse, please file an issue on [github](https://github.com/facebook/dfuse/issues). Please ensure that you maintain a constructive feedback atmosphere and if possible attach a reproduction step. If you have any questions, feel free to write to the D mailinglist or ask in IRC. + +Pull requests are highly appreciated! + +## Join the dfuse community +* Website: https://github.com/facebook/dfuse/wiki +* Mailing list: [The D Mailinglist](http://lists.puremagic.com/cgi-bin/mailman/listinfo/digitalmars-d) +* irc: irc.freenode.net #d + +## License +dfuse is Boost-licensed. We also provide an additional patent grant. diff --git a/dfuse/dub.json b/dfuse/dub.json new file mode 100644 index 0000000..1d35d9a --- /dev/null +++ b/dfuse/dub.json @@ -0,0 +1,20 @@ +{ + "name": "dfuse", + "description": "A D binding for libfuse", + "authors": ["David Soria Parra"], + "copyright": "Copyright (c) 2014, Facebook, Inc.", + "homepage": "http://github.com/facebook/dfuse", + "license": "BSL-1.0", + "dependencies": {}, + "configurations": [{ + "name": "dfuse-osx", + "targetType": "library", + "platforms": ["osx"], + "libs": ["osxfuse_i32"] + }, { + "name": "dfuse-linux", + "targetType": "library", + "platforms": ["linux"], + "libs": ["fuse"] + }] +} diff --git a/dfuse/example/simplefs.d b/dfuse/example/simplefs.d new file mode 100644 index 0000000..3c9554d --- /dev/null +++ b/dfuse/example/simplefs.d @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the Boost-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +import dfuse.fuse; + +import std.algorithm, std.conv, std.stdio; + +/** + * A simple directory listing using dfuse + */ +class SimpleFS : Operations +{ + override void getattr(const(char)[] path, ref stat_t s) + { + if (path == "/") + { + s.st_mode = S_IFDIR | octal!755; + s.st_size = 0; + return; + } + + if (path.among("/a", "/b")) + { + s.st_mode = S_IFREG | octal!644; + s.st_size = 42; + return; + } + + throw new FuseException(errno.ENOENT); + } + + override string[] readdir(const(char)[] path) + { + if (path == "/") + { + return ["a", "b"]; + } + + throw new FuseException(errno.ENOENT); + } +} + +int main(string[] args) +{ + if (args.length != 2) + { + stderr.writeln("simplefs "); + return -1; + } + + stdout.writeln("mounting simplefs"); + + auto fs = new Fuse("SimpleFS", true, false); + fs.mount(new SimpleFS(), args[1], []); + + return 0; +} diff --git a/dfuse/source/c/fuse/common.d b/dfuse/source/c/fuse/common.d new file mode 100644 index 0000000..dcb945d --- /dev/null +++ b/dfuse/source/c/fuse/common.d @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the Boost-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +module c.fuse.common; + +import std.stdint; + +extern (System) { + static assert(fuse_conn_info.sizeof == 128); + struct fuse_conn_info + { + /** + * Major version of the protocol (read-only) + */ + uint proto_major; + + /** + * Minor version of the protocol (read-only) + */ + uint proto_minor; + + /** + * Is asynchronous read supported (read-write) + */ + uint async_read; + + /** + * Maximum size of the write buffer + */ + uint max_write; + + /** + * Maximum readahead + */ + uint max_readahead; + + /** + * Capability flags, that the kernel supports + */ + uint capable; + + /** + * Capability flags, that the filesystem wants to enable + */ + uint want; + + /** + * Maximum number of backgrounded requests + */ + uint max_background; + + /** + * Kernel congestion threshold parameter + */ + uint congestion_threshold; + + /** + * For future use. + */ + uint[23] reserved; + } + + static assert(fuse_file_info.sizeof == 64); + struct fuse_file_info + { + /** Open flags. Available in open() and release() */ + int flags; + + /** Old file handle, don't use */ + ulong fh_old; + + /** In case of a write operation indicates if this was caused by a + writepage */ + int writepage; + + /** Can be filled in by open, to use direct I/O on this file. + Introduced in version 2.4 */ + uint direct_io = 1; + + /** Can be filled in by open, to indicate, that cached file data + need not be invalidated. Introduced in version 2.4 */ + uint keep_cache = 1; + + /** Indicates a flush operation. Set in flush operation, also + maybe set in highlevel lock operation and lowlevel release + operation. Introduced in version 2.6 */ + uint flush = 1; + + /** Can be filled in by open, to indicate that the file is not + seekable. Introduced in version 2.8 */ + uint nonseekable = 1; + + /* Indicates that flock locks for this file should be + released. If set, lock_owner shall contain a valid value. + May only be set in ->release(). Introduced in version + 2.9 */ + uint flock_release = 1; + + /** Padding. Do not use*/ + uint padding = 27; + + /** File handle. May be filled in by filesystem in open(). + Available in all other file operations */ + uint64_t fh; + + /** Lock owner id. Available in locking operations and flush */ + uint64_t lock_owner; + } +} diff --git a/dfuse/source/c/fuse/fuse.d b/dfuse/source/c/fuse/fuse.d new file mode 100644 index 0000000..bc8c47f --- /dev/null +++ b/dfuse/source/c/fuse/fuse.d @@ -0,0 +1,104 @@ +/* + * From: https://code.google.com/p/dutils/ + * + * Licensed under the Apache License 2.0. See + * http://www.apache.org/licenses/LICENSE-2.0 + */ +module c.fuse.fuse; +public import c.fuse.common; + +import std.stdint; +import core.sys.posix.sys.statvfs; +import core.sys.posix.fcntl; +import core.sys.posix.time; +import core.sys.posix.utime; + +extern (System) +{ + struct fuse; + + struct fuse_pollhandle; + struct fuse_conn_info; /* temporary anonymous struct */ + struct fuse_dirhandle; + + alias fuse_dirh_t = fuse_dirhandle*; + alias flock _flock; + alias fuse_fill_dir_t = + int function(void *buf, char *name, stat_t *stbuf, off_t off); + alias fuse_dirfil_t = + int function(fuse_dirh_t, const char *name, int type, ino_t ino); + + struct fuse_operations + { + int function(const char*, stat_t*) getattr; + int function(const char*, char *, size_t) readlink; + int function(const char*, fuse_dirh_t, fuse_dirfil_t) getdir; + int function(const char*, mode_t, dev_t) mknod; + int function(const char*, mode_t) mkdir; + int function(const char*) unlink; + int function(const char*) rmdir; + int function(const char*, char*) symlink; + int function(const char*, char*) rename; + int function(const char*, char*) link; + int function(const char*, mode_t) chmod; + int function(const char*, uid_t, gid_t) chown; + int function(const char*, off_t) truncate; + int function(const char*, utimbuf *) utime; + int function(const char*, fuse_file_info *) open; + int function(const char*, char*, size_t, off_t, fuse_file_info*) read; + int function(const char*, char*, size_t, off_t, fuse_file_info*) write; + int function(const char*, statvfs_t*) statfs; + int function(const char*, fuse_file_info*) flush; + int function(const char*, fuse_file_info*) release; + int function(const char*, int, fuse_file_info*) fsync; + int function(const char*, char*, char*, size_t, int) setxattr; + int function(const char*, char*, char*, size_t) getxattr; + int function(const char*, char*, size_t) listxattr; + int function(const char*, char*) removexattr; + int function(const char*, fuse_file_info*) opendir; + int function(const char*, void*, fuse_fill_dir_t, off_t, + fuse_file_info*) readdir; + int function(const char*, fuse_file_info*) releasedir; + int function(const char*, int, fuse_file_info*) fsyncdir; + void* function(fuse_conn_info* conn) init; + void function(void*) destroy; + int function(const char*, int) access; + int function(const char*, mode_t, fuse_file_info*) create; + int function(const char*, off_t, fuse_file_info*) ftruncate; + int function(const char*, stat_t*, fuse_file_info*) fgetattr; + int function(const char*, fuse_file_info*, int cmd, _flock*) lock; + int function(const char*, const timespec) utimens; + int function(const char*, size_t, uint64_t*) bmap; + uint flag_nullpath_ok = 1; + uint flag_reserved = 31; + int function(const char*, int, void*, fuse_file_info*, uint, void*) + ioctl; + int function(const char*, fuse_file_info*, fuse_pollhandle*, uint*) + poll; + } + + struct fuse_context + { + /** Pointer to the fuse object */ + fuse* _fuse; + + uid_t uid; // User ID of the calling process + gid_t gid; // Group ID of the calling process + pid_t pid; // Thread ID of the calling process + + void* private_data; // Private filesystem data + + mode_t umask; // Umask of the calling process (introduced in version 2.8) + } + + fuse_context* fuse_get_context(); + int fuse_main_real(int argc, char** argv, fuse_operations* op, + size_t op_size, void* user_data); +} + + +/* mappping of the fuse_main macro in fuse.h */ +int fuse_main(int argc, char** argv, fuse_operations* op, void* user_data) +{ + return fuse_main_real(argc, argv, op, fuse_operations.sizeof, user_data); +} diff --git a/dfuse/source/dfuse/fuse.d b/dfuse/source/dfuse/fuse.d new file mode 100644 index 0000000..8048b03 --- /dev/null +++ b/dfuse/source/dfuse/fuse.d @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the Boost-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +module dfuse.fuse; + +/* reexport stat_t */ +public import core.sys.posix.fcntl; + +import std.algorithm; +import std.array; +import std.conv; +import std.stdio; +import std.string; +import errno = core.stdc.errno; +import core.stdc.string; + +import c.fuse.fuse; + +import core.thread : thread_attachThis, thread_detachThis; +import core.sys.posix.pthread; + +/** + * libfuse is handling the thread creation and we cannot hook into it. However + * we need to make the GC aware of the threads. So for any call to a handler + * we check if the current thread is attached and attach it if necessary. + */ +private int threadAttached = false; +private pthread_cleanup cleanup; + +extern(C) void detach(void* ptr) nothrow +{ + import std.exception; + collectException(thread_detachThis()); +} + +private void attach() +{ + if (!threadAttached) + { + thread_attachThis(); + cleanup.push(&detach, cast(void*) null); + threadAttached = true; + } +} + +/** + * A template to wrap C function calls and support exceptions to indicate + * errors. + * + * The templates passes the Operations object to the lambda as the first + * arguemnt. + */ +private auto call(alias fn)() +{ + attach(); + auto t = cast(Operations*) fuse_get_context().private_data; + try + { + return fn(*t); + } + catch (FuseException fe) + { + /* errno is used to indicate an error to libfuse */ + errno.errno = fe.errno; + return -fe.errno; + } + catch (Exception e) + { + (*t).exception(e); + return -errno.EIO; + } +} + +/* C calling convention compatible function to hand into libfuse which wrap + * the call to our Operations object. + * + * Note that we convert our * char pointer to an array using the + * ptr[0..len] syntax. + */ +extern(System) +{ + private int dfuse_access(const char* path, int mode) + { + return call!( + (Operations t) + { + if(t.access(path[0..path.strlen], mode)) + { + return 0; + } + return -1; + })(); + } + + private int dfuse_getattr(const char* path, stat_t* st) + { + return call!( + (Operations t) + { + t.getattr(path[0..path.strlen], *st); + return 0; + })(); + } + + private int dfuse_readdir(const char* path, void* buf, + fuse_fill_dir_t filler, off_t offset, fuse_file_info* fi) + { + return call!( + (Operations t) + { + foreach(file; t.readdir(path[0..path.strlen])) + { + filler(buf, cast(char*) toStringz(file), null, 0); + } + return 0; + })(); + } + + private int dfuse_readlink(const char* path, char* buf, size_t size) + { + return call!( + (Operations t) + { + auto length = t.readlink(path[0..path.strlen], + (cast(ubyte*)buf)[0..size]); + /* Null-terminate the string and copy it over to the buffer. */ + assert(length <= size); + buf[length] = '\0'; + + return 0; + })(); + } + + private int dfuse_read(const char* path, char* buf, size_t size, + off_t offset, fuse_file_info* fi) + { + /* Ensure at compile time that off_t and size_t fit into an ulong. */ + static assert(ulong.max >= size_t.max); + static assert(ulong.max >= off_t.max); + + return call!( + (Operations t) + { + auto bbuf = cast(ubyte*) buf; + return cast(int) t.read(path[0..path.strlen], bbuf[0..size], + to!ulong(offset)); + })(); + } + + private int dfuse_write(const char* path, char* data, size_t size, + off_t offset, fuse_file_info* fi) + { + static assert(ulong.max >= size_t.max); + static assert(ulong.max >= off_t.max); + + return call!( + (Operations t) + { + auto bdata = cast(ubyte*) data; + return t.write(path[0..path.strlen], bdata[0..size], + to!ulong(offset)); + })(); + } + + private int dfuse_truncate(const char* path, off_t length) + { + static assert(ulong.max >= off_t.max); + return call!( + (Operations t) + { + t.truncate(path[0..path.strlen], to!ulong(length)); + return 0; + })(); + } + + private void* dfuse_init(fuse_conn_info* conn) + { + attach(); + auto t = cast(Operations*) fuse_get_context().private_data; + (*t).initialize(); + return t; + } + + private void dfuse_destroy(void* data) + { + /* this is an ugly hack at the moment. We need to somehow detach all + threads from the runtime because after fuse_main finishes the pthreads + are joined. We circumvent that problem by just exiting while our + threads still run. */ + import std.c.process; + exit(0); + } +} /* extern(C) */ + +export class FuseException : Exception +{ + public int errno; + this(int errno, string file = __FILE__, size_t line = __LINE__, + Throwable next = null) + { + super("Fuse Exception", file, line, next); + this.errno = errno; + } +} + +/** + * An object oriented wrapper around fuse_operations. + */ +export class Operations +{ + /** + * Runs on filesystem creation + */ + void initialize() + { + } + + /** + * Called to get a stat(2) structure for a path. + */ + void getattr(const(char)[] path, ref stat_t stat) + { + throw new FuseException(errno.EOPNOTSUPP); + } + + /** + * Read path into the provided buffer beginning at offset. + * + * Params: + * path = The path to the file to read. + * buf = The buffer to read the data into. + * offset = An offset to start reading at. + * Returns: The amount of bytes read. + */ + ulong read(const(char)[] path, ubyte[] buf, ulong offset) + { + throw new FuseException(errno.EOPNOTSUPP); + } + + /** + * Write the given data to the file. + * + * Params: + * path = The path to the file to write. + * buf = A read-only buffer containing the data to write. + * offset = An offset to start writing at. + * Returns: The amount of bytes written. + */ + int write(const(char)[] path, in ubyte[] data, ulong offset) + { + throw new FuseException(errno.EOPNOTSUPP); + } + + /** + * Truncate a file to the given length. + * Params: + * path = The path to the file to trunate. + * length = Truncate file to this given length. + */ + void truncate(const(char)[] path, ulong length) + { + throw new FuseException(errno.EOPNOTSUPP); + } + + /** + * Returns a list of files and directory names in the given folder. Note + * that you have to return . and .. + * + * Params: + * path = The path to the directory. + * Returns: An array of filenames. + */ + string[] readdir(const(char)[] path) + { + throw new FuseException(errno.EOPNOTSUPP); + } + + /** + * Reads the link identified by path into the given buffer. + * + * Params: + * path = The path to the directory. + */ + ulong readlink(const(char)[] path, ubyte[] buf) + { + throw new FuseException(errno.EOPNOTSUPP); + } + + /** + * Determine if the user has access to the given path. + * + * Params: + * path = The path to check. + * mode = An flag indicating what to check for. See access(2) for + * supported modes. + * Returns: True on success otherwise false. + */ + bool access(const(char)[] path, int mode) + { + throw new FuseException(errno.EOPNOTSUPP); + } + + void exception(Exception e) + { + } +} + +/** + * A wrapper around fuse_main() + */ +export class Fuse +{ +private: + bool foreground; + bool threaded; + string fsname; + +public: + this(string fsname) + { + this(fsname, false, true); + } + + this(string fsname, bool foreground, bool threaded) + { + this.fsname = fsname; + this.foreground = foreground; + this.threaded = threaded; + } + + void mount(Operations ops, const string mountpoint, string[] mountopts) + { + string [] args = [this.fsname]; + + args ~= mountpoint; + + if(mountopts.length > 0) + { + args ~= format("-o%s", mountopts.join(",")); + } + + if(this.foreground) + { + args ~= "-f"; + } + + if(!this.threaded) + { + args ~= "-s"; + } + + debug writefln("fuse arguments s=(%s)", args); + + fuse_operations fops; + + fops.init = &dfuse_init; + fops.access = &dfuse_access; + fops.getattr = &dfuse_getattr; + fops.readdir = &dfuse_readdir; + fops.read = &dfuse_read; + fops.write = &dfuse_write; + fops.truncate = &dfuse_truncate; + fops.readlink = &dfuse_readlink; + fops.destroy = &dfuse_destroy; + + /* Create c-style arguments from a string[] array. */ + auto cargs = array(map!(a => toStringz(a))(args)); + int length = cast(int) cargs.length; + static if(length.max < cargs.length.max) + { + /* This is an unsafe cast that we need to do for C compat. + Enforce unlike assert will be checked in opt-builds as well. */ + import std.exception : enforce; + enforce(length >= 0); + enforce(length == cargs.length); + } + + fuse_main(length, cast(char**) cargs.ptr, &fops, &ops); + } +}