summaryrefslogtreecommitdiff
path: root/netfs/fuse
diff options
context:
space:
mode:
authorrandomdan <randomdan@localhost>2014-03-20 21:07:25 +0000
committerrandomdan <randomdan@localhost>2014-03-20 21:07:25 +0000
commit5780e0d6b8fb5c4269d224b865365a1c59afd38b (patch)
tree2fdef12c4a975d2b412b78d75654db6fab471379 /netfs/fuse
parentFix slice scanner and split .ice files back into logical blocks (diff)
downloadnetfs-5780e0d6b8fb5c4269d224b865365a1c59afd38b.tar.bz2
netfs-5780e0d6b8fb5c4269d224b865365a1c59afd38b.tar.xz
netfs-5780e0d6b8fb5c4269d224b865365a1c59afd38b.zip
Modularized interfaces for netfs
Diffstat (limited to 'netfs/fuse')
-rw-r--r--netfs/fuse/Jamfile.jam33
-rw-r--r--netfs/fuse/fuse.cpp148
-rw-r--r--netfs/fuse/fuse.h117
-rw-r--r--netfs/fuse/fuseConfig.cpp29
-rw-r--r--netfs/fuse/fuseConfig.h34
-rw-r--r--netfs/fuse/fuseDirs.cpp103
-rw-r--r--netfs/fuse/fuseFiles.cpp155
-rw-r--r--netfs/fuse/fuseMisc.cpp110
-rw-r--r--netfs/fuse/fuseSystem.cpp16
-rw-r--r--netfs/fuse/pch.hpp13
10 files changed, 758 insertions, 0 deletions
diff --git a/netfs/fuse/Jamfile.jam b/netfs/fuse/Jamfile.jam
new file mode 100644
index 0000000..873f67c
--- /dev/null
+++ b/netfs/fuse/Jamfile.jam
@@ -0,0 +1,33 @@
+cpp-pch pch : pch.hpp :
+ <define>_FILE_OFFSET_BITS=64
+ <include>../../libmisc
+ <include>../../libfusepp
+ <implicit-dependency>../ice//netfsComms
+ <library>../ice//netfsComms
+ <library>..//boost_thread
+ <library>..//fuse
+ <library>..//Ice
+ <library>..//libxml2
+ ;
+
+
+exe netfs :
+ pch
+ [ glob *.cpp ]
+ [ glob ../../libfusepp/fuse*.cpp ]
+ :
+ <define>_FILE_OFFSET_BITS=64
+ <include>../../libmisc
+ <include>../../libfusepp
+ <implicit-dependency>../ice//netfsComms
+ <library>../ice//netfsComms
+ <library>../lib//netfsCommon
+ <library>..//boost_thread
+ <library>..//boost_system
+ <library>..//fuse
+ <library>..//Ice
+ <library>..//IceUtil
+ <library>..//pthread
+ <library>..//libxml2
+ ;
+
diff --git a/netfs/fuse/fuse.cpp b/netfs/fuse/fuse.cpp
new file mode 100644
index 0000000..e221cfd
--- /dev/null
+++ b/netfs/fuse/fuse.cpp
@@ -0,0 +1,148 @@
+#include "pch.hpp"
+#include <string.h>
+#include <boost/foreach.hpp>
+#include "fuse.h"
+
+NetFS::FuseApp::FuseApp(int & argc, char ** argv) :
+ _argc(argc),
+ _argv(argv),
+ openDirID(0),
+ openFileID(0)
+{
+}
+
+NetFS::FuseApp::~FuseApp()
+{
+ volume->disconnect();
+ if (ic) {
+ ic->destroy();
+ }
+}
+
+void *
+NetFS::FuseApp::init(struct fuse_conn_info *)
+{
+ ic = Ice::initialize(_argc, _argv);
+ fc = FuseConfig::Load(configPath);
+ return NULL;
+}
+
+int
+NetFS::FuseApp::opt_parse(void *, const char * arg, int, struct fuse_args *)
+{
+ if (strncmp(arg, "--Ice.", 6) == 0) {
+ return 0;
+ }
+ else if (arg[0] == '-') {
+ return 1;
+ }
+ else if (exportName.empty()) {
+ const char * colon = strchr(arg, ':');
+ exportName = colon + 1;
+ configPath.assign(arg, colon);
+ return 0;
+ }
+ else if (mountPoint.empty()) {
+ mountPoint = arg;
+ return 1;
+ }
+ return 1;
+}
+
+void
+NetFS::FuseApp::connectToService()
+{
+ if (!service) {
+ LOCK;
+ FuseConfig::ExportPtr e = fc->exports[exportName];
+ const std::string & ep = *e->endpoints.begin();
+
+ service = NetFS::ServicePrx::checkedCast(ic->stringToProxy("Service:" + ep));
+ if (!service) {
+ throw "Invalid service proxy";
+ }
+ }
+}
+
+void
+NetFS::FuseApp::connectToVolume()
+{
+ if (!volume) {
+ volume = service->connect(exportName, "bar");
+ if (!volume) {
+ throw "Invalid filesystem proxy";
+ }
+ }
+}
+
+void
+NetFS::FuseApp::connectHandles()
+{
+ BOOST_FOREACH(const OpenFiles::value_type & of, openFiles) {
+ try {
+ of.second->remote->ice_ping();
+ }
+ catch (const Ice::ObjectNotExistException &) {
+ of.second->remote = volume->open(reqEnv(), of.second->path, of.second->flags);
+ }
+ }
+ BOOST_FOREACH(const OpenDirs::value_type & of, openDirs) {
+ try {
+ of.second->remote->ice_ping();
+ }
+ catch (const Ice::ObjectNotExistException &) {
+ of.second->remote = volume->opendir(reqEnv(), of.second->path);
+ }
+ }
+}
+
+void
+NetFS::FuseApp::verifyConnection()
+{
+ LOCK;
+ if (service) {
+ try {
+ service->ice_ping();
+ }
+ catch (const Ice::Exception &) {
+ service = NULL;
+ }
+ }
+ if (volume) {
+ try {
+ volume->ice_ping();
+ }
+ catch (const Ice::Exception &) {
+ volume = NULL;
+ }
+ }
+}
+
+int
+NetFS::FuseApp::onError(const std::exception & e) throw()
+{
+ if (dynamic_cast<const Ice::ObjectNotExistException *>(&e)) {
+ verifyConnection();
+ connectToService();
+ connectToVolume();
+ connectHandles();
+ return 0;
+ }
+ return FuseAppBase::onError(e);
+}
+
+NetFS::ReqEnv
+NetFS::FuseApp::reqEnv()
+{
+ connectToService();
+ connectToVolume();
+ struct fuse_context * c = fuse_get_context();
+ return { uentries.getName(c->uid), gentries.getName(c->gid) };
+}
+
+int
+main(int argc, char* argv[])
+{
+ return FuseAppBase::run(argc, argv, new NetFS::FuseApp(argc, argv));
+}
+
diff --git a/netfs/fuse/fuse.h b/netfs/fuse/fuse.h
new file mode 100644
index 0000000..033d7fa
--- /dev/null
+++ b/netfs/fuse/fuse.h
@@ -0,0 +1,117 @@
+#ifndef FUSE_H
+#define FUSE_H
+
+#include <boost/thread/shared_mutex.hpp>
+#include <Ice/Ice.h>
+#include <service.h>
+#include "fuseapp.h"
+#include "fuseConfig.h"
+#include "entCache.h"
+#include "intrusivePtrBase.h"
+
+#define LOCK boost::unique_lock<boost::shared_mutex> _lck(_lock)
+#define SLOCK boost::shared_lock<boost::shared_mutex> _lck(_lock)
+
+namespace NetFS {
+ class FuseApp : public FuseAppBase {
+ private:
+ class OpenDir : public IntrusivePtrBase {
+ public:
+ OpenDir(DirectoryPrx remote, const std::string & path);
+
+ DirectoryPrx remote;
+ const std::string path;
+ };
+ typedef boost::intrusive_ptr<OpenDir> OpenDirPtr;
+ typedef std::map<int, OpenDirPtr> OpenDirs;
+
+ class OpenFile : public IntrusivePtrBase {
+ public:
+ OpenFile(FilePrx remote, const std::string & path, int flags);
+
+ FilePrx remote;
+ const std::string path;
+ const int flags;
+ };
+ typedef boost::intrusive_ptr<OpenFile> OpenFilePtr;
+ typedef std::map<int, OpenFilePtr> OpenFiles;
+
+ public:
+ FuseApp(int & argc, char ** argv);
+ ~FuseApp();
+
+ private:
+ void * init (struct fuse_conn_info * info);
+ int opt_parse(void *, const char * arg, int key, struct fuse_args *);
+
+ void connectToService();
+ void connectToVolume();
+ void connectHandles();
+ void verifyConnection();
+
+ public:
+ // misc
+ int access(const char * p, int a);
+ int getattr(const char * p, struct stat * s);
+ int fgetattr(const char *, struct stat *, struct fuse_file_info *);
+ int chmod(const char *, mode_t);
+ int chown(const char *, uid_t, gid_t);
+ int link(const char *, const char *);
+ int readlink(const char *, char *, size_t);
+ int rename(const char *, const char *);
+ int symlink(const char *, const char *);
+ int unlink(const char *);
+ int utimens(const char *, const struct timespec tv[2]);
+ // dirs
+ int opendir(const char * p, struct fuse_file_info * fi);
+ int releasedir(const char *, struct fuse_file_info * fi);
+ int readdir(const char *, void * buf, fuse_fill_dir_t filler, off_t, struct fuse_file_info * fi);
+ int mkdir(const char *, mode_t);
+ int rmdir(const char *);
+ // files
+ int open(const char * p, struct fuse_file_info * fi);
+ int create(const char *, mode_t, struct fuse_file_info *);
+ int release(const char *, struct fuse_file_info * fi);
+ int read(const char *, char * buf, size_t s, off_t o, struct fuse_file_info * fi);
+ int write(const char *, const char * buf, size_t s, off_t o, struct fuse_file_info * fi);
+ int truncate(const char *, off_t);
+ int ftruncate(const char *, off_t, struct fuse_file_info *);
+ // fs
+ int statfs(const char *, struct statvfs *);
+ // stuff
+ int onError(const std::exception & err) throw();
+
+ private:
+ void setProxy(OpenFilePtr, uint64_t & fh);
+ OpenFilePtr getFileProxy(uint64_t localID) const;
+ void clearFileProxy(uint64_t localID);
+
+ void setProxy(OpenDirPtr, uint64_t & fh);
+ OpenDirPtr getDirProxy(uint64_t localID) const;
+ void clearDirProxy(uint64_t localID);
+
+ ReqEnv reqEnv();
+
+ int & _argc;
+ char ** _argv;
+ Ice::CommunicatorPtr ic;
+ FuseConfigPtr fc;
+ mutable boost::shared_mutex _lock;
+
+ NetFS::VolumePrx volume;
+ NetFS::ServicePrx service;
+
+ std::string mountPoint;
+ std::string exportName;
+ std::string configPath;
+
+ UserEntCache uentries;
+ GroupEntCache gentries;
+ OpenDirs openDirs;
+ int openDirID;
+ OpenFiles openFiles;
+ int openFileID;
+ };
+}
+
+#endif
diff --git a/netfs/fuse/fuseConfig.cpp b/netfs/fuse/fuseConfig.cpp
new file mode 100644
index 0000000..168550e
--- /dev/null
+++ b/netfs/fuse/fuseConfig.cpp
@@ -0,0 +1,29 @@
+#include "pch.hpp"
+#include "fuseConfig.h"
+#include <string.h>
+
+FuseConfigPtr
+FuseConfig::Load(const std::string & path)
+{
+ xmlDoc * doc = xmlReadFile(path.c_str(), NULL, 0);
+ FuseConfigPtr dc = new FuseConfig(doc->children);
+ xmlFreeDoc(doc);
+ return dc;
+}
+
+FuseConfig::FuseConfig(xmlNodePtr conf)
+{
+ foreachxml(exp, xmlGetNode(conf, "exports"), "export") {
+ ExportPtr e = new Export(exp);
+ exports[e->name] = e;
+ }
+}
+
+FuseConfig::Export::Export(xmlNodePtr conf) :
+ name(xmlGetNodeValue(conf, "name"))
+{
+ foreachxml(ep, xmlGetNode(conf, "endpoints"), "endpoint") {
+ endpoints.insert(xmlGetNodeValue(ep));
+ }
+}
+
diff --git a/netfs/fuse/fuseConfig.h b/netfs/fuse/fuseConfig.h
new file mode 100644
index 0000000..f33c8f7
--- /dev/null
+++ b/netfs/fuse/fuseConfig.h
@@ -0,0 +1,34 @@
+#ifndef FUSECONFIG_H
+#define FUSECONFIG_H
+
+#include <string>
+#include <map>
+#include <set>
+#include <intrusivePtrBase.h>
+#include <boost/intrusive_ptr.hpp>
+#include "xml.h"
+
+class FuseConfig : public virtual IntrusivePtrBase {
+ public:
+ class Export;
+ typedef boost::intrusive_ptr<Export> ExportPtr;
+ typedef std::map<std::string, ExportPtr> ExportMap;
+ class Export : public virtual IntrusivePtrBase {
+ public:
+ typedef std::set<std::string> Endpoints;
+ Export(xmlNodePtr);
+
+ std::string name;
+ Endpoints endpoints;
+ };
+
+ FuseConfig(xmlNodePtr);
+ static boost::intrusive_ptr<FuseConfig> Load(const std::string & path);
+
+ ExportMap exports;
+};
+typedef boost::intrusive_ptr<FuseConfig> FuseConfigPtr;
+
+#endif
+
+
diff --git a/netfs/fuse/fuseDirs.cpp b/netfs/fuse/fuseDirs.cpp
new file mode 100644
index 0000000..fac70a6
--- /dev/null
+++ b/netfs/fuse/fuseDirs.cpp
@@ -0,0 +1,103 @@
+#include "pch.hpp"
+#include "fuse.h"
+#include "misc.h"
+
+NetFS::FuseApp::OpenDir::OpenDir(DirectoryPrx r, const std::string & p) :
+ remote(r),
+ path(p)
+{
+}
+
+void
+NetFS::FuseApp::setProxy(OpenDirPtr od, uint64_t & fh)
+{
+ LOCK;
+ while (openDirs.find(fh = ++openDirID) != openDirs.end()) ;
+ openDirs.insert({ fh, od });
+}
+
+NetFS::FuseApp::OpenDirPtr
+NetFS::FuseApp::getDirProxy(uint64_t localID) const
+{
+ SLOCK;
+ OpenDirs::const_iterator i = openDirs.find(localID);
+ if (i != openDirs.end()) {
+ return i->second;
+ }
+ throw NetFS::SystemError(EBADF);
+}
+
+void
+NetFS::FuseApp::clearDirProxy(uint64_t localID)
+{
+ LOCK;
+ openDirs.erase(localID);
+}
+
+int
+NetFS::FuseApp::opendir(const char * p, struct fuse_file_info * fi)
+{
+ try {
+ auto remote = volume->opendir(reqEnv(), p);
+ setProxy(new OpenDir(remote, p), fi->fh);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::releasedir(const char *, struct fuse_file_info * fi)
+{
+ try {
+ auto remote = getDirProxy(fi->fh)->remote;
+ remote->close();
+ clearDirProxy(fi->fh);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::readdir(const char *, void * buf, fuse_fill_dir_t filler, off_t, struct fuse_file_info * fi)
+{
+ try {
+ auto remote = getDirProxy(fi->fh)->remote;
+ NetFS::NameList ds = remote->readdir();
+ BOOST_FOREACH(const auto & e, ds) {
+ filler(buf, e.c_str(), NULL, 0);
+ }
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::mkdir(const char * p, mode_t m)
+{
+ try {
+ volume->mkdir(reqEnv(), p, m);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::rmdir(const char * p)
+{
+ try {
+ volume->rmdir(reqEnv(), p);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
diff --git a/netfs/fuse/fuseFiles.cpp b/netfs/fuse/fuseFiles.cpp
new file mode 100644
index 0000000..4a96e51
--- /dev/null
+++ b/netfs/fuse/fuseFiles.cpp
@@ -0,0 +1,155 @@
+#include "pch.hpp"
+#include <string.h>
+#include <typeConvert.h>
+#include "fuse.h"
+
+NetFS::FuseApp::OpenFile::OpenFile(FilePrx r, const std::string & p, int f) :
+ remote(r),
+ path(p),
+ flags(f)
+{
+}
+
+void
+NetFS::FuseApp::setProxy(OpenFilePtr of, uint64_t & fh)
+{
+ LOCK;
+ while (openFiles.find(fh = ++openFileID) != openFiles.end()) ;
+ openFiles.insert({ fh, of });
+}
+
+NetFS::FuseApp::OpenFilePtr
+NetFS::FuseApp::getFileProxy(uint64_t localID) const
+{
+ SLOCK;
+ OpenFiles::const_iterator i = openFiles.find(localID);
+ if (i != openFiles.end()) {
+ return i->second;
+ }
+ throw NetFS::SystemError(EBADF);
+}
+
+void
+NetFS::FuseApp::clearFileProxy(uint64_t localID)
+{
+ LOCK;
+ openFiles.erase(localID);
+}
+
+int
+NetFS::FuseApp::open(const char * p, struct fuse_file_info * fi)
+{
+ try {
+ auto remote = volume->open(reqEnv(), p, fi->flags);
+ setProxy(new OpenFile(remote, p, fi->flags), fi->fh);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::create(const char * p, mode_t m, struct fuse_file_info * fi)
+{
+ try {
+ auto remote = volume->create(reqEnv(), p, fi->flags, m);
+ setProxy(new OpenFile(remote, p, fi->flags), fi->fh);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::release(const char *, struct fuse_file_info * fi)
+{
+ try {
+ auto remote = getFileProxy(fi->fh)->remote;
+ remote->close();
+ clearFileProxy(fi->fh);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::read(const char *, char * buf, size_t s, off_t o, struct fuse_file_info * fi)
+{
+ try {
+ auto remote = getFileProxy(fi->fh)->remote;
+ NetFS::Buffer data = remote->read(o, s);
+ memcpy(buf, &data.front(), data.size());
+ return data.size();
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::write(const char *, const char * buf, size_t s, off_t o, struct fuse_file_info * fi)
+{
+ try {
+ auto remote = getFileProxy(fi->fh)->remote;
+ remote->write(o, s, NetFS::Buffer(buf, buf + s));
+ return s;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::truncate(const char * p, off_t o)
+{
+ try {
+ volume->truncate(reqEnv(), p, o);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::ftruncate(const char *, off_t o, fuse_file_info * fi)
+{
+ try {
+ auto remote = getFileProxy(fi->fh)->remote;
+ remote->ftruncate(reqEnv(), o);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::fgetattr(const char *, struct stat * s, fuse_file_info * fi)
+{
+ try {
+ auto remote = getFileProxy(fi->fh)->remote;
+ *s << AttrSource { remote->fgetattr(reqEnv()), boost::bind(&UserEntCache::getID, &uentries, _1), boost::bind(&GroupEntCache::getID, &gentries, _1) };
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::unlink(const char * p)
+{
+ try {
+ volume->unlink(reqEnv(), p);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
diff --git a/netfs/fuse/fuseMisc.cpp b/netfs/fuse/fuseMisc.cpp
new file mode 100644
index 0000000..568cf52
--- /dev/null
+++ b/netfs/fuse/fuseMisc.cpp
@@ -0,0 +1,110 @@
+#include "pch.hpp"
+#include "fuse.h"
+#include <string.h>
+#include <typeConvert.h>
+
+int
+NetFS::FuseApp::access(const char * p, int a)
+{
+ return -volume->access(reqEnv(), p, a);
+}
+
+int
+NetFS::FuseApp::getattr(const char * p, struct stat * s)
+{
+ try {
+ *s << AttrSource { volume->getattr(reqEnv(), p), boost::bind(&UserEntCache::getID, &uentries, _1), boost::bind(&GroupEntCache::getID, &gentries, _1) };
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::chmod(const char * p, mode_t m)
+{
+ try {
+ volume->chmod(reqEnv(), p, m);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::chown(const char * p, uid_t u, gid_t g)
+{
+ try {
+ volume->chown(reqEnv(), p, u, g);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::link(const char * p1, const char * p2)
+{
+ try {
+ volume->link(reqEnv(), p1, p2);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::symlink(const char * p1, const char * p2)
+{
+ try {
+ volume->symlink(reqEnv(), p1, p2);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::readlink(const char * p, char * p2, size_t s)
+{
+ try {
+ std::string l = volume->readlink(reqEnv(), p);
+ l.copy(p2, s);
+ p2[l.length()] = '\0';
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::rename(const char * p1, const char * p2)
+{
+ try {
+ volume->rename(reqEnv(), p1, p2);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
+int
+NetFS::FuseApp::utimens(const char * path, const struct timespec times[2])
+{
+ try {
+ volume->utimens(reqEnv(), path,
+ times[0].tv_sec, times[0].tv_nsec, times[1].tv_sec, times[1].tv_nsec);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
diff --git a/netfs/fuse/fuseSystem.cpp b/netfs/fuse/fuseSystem.cpp
new file mode 100644
index 0000000..0b29d86
--- /dev/null
+++ b/netfs/fuse/fuseSystem.cpp
@@ -0,0 +1,16 @@
+#include "pch.hpp"
+#include <typeConvert.h>
+#include "fuse.h"
+
+int
+NetFS::FuseApp::statfs(const char * p, struct statvfs * vfs)
+{
+ try {
+ *vfs << volume->statfs(reqEnv(), p);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
+}
+
diff --git a/netfs/fuse/pch.hpp b/netfs/fuse/pch.hpp
new file mode 100644
index 0000000..7cb79ed
--- /dev/null
+++ b/netfs/fuse/pch.hpp
@@ -0,0 +1,13 @@
+#ifdef BOOST_BUILD_PCH_ENABLED
+#ifndef NETFS_FUSE_PCH
+#define NETFS_FUSE_PCH
+
+#include "../lib/pch.hpp"
+#include "../../libfusepp/fuseapp.h"
+#include <fuse.h>
+
+#endif
+#endif
+
+
+