Add dfuse
This commit is contained in:
parent
6e5c10d60e
commit
1b43c4965d
1
dfuse
1
dfuse
|
@ -1 +0,0 @@
|
||||||
Subproject commit 8daf1d4fffbcd1415b963ed5a43fae5e4bf06464
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
*.o
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.a
|
||||||
|
simplefs
|
||||||
|
*.swp
|
||||||
|
*~
|
|
@ -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.
|
|
@ -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
|
|
@ -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.
|
|
@ -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.
|
|
@ -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"]
|
||||||
|
}]
|
||||||
|
}
|
|
@ -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 <MOUNTPOINT>");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout.writeln("mounting simplefs");
|
||||||
|
|
||||||
|
auto fs = new Fuse("SimpleFS", true, false);
|
||||||
|
fs.mount(new SimpleFS(), args[1], []);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue