summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jamroot.jam86
-rw-r--r--netfs/daemon/Jamfile.jam18
-rw-r--r--netfs/daemon/daemon.cpp35
-rw-r--r--netfs/daemon/daemon.h34
-rw-r--r--netfs/daemon/daemonConfigImpl.cpp3
-rw-r--r--netfs/daemon/daemonDirectory.cpp43
-rw-r--r--netfs/daemon/daemonDirectory.h25
-rw-r--r--netfs/daemon/daemonFile.cpp73
-rw-r--r--netfs/daemon/daemonFile.h30
-rw-r--r--netfs/daemon/daemonService.cpp17
-rw-r--r--netfs/daemon/daemonService.h28
-rw-r--r--netfs/daemon/daemonVolume.cpp181
-rw-r--r--netfs/daemon/daemonVolume.h75
-rw-r--r--netfs/daemon/modeCheck.cpp79
-rw-r--r--netfs/daemon/modeCheck.h46
-rw-r--r--netfs/fuse/Jamfile.jam29
-rw-r--r--netfs/fuse/fuseApp.cpp488
-rw-r--r--netfs/fuse/fuseApp.h258
-rw-r--r--netfs/fuse/fuseApp.impl.h31
-rw-r--r--netfs/fuse/fuseAppBase.cpp229
-rw-r--r--netfs/fuse/fuseAppBase.h341
-rw-r--r--netfs/fuse/fuseConfig.ice5
-rw-r--r--netfs/fuse/fuseConfigImpl.cpp14
-rw-r--r--netfs/fuse/fuseDirs.cpp163
-rw-r--r--netfs/fuse/fuseDirs.h13
-rw-r--r--netfs/fuse/fuseFiles.cpp513
-rw-r--r--netfs/fuse/fuseFiles.h27
-rw-r--r--netfs/fuse/fuseMappers.ice23
-rw-r--r--netfs/fuse/fuseMappersImpl.cpp74
-rw-r--r--netfs/fuse/fuseMappersImpl.h26
-rw-r--r--netfs/fuse/fuseMisc.cpp201
-rw-r--r--netfs/fuse/fuseSystem.cpp21
-rw-r--r--netfs/fuse/netfs.cpp80
-rw-r--r--netfs/ice/Jamfile.jam16
-rw-r--r--netfs/ice/directory.ice8
-rw-r--r--netfs/ice/entryResolver.h16
-rw-r--r--netfs/ice/exceptions.ice6
-rw-r--r--netfs/ice/exceptionsImpl.cpp14
-rw-r--r--netfs/ice/file.ice13
-rw-r--r--netfs/ice/mapper.ice28
-rw-r--r--netfs/ice/numeric.cpp21
-rw-r--r--netfs/ice/numeric.h113
-rw-r--r--netfs/ice/service.ice8
-rw-r--r--netfs/ice/typeConverter.cpp160
-rw-r--r--netfs/ice/typeConverter.h35
-rw-r--r--netfs/ice/types.ice10
-rw-r--r--netfs/ice/volume.ice8
-rw-r--r--netfs/lib/Jamfile.jam10
-rw-r--r--netfs/lib/baseMapper.cpp13
-rw-r--r--netfs/lib/baseMapper.h20
-rw-r--r--netfs/lib/defaultMapper.cpp27
-rw-r--r--netfs/lib/defaultMapper.h18
-rw-r--r--netfs/lib/entCache.cpp141
-rw-r--r--netfs/lib/entCache.h100
-rw-r--r--netfs/lib/entCache.impl.h58
-rw-r--r--netfs/lib/entries.cpp16
-rw-r--r--netfs/lib/entries.h24
-rw-r--r--netfs/lib/entryResolver.h14
-rw-r--r--netfs/unittests/Jamfile.jam83
-rw-r--r--netfs/unittests/altFuse.xml16
-rw-r--r--netfs/unittests/defaultFuseHide.xml15
-rw-r--r--netfs/unittests/defaultFuseMask.xml20
-rw-r--r--netfs/unittests/defaultFuseNoAsync.xml15
-rw-r--r--netfs/unittests/leak-suppressions.txt3
-rw-r--r--netfs/unittests/mockDaemon.cpp28
-rw-r--r--netfs/unittests/mockDaemon.h41
-rw-r--r--netfs/unittests/mockFuse.cpp77
-rw-r--r--netfs/unittests/mockFuse.h43
-rw-r--r--netfs/unittests/mockGlacier.cpp29
-rw-r--r--netfs/unittests/mockGlacier.h16
-rw-r--r--netfs/unittests/mockMount.cpp76
-rw-r--r--netfs/unittests/mockMount.h45
-rw-r--r--netfs/unittests/testCore.cpp528
-rw-r--r--netfs/unittests/testDaemon.cpp28
-rw-r--r--netfs/unittests/testEdgeCases.cpp160
-rw-r--r--netfs/unittests/testFuse.cpp143
-rw-r--r--netfs/unittests/testGlacier.cpp34
-rw-r--r--netfs/unittests/testLib.cpp206
-rw-r--r--netfs/unittests/testPerf.cpp133
-rw-r--r--netfs/unittests/thread-suppressions.txt5
80 files changed, 3702 insertions, 2248 deletions
diff --git a/Jamroot.jam b/Jamroot.jam
index 0edd310..eb3aceb 100644
--- a/Jamroot.jam
+++ b/Jamroot.jam
@@ -1,42 +1,68 @@
import os ;
import slice ;
+import testing ;
-using gcc : : [ os.environ CXX ] ;
+using pkg-config ;
+import pkg-config ;
variant coverage : debug ;
-project
- : requirements
+project netfs : requirements
<define>ICE_CPP11_MAPPING
- <define>_FILE_OFFSET_BITS=64
- <cxxflags>"-std=c++17 -fvisibility=hidden -fvisibility-inlines-hidden"
- <linkflags>"-Wl,-z,defs,--warn-once,--gc-sections"
- <variant>release:<cxxflags>"-flto=3"
- <variant>release:<linkflags>"-flto=3"
- <variant>debug:<cxxflags>"-W -Wall -Werror -Wextra"
- <variant>coverage:<cxxflags>"--coverage"
- <variant>coverage:<linkflags>"--coverage"
+ <cxxstd>23
+ <visibility>hidden
+ <linkflags>"-Wl,-z,defs,--warn-once,--gc-sections"
+ <variant>release:<lto>on
+ <variant>debug:<warnings>extra
+ <variant>debug:<warnings-as-errors>on
+ <variant>debug:<cflags>-Wnon-virtual-dtor
+ <variant>debug:<cflags>-Wold-style-cast
+ <variant>debug:<cflags>-Wcast-align
+ <variant>debug:<cflags>-Wunused
+ <variant>debug:<cflags>-Woverloaded-virtual
+ <toolset>gcc,<variant>debug:<cflags>-Wpedantic
+ <variant>debug:<cflags>-Wconversion
+ <variant>debug:<cflags>-Wsign-conversion
+ <variant>debug:<cflags>-Wnull-dereference
+ <variant>debug:<cflags>-Wdouble-promotion
+ <variant>debug:<cflags>-Wformat=2
+ <variant>debug:<cflags>-Wshadow
+ <toolset>gcc,<variant>debug:<cflags>-Wduplicated-cond
+ <toolset>gcc,<variant>debug:<cflags>-Wduplicated-branches
+ <toolset>gcc,<variant>debug:<cflags>-Wlogical-op
+ <toolset>gcc,<variant>debug:<cflags>-Wuseless-cast
+ <variant>coverage:<coverage>on
+ <toolset>tidy:<enable>all
+ <toolset>tidy:<define>ICE_IGNORE_VERSION
+ <toolset>tidy:<checkxx>boost-*
+ <toolset>tidy:<checkxx>bugprone-*
+ <toolset>tidy:<xcheckxx>bugprone-easily-swappable-parameters
+ <toolset>tidy:<checkxx>clang-*
+ <toolset>tidy:<checkxx>misc-*
+ <toolset>tidy:<checkxx>modernize-*
+ <toolset>tidy:<xcheckxx>modernize-use-trailing-return-type
+ <toolset>tidy:<xcheckxx>misc-non-private-member-variables-in-classes
+ <toolset>tidy:<checkxx>hicpp-*
+ <toolset>tidy:<xcheckxx>hicpp-vararg
+ <toolset>tidy:<xcheckxx>hicpp-signed-bitwise
+ <toolset>tidy:<xcheckxx>hicpp-no-array-decay
+ <toolset>tidy:<xcheckxx>hicpp-named-parameter
+ <toolset>tidy:<checkxx>performance-*
+ <toolset>tidy:<exclude>netfs/daemon/bin/tidy/debug/checker-none/cxxstd-20-iso/daemonConfig.h
+ <toolset>tidy:<exclude>netfs/fuse/bin/tidy/debug/checker-none/cxxstd-20-iso/fuseConfig.h
+ <toolset>tidy:<exclude>netfs/fuse/bin/tidy/debug/checker-none/cxxstd-20-iso/fuseMappers.h
+ <toolset>tidy:<exclude>netfs/ice/bin/directory.h
+ <toolset>tidy:<exclude>netfs/ice/bin/exceptions.h
+ <toolset>tidy:<exclude>netfs/ice/bin/file.h
+ <toolset>tidy:<exclude>netfs/ice/bin/service.h
+ <toolset>tidy:<exclude>netfs/ice/bin/types.h
+ <toolset>tidy:<exclude>netfs/ice/bin/volume.h
+ <toolset>tidy:<librarydef>std
+ <toolset>tidy:<librarydef>boost
;
build-project netfs ;
-# Some useful aliases
-
-lib glibmm-2.4 ;
-lib gobject-2.0 ;
-lib glib-2.0 ;
-lib sigc-2.0 ;
-
-alias glibmm : : : :
- <include>/usr/include/glibmm-2.4
- <include>/usr/lib/glibmm-2.4/include
- <include>/usr/include/glib-2.0
- <include>/usr/lib/glib-2.0/include
- <include>/usr/include/sigc++-2.0
- <include>/usr/lib/sigc++-2.0/include
- <library>glibmm-2.4
- <library>gobject-2.0
- <library>glib-2.0
- <library>sigc-2.0
- ;
+pkg-config.import glibmm : : <name>glibmm-2.68 ;
+pkg-config.import fuse : : <name>fuse3 ;
diff --git a/netfs/daemon/Jamfile.jam b/netfs/daemon/Jamfile.jam
index 4809935..447d2e8 100644
--- a/netfs/daemon/Jamfile.jam
+++ b/netfs/daemon/Jamfile.jam
@@ -1,24 +1,38 @@
import package ;
+obj daemonConfig : daemonConfig.ice :
+ <toolset>tidy:<checker>none
+ <library>../ice//netfs-api
+ <implicit-dependency>../ice//netfs-api
+;
lib netfsd-configuration :
+ daemonConfig
daemonConfig.ice
daemonConfigImpl.cpp
:
<library>../ice//netfs-api
<implicit-dependency>../ice//netfs-api
+ <implicit-dependency>daemonConfig
<library>..//Ice
<library>..//stdc++fs
<library>..//slicer
<library>..//adhocutil
- <slicer>yes
+ <slicer>pure
: :
<include>.
<library>..//Ice
<library>..//slicer
;
+obj modeCheck : modeCheck.cpp
+ :
+ <implicit-dependency>../ice//netfs-api
+ <use>../ice//netfs-api
+ <use>../lib//netfs-common
+ ;
lib netfsd++11 :
- [ glob *.cpp : daemonConfigImpl.cpp ]
+ [ glob *.cpp : modeCheck.cpp daemonConfigImpl.cpp ]
+ modeCheck
:
<implicit-dependency>../ice//netfs-api
<implicit-dependency>netfsd-configuration
diff --git a/netfs/daemon/daemon.cpp b/netfs/daemon/daemon.cpp
index 27dfbb9..831c409 100644
--- a/netfs/daemon/daemon.cpp
+++ b/netfs/daemon/daemon.cpp
@@ -1,15 +1,11 @@
-#include <Ice/Ice.h>
#include "daemon.h"
#include "daemonService.h"
#include "daemonVolume.h"
-#include <slicer/slicer.h>
#include "modeCheck.h"
-#include <sys/stat.h>
+#include <Ice/Ice.h>
#include <safeMapFind.h>
-
-NetFSDaemon::NetFSDaemon()
-{
-}
+#include <slicer/slicer.h>
+#include <sys/stat.h>
NetFSDaemon::~NetFSDaemon()
{
@@ -24,8 +20,11 @@ NetFSDaemon::hostname()
auto props = ic->getProperties();
auto hostNameOverride = props->getProperty("NetFSD.HostNameOverride");
if (hostNameOverride.empty()) {
- char buf[128];
- gethostname(buf, sizeof(buf));
+ std::string buf(128, 0);
+ if (gethostname(buf.data(), buf.size())) {
+ throw NetFS::Daemon::HostNotConfigured(strerror(errno));
+ }
+ buf.resize(strlen(buf.data()));
return buf;
}
return hostNameOverride;
@@ -33,9 +32,9 @@ NetFSDaemon::hostname()
// name = NetFSDaemonAdapter
void
-NetFSDaemon::start(const std::string & name, const Ice::CommunicatorPtr & ic, const Ice::StringSeq&)
+NetFSDaemon::start(const std::string & name, const Ice::CommunicatorPtr & c, const Ice::StringSeq &)
{
- this->ic = ic;
+ ic = c;
Ice::PropertiesPtr props = ic->getProperties();
LoadConfiguration(props->getProperty("NetFSD.ConfigPath"));
@@ -67,11 +66,11 @@ NetFSDaemon::stop()
}
extern "C" {
- DLL_PUBLIC
- IceBox::Service *
- createNetFSDaemon(Ice::CommunicatorPtr)
- {
- return new NetFSDaemon();
- }
+DLL_PUBLIC
+IceBox::Service *
+// NOLINTNEXTLINE(performance-unnecessary-value-param)
+createNetFSDaemon(Ice::CommunicatorPtr)
+{
+ return new NetFSDaemon();
+}
}
-
diff --git a/netfs/daemon/daemon.h b/netfs/daemon/daemon.h
index 57e2023..353c89b 100644
--- a/netfs/daemon/daemon.h
+++ b/netfs/daemon/daemon.h
@@ -1,32 +1,30 @@
-#ifndef DAEMON_H
-#define DAEMON_H
+#pragma once
#include <Ice/Ice.h>
#include <IceBox/IceBox.h>
+#include <c++11Helpers.h>
#include <daemonConfig.h>
#include <filesystem>
#include <visibility.h>
class DLL_PUBLIC NetFSDaemon : public IceBox::Service {
- public:
- NetFSDaemon();
- virtual ~NetFSDaemon();
+public:
+ NetFSDaemon() = default;
+ SPECIAL_MEMBERS_MOVE_RO(NetFSDaemon);
+ ~NetFSDaemon() override;
- virtual void start(const std::string&, const Ice::CommunicatorPtr&, const Ice::StringSeq&) override;
- virtual void stop() override;
+ void start(const std::string &, const Ice::CommunicatorPtr &, const Ice::StringSeq &) override;
+ void stop() override;
- protected:
- virtual NetFS::Daemon::ConfigurationPtr ReadConfiguration(const std::filesystem::path & path) const;
+protected:
+ [[nodiscard]] virtual NetFS::Daemon::ConfigurationPtr ReadConfiguration(const std::filesystem::path & path) const;
- private:
- void LoadConfiguration(const std::filesystem::path & path);
+private:
+ void LoadConfiguration(const std::filesystem::path & path);
- Ice::CommunicatorPtr ic;
- Ice::ObjectAdapterPtr adapter;
- NetFS::Daemon::RuntimeConfigurationPtr dc;
+ Ice::CommunicatorPtr ic;
+ Ice::ObjectAdapterPtr adapter;
+ NetFS::Daemon::RuntimeConfigurationPtr dc;
- std::string hostname();
+ std::string hostname();
};
-
-#endif
-
diff --git a/netfs/daemon/daemonConfigImpl.cpp b/netfs/daemon/daemonConfigImpl.cpp
index 48b1d0e..e45cf97 100644
--- a/netfs/daemon/daemonConfigImpl.cpp
+++ b/netfs/daemon/daemonConfigImpl.cpp
@@ -1,5 +1,5 @@
-#include <daemonConfig.h>
#include <compileTimeFormatter.h>
+#include <daemonConfig.h>
AdHocFormatter(HostNotConfiguredMsg, "This host [%?] is not defined in the configuration");
void
@@ -7,4 +7,3 @@ NetFS::Daemon::HostNotConfigured::ice_print(std::ostream & s) const
{
HostNotConfiguredMsg::write(s, hostName);
}
-
diff --git a/netfs/daemon/daemonDirectory.cpp b/netfs/daemon/daemonDirectory.cpp
index b03bd1f..5412bbe 100644
--- a/netfs/daemon/daemonDirectory.cpp
+++ b/netfs/daemon/daemonDirectory.cpp
@@ -1,21 +1,13 @@
+#include "daemonDirectory.h"
#include <Ice/ObjectAdapter.h>
+#include <cerrno>
#include <dirent.h>
-#include <errno.h>
+#include <fcntl.h>
#include <map>
#include <sys/stat.h>
#include <sys/types.h>
-#include <fcntl.h>
-#include "daemonDirectory.h"
-DirectoryServer::DirectoryServer(DIR * d, EntryTypeConverter & t) :
- EntryTypeConverter(t),
- od(d)
-{
-}
-
-DirectoryServer::~DirectoryServer()
-{
-}
+DirectoryServer::DirectoryServer(DIR * d, EntryTypeConverter & t) : EntryTypeConverter(t), od(d) { }
void
DirectoryServer::close(const Ice::Current & ice)
@@ -30,34 +22,29 @@ DirectoryServer::close(const Ice::Current & ice)
}
NetFS::NameList
-DirectoryServer::readdir(const Ice::Current&)
+DirectoryServer::readdir(const Ice::Current &)
{
errno = 0;
NetFS::NameList list;
while (dirent * d = ::readdir(od)) {
- if (errno) {
- // LCOV_EXCL_START
- throw NetFS::SystemError(errno);
- // LCOV_EXCL_STOP
- }
list.emplace_back(d->d_name);
}
+ if (errno) {
+ // LCOV_EXCL_START
+ throw NetFS::SystemError(errno);
+ // LCOV_EXCL_STOP
+ }
return list;
}
NetFS::DirectoryContents
-DirectoryServer::listdir(const Ice::Current&)
+DirectoryServer::listdir(const Ice::Current &)
{
errno = 0;
NetFS::DirectoryContents list;
int fd = dirfd(od);
while (dirent * d = ::readdir(od)) {
- if (errno) {
- // LCOV_EXCL_START
- throw NetFS::SystemError(errno);
- // LCOV_EXCL_STOP
- }
- struct stat s;
+ struct stat s { };
if (::fstatat(fd, d->d_name, &s, AT_SYMLINK_NOFOLLOW) != 0) {
// LCOV_EXCL_START
throw NetFS::SystemError(errno);
@@ -65,6 +52,10 @@ DirectoryServer::listdir(const Ice::Current&)
}
list.emplace(d->d_name, convert(s));
}
+ if (errno) {
+ // LCOV_EXCL_START
+ throw NetFS::SystemError(errno);
+ // LCOV_EXCL_STOP
+ }
return list;
}
-
diff --git a/netfs/daemon/daemonDirectory.h b/netfs/daemon/daemonDirectory.h
index 5e549fe..91a0bc2 100644
--- a/netfs/daemon/daemonDirectory.h
+++ b/netfs/daemon/daemonDirectory.h
@@ -1,24 +1,17 @@
-#ifndef DAEMONDIRECTORY_H
-#define DAEMONDIRECTORY_H
+#pragma once
#include <directory.h>
#include <dirent.h>
#include <typeConverter.h>
-class DirectoryServer : public NetFS::DirectoryV2, EntryTypeConverter {
- public:
- DirectoryServer(DIR * od, EntryTypeConverter &);
- virtual ~DirectoryServer();
+class DirectoryServer : public NetFS::Directory, NetFS::EntryTypeConverter {
+public:
+ DirectoryServer(DIR * od, EntryTypeConverter &);
- virtual void close(const Ice::Current&) override;
- virtual NetFS::NameList readdir(const Ice::Current&) override;
- virtual NetFS::DirectoryContents listdir(const Ice::Current&) override;
+ void close(const Ice::Current &) override;
+ NetFS::NameList readdir(const Ice::Current &) override;
+ NetFS::DirectoryContents listdir(const Ice::Current &) override;
- private:
- DIR * od;
+private:
+ DIR * od;
};
-
-#endif
-
-
-
diff --git a/netfs/daemon/daemonFile.cpp b/netfs/daemon/daemonFile.cpp
index 0df3c2a..cf34b7d 100644
--- a/netfs/daemon/daemonFile.cpp
+++ b/netfs/daemon/daemonFile.cpp
@@ -1,26 +1,19 @@
+#include "daemonFile.h"
#include <Ice/ObjectAdapter.h>
-#include <errno.h>
-#include <map>
+#include <cerrno>
+#include <entCache.h>
#include <fcntl.h>
-#include <typeConverter.h>
+#include <map>
+#include <numeric.h>
#include <sys/stat.h>
-#include "daemonFile.h"
-#include <entCache.h>
+#include <typeConverter.h>
+#include <unistd.h>
-FileServer::FileServer(int f, EntryTypeConverter & t) :
- EntryTypeConverter(t),
- fd(f)
-{
-}
-
-FileServer::~FileServer()
-{
-}
+FileServer::FileServer(int f, EntryTypeConverter & t) : EntryTypeConverter(t), fd(f) { }
void
-FileServer::ftruncate(const NetFS::ReqEnv re, Ice::Long size, const Ice::Current&)
+FileServer::ftruncate(Ice::Long size, const Ice::Current &)
{
- (void)re;
errno = 0;
if (::ftruncate(fd, size) != 0) {
throw NetFS::SystemError(errno);
@@ -28,10 +21,9 @@ FileServer::ftruncate(const NetFS::ReqEnv re, Ice::Long size, const Ice::Current
}
NetFS::Attr
-FileServer::fgetattr(const NetFS::ReqEnv re, const Ice::Current &)
+FileServer::fgetattr(const Ice::Current &)
{
- (void)re;
- struct stat s;
+ struct stat s { };
if (::fstat(fd, &s) != 0) {
throw NetFS::SystemError(errno);
}
@@ -49,27 +41,48 @@ FileServer::close(const Ice::Current & ice)
}
NetFS::Buffer
-FileServer::read(Ice::Long offset, Ice::Long size, const Ice::Current&)
+FileServer::read(Ice::Long offset, Ice::Long size, const Ice::Current &)
{
NetFS::Buffer buf;
- buf.resize(size);
- errno = 0;
- int r = pread(fd, &buf[0], size, offset);
- if (r == -1) {
- throw NetFS::SystemError(errno);
- }
- else if (r != size) {
- buf.resize(r);
+ if (size) {
+ buf.resize(safe {size});
+ errno = 0;
+ auto r = pread(fd, buf.data(), safe {size}, offset);
+ if (r == -1) {
+ throw NetFS::SystemError(errno);
+ }
+ else if (std::cmp_not_equal(r, size)) {
+ buf.resize(safe {r});
+ }
}
return buf;
}
void
-FileServer::write(Ice::Long offset, Ice::Long size, const NetFS::Buffer data, const Ice::Current&)
+FileServer::write(Ice::Long offset, Ice::Long size, const ::std::pair<const ::Ice::Byte *, const ::Ice::Byte *> data,
+ const Ice::Current &)
{
errno = 0;
- if (pwrite(fd, &data.front(), size, offset) != size) {
+ if (pwrite(fd, data.first, safe {size}, offset) != size) {
throw NetFS::SystemError(errno);
}
}
+Ice::Long
+FileServer::copyrange(NetFS::FilePrxPtr to, Ice::Long offsrc, Ice::Long offdst, Ice::Long size, Ice::Int flags,
+ const Ice::Current & ice)
+{
+ if (auto obj = ice.adapter->findByProxy(to); auto file = std::dynamic_pointer_cast<FileServer>(obj)) {
+ errno = 0;
+ off_t src = offsrc, dst = offdst;
+ if (auto rtn = copy_file_range(fd, &src, file->fd, &dst, safe {size}, safe {flags}); rtn != -1) {
+ return rtn;
+ }
+ throw NetFS::SystemError(errno);
+ }
+ else {
+ const auto bytes = read(offsrc, size, ice);
+ to->write(offdst, size, {bytes.data(), bytes.data() + size});
+ return size;
+ }
+}
diff --git a/netfs/daemon/daemonFile.h b/netfs/daemon/daemonFile.h
index 9a01abf..623de26 100644
--- a/netfs/daemon/daemonFile.h
+++ b/netfs/daemon/daemonFile.h
@@ -1,25 +1,21 @@
-#ifndef DAEMONFILE_H
-#define DAEMONFILE_H
+#pragma once
#include <file.h>
#include <typeConverter.h>
-class FileServer : public NetFS::File, EntryTypeConverter {
- public:
- FileServer(int fd, EntryTypeConverter &);
- virtual ~FileServer();
+class FileServer : public NetFS::File, NetFS::EntryTypeConverter {
+public:
+ FileServer(int fd, EntryTypeConverter &);
- virtual void close(const Ice::Current&) override;
- virtual void ftruncate(const NetFS::ReqEnv, Ice::Long size, const Ice::Current&) override;
- virtual NetFS::Attr fgetattr(const NetFS::ReqEnv, const Ice::Current&) override;
+ void close(const Ice::Current &) override;
+ void ftruncate(Ice::Long size, const Ice::Current &) override;
+ NetFS::Attr fgetattr(const Ice::Current &) override;
- virtual NetFS::Buffer read(Ice::Long offset, Ice::Long size, const Ice::Current&) override;
- virtual void write(Ice::Long offset, Ice::Long size, const NetFS::Buffer data, const Ice::Current&) override;
+ NetFS::Buffer read(Ice::Long offset, Ice::Long size, const Ice::Current &) override;
+ void write(Ice::Long offset, Ice::Long size, const ::std::pair<const ::Ice::Byte *, const ::Ice::Byte *> data,
+ const Ice::Current &) override;
+ Ice::Long copyrange(NetFS::FilePrxPtr, Ice::Long, Ice::Long, Ice::Long, Ice::Int, const Ice::Current &) override;
- private:
- const int fd;
+private:
+ const int fd;
};
-
-#endif
-
-
diff --git a/netfs/daemon/daemonService.cpp b/netfs/daemon/daemonService.cpp
index a516cb2..93d48a8 100644
--- a/netfs/daemon/daemonService.cpp
+++ b/netfs/daemon/daemonService.cpp
@@ -1,21 +1,30 @@
-#include "daemon.h"
#include "daemonService.h"
+#include "daemon.h"
#include "daemonVolume.h"
+#include <entCache.h>
#include <safeMapFind.h>
ServiceServer::ServiceServer(NetFS::Daemon::ConfigurationPtr c) :
- config(c)
+ userLookup(std::make_shared<UserEntCache>()), groupLookup(std::make_shared<GroupEntCache>(userLookup)),
+ config(std::move(c))
{
}
NetFS::VolumePrxPtr
+// cppcheck-suppress passedByValue;
ServiceServer::connect(const std::string share, const std::string authtoken, const Ice::Current & ice)
{
- auto ex = AdHoc::safeMapLookup<NetFS::ExportNotFound>(config->Exports, share);
+ const auto & ex = AdHoc::safeMapLookup<NetFS::ExportNotFound>(config->Exports, share);
if (!ex->AuthToken.empty() && ex->AuthToken != authtoken) {
throw NetFS::AuthError();
}
return Ice::uncheckedCast<NetFS::VolumePrx>(ice.adapter->addFacetWithUUID(
- std::make_shared<VolumeServer>(ex->RootPath, userLookup, groupLookup), "v01"));
+ std::make_shared<VolumeServer>(ex->RootPath, userLookup, groupLookup), "v01"));
}
+NetFS::SettingsPtr
+ServiceServer::getSettings(const Ice::Current & ice)
+{
+ const auto props = ice.adapter->getCommunicator()->getProperties();
+ return std::make_shared<NetFS::Settings>(props->getPropertyAsIntWithDefault("Ice.MessageSizeMax", 1024));
+}
diff --git a/netfs/daemon/daemonService.h b/netfs/daemon/daemonService.h
index d1c77b3..e796dc5 100644
--- a/netfs/daemon/daemonService.h
+++ b/netfs/daemon/daemonService.h
@@ -1,23 +1,21 @@
-#ifndef DAEMONSERVICE_H
-#define DAEMONSERVICE_H
+#pragma once
-#include <service.h>
-#include <entCache.h>
#include <daemonConfig.h>
-#include <entCache.h>
+#include <entries.h>
+#include <entryResolver.h>
+#include <service.h>
class ServiceServer : public NetFS::Service {
- public:
- ServiceServer(NetFS::Daemon::ConfigurationPtr c);
+public:
+ explicit ServiceServer(NetFS::Daemon::ConfigurationPtr c);
- virtual NetFS::VolumePrxPtr connect(const std::string share, const std::string auth, const Ice::Current&) override;
+ // cppcheck-suppress passedByValue;
+ NetFS::VolumePrxPtr connect(const std::string share, const std::string auth, const Ice::Current &) override;
+ NetFS::SettingsPtr getSettings(const Ice::Current &) override;
- private:
- EntCache<User> userLookup;
- EntCache<Group> groupLookup;
+private:
+ EntryResolverPtr<User> userLookup;
+ EntryResolverPtr<Group> groupLookup;
- NetFS::Daemon::ConfigurationPtr config;
+ NetFS::Daemon::ConfigurationPtr config;
};
-
-#endif
-
diff --git a/netfs/daemon/daemonVolume.cpp b/netfs/daemon/daemonVolume.cpp
index 2a6cc09..ab2f0fe 100644
--- a/netfs/daemon/daemonVolume.cpp
+++ b/netfs/daemon/daemonVolume.cpp
@@ -1,29 +1,26 @@
-#include <Ice/ObjectAdapter.h>
-#include <errno.h>
-#include <map>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <limits.h>
-#include <fcntl.h>
#include "daemonVolume.h"
-#include "daemonFile.h"
+#include "daemon.h"
#include "daemonDirectory.h"
+#include "daemonFile.h"
#include "modeCheck.h"
+#include <Ice/ObjectAdapter.h>
#include <boost/algorithm/string/predicate.hpp>
+#include <cerrno>
+#include <climits>
+#include <defaultMapper.h>
#include <entCache.h>
-#include "daemon.h"
+#include <fcntl.h>
+#include <map>
+#include <numeric.h>
+#include <sys/stat.h>
+#include <unistd.h>
extern std::map<Ice::Int, int> files;
-VolumeServer::VolumeServer(const std::filesystem::path & r, const EntCache<User> & u, const EntCache<Group> & g) :
+VolumeServer::VolumeServer(
+ const std::filesystem::path & r, const EntryResolverPtr<User> & u, const EntryResolverPtr<Group> & g) :
root(std::filesystem::canonical(r)),
- userLookup(u),
- groupLookup(g),
- converter(u, g)
-{
-}
-
-VolumeServer::~VolumeServer()
+ userLookup(*u), groupLookup(*g), converter(std::make_shared<NetFS::Mapping::DefaultMapper>(u, g))
{
}
@@ -34,11 +31,12 @@ VolumeServer::disconnect(const Ice::Current & ice)
}
Ice::Int
-VolumeServer::access(const NetFS::ReqEnv re, const std::string path, Ice::Int mode, const Ice::Current &)
+// cppcheck-suppress passedByValue;
+VolumeServer::access(const NetFS::ReqEnv re, std::string path, Ice::Int mode, const Ice::Current &)
{
ModeCheck mc(re, root, userLookup, groupLookup);
- struct stat s;
- std::filesystem::path p(resolvePath(path));
+ struct stat s { };
+ std::filesystem::path p(resolvePath(std::move(path)));
if (::stat(p.c_str(), &s) != 0) {
return errno;
}
@@ -60,11 +58,12 @@ VolumeServer::access(const NetFS::ReqEnv re, const std::string path, Ice::Int mo
}
NetFS::Attr
-VolumeServer::getattr(const NetFS::ReqEnv re, const std::string path, const Ice::Current &)
+// cppcheck-suppress passedByValue;
+VolumeServer::getattr(const NetFS::ReqEnv re, std::string path, const Ice::Current &)
{
ModeCheck mc(re, root, userLookup, groupLookup);
- struct stat s;
- std::filesystem::path p(resolvePath(path));
+ struct stat s { };
+ std::filesystem::path p(resolvePath(std::move(path)));
mc.AssertReadParent(p);
if (::lstat(p.c_str(), &s) != 0) {
throw NetFS::SystemError(errno);
@@ -73,23 +72,24 @@ VolumeServer::getattr(const NetFS::ReqEnv re, const std::string path, const Ice:
}
void
-VolumeServer::mknod(const NetFS::ReqEnv re, const std::string path, Ice::Int mode, Ice::Int dev, const Ice::Current&)
+// cppcheck-suppress passedByValue;
+VolumeServer::mknod(const NetFS::ReqEnv re, std::string path, Ice::Int mode, Ice::Int dev, const Ice::Current &)
{
ModeCheck mc(re, root, userLookup, groupLookup);
errno = 0;
- std::filesystem::path p(resolvePath(path));
+ std::filesystem::path p(resolvePath(std::move(path)));
mc.AssertWriteParent(p);
- if (::mknod(p.c_str(), mode, dev) != 0) {
+ if (::mknod(p.c_str(), safe {mode}, safe {dev}) != 0) {
throw NetFS::SystemError(errno);
}
}
void
-VolumeServer::symlink(const NetFS::ReqEnv re, const std::string path1, const std::string path2, const Ice::Current &)
+VolumeServer::symlink(const NetFS::ReqEnv re, const std::string path1, std::string path2, const Ice::Current &)
{
ModeCheck mc(re, root, userLookup, groupLookup);
errno = 0;
- std::filesystem::path p(resolvePath(path2));
+ std::filesystem::path p(resolvePath(std::move(path2)));
mc.AssertWriteParent(p);
if (::symlink(path1.c_str(), p.c_str()) != 0) {
throw NetFS::SystemError(errno);
@@ -101,12 +101,13 @@ VolumeServer::symlink(const NetFS::ReqEnv re, const std::string path1, const std
}
void
-VolumeServer::link(const NetFS::ReqEnv re, const std::string path1, const std::string path2, const Ice::Current &)
+// cppcheck-suppress passedByValue;
+VolumeServer::link(const NetFS::ReqEnv re, std::string path1, std::string path2, const Ice::Current &)
{
ModeCheck mc(re, root, userLookup, groupLookup);
errno = 0;
- std::filesystem::path p1(resolvePath(path1));
- std::filesystem::path p2(resolvePath(path2));
+ std::filesystem::path p1(resolvePath(std::move(path1)));
+ std::filesystem::path p2(resolvePath(std::move(path2)));
if (::link(p1.c_str(), p2.c_str()) != 0) {
throw NetFS::SystemError(errno);
}
@@ -117,12 +118,16 @@ VolumeServer::link(const NetFS::ReqEnv re, const std::string path1, const std::s
}
void
-VolumeServer::rename(const NetFS::ReqEnv re, const std::string from, const std::string to, const Ice::Current &)
+VolumeServer::rename(const NetFS::ReqEnv re, std::string from, std::string to, const Ice::optional<Ice::Int> flags,
+ const Ice::Current &)
{
ModeCheck mc(re, root, userLookup, groupLookup);
errno = 0;
- std::filesystem::path f(resolvePath(from));
- std::filesystem::path t(resolvePath(to));
+ std::filesystem::path t(resolvePath(std::move(to)));
+ if (flags && *flags == RENAME_NOREPLACE && ::access(t.c_str(), F_OK) == 0) {
+ throw NetFS::SystemError(EEXIST);
+ }
+ std::filesystem::path f(resolvePath(std::move(from)));
mc.AssertWriteParent(f);
mc.AssertWriteParent(t);
if (::rename(f.c_str(), t.c_str()) != 0) {
@@ -131,69 +136,71 @@ VolumeServer::rename(const NetFS::ReqEnv re, const std::string from, const std::
}
std::string
-VolumeServer::readlink(const NetFS::ReqEnv re, const std::string path, const Ice::Current &)
+// cppcheck-suppress passedByValue;
+VolumeServer::readlink(const NetFS::ReqEnv re, std::string path, const Ice::Current &)
{
ModeCheck mc(re, root, userLookup, groupLookup);
errno = 0;
- char buf[PATH_MAX];
- std::filesystem::path p(resolvePath(path));
+ std::string buf(PATH_MAX, 0);
+ std::filesystem::path p(resolvePath(std::move(path)));
mc.AssertRead(p);
- ssize_t rc = ::readlink(p.c_str(), buf, PATH_MAX);
- if (rc == -1) {
+ auto rc = ::readlink(p.c_str(), buf.data(), PATH_MAX);
+ if (rc < 0) {
throw NetFS::SystemError(errno);
}
- return std::string(buf, rc);
+ buf.resize(safe {rc});
+ return buf;
}
void
-VolumeServer::chmod(const NetFS::ReqEnv re, const std::string path, Ice::Int mode, const Ice::Current &)
+// cppcheck-suppress passedByValue;
+VolumeServer::chmod(const NetFS::ReqEnv re, std::string path, Ice::Int mode, const Ice::Current &)
{
ModeCheck mc(re, root, userLookup, groupLookup);
errno = 0;
- std::filesystem::path p(resolvePath(path));
+ std::filesystem::path p(resolvePath(std::move(path)));
mc.AssertWritePerms(p);
- if (::chmod(p.c_str(), mode) != 0) {
+ if (::chmod(p.c_str(), safe {mode}) != 0) {
throw NetFS::SystemError(errno);
}
}
void
-VolumeServer::chown(const NetFS::ReqEnv re, const std::string path, Ice::Int uid, Ice::Int gid, const Ice::Current &)
+// cppcheck-suppress passedByValue;
+VolumeServer::chown(const NetFS::ReqEnv re, std::string path, Ice::Int uid, Ice::Int gid, const Ice::Current &)
{
ModeCheck mc(re, root, userLookup, groupLookup);
errno = 0;
- std::filesystem::path p(resolvePath(path));
+ std::filesystem::path p(resolvePath(std::move(path)));
mc.AssertWrite(p);
- if (::lchown(p.c_str(), uid, gid) != 0) {
+ if (::lchown(p.c_str(), safe {uid}, safe {gid}) != 0) {
throw NetFS::SystemError(errno);
}
}
void
-VolumeServer::utimens(const NetFS::ReqEnv re, const std::string path,
- Ice::Long s0, Ice::Long ns0, Ice::Long s1, Ice::Long ns1, const Ice::Current&)
+// cppcheck-suppress passedByValue;
+VolumeServer::utimens(const NetFS::ReqEnv re, std::string path, Ice::Long s0, Ice::Long ns0, Ice::Long s1,
+ Ice::Long ns1, const Ice::Current &)
{
ModeCheck mc(re, root, userLookup, groupLookup);
errno = 0;
- struct timespec times[2];
- times[0].tv_sec = s0;
- times[0].tv_nsec = ns0;
- times[1].tv_sec = s1;
- times[1].tv_nsec = ns1;
- std::filesystem::path p(resolvePath(path));
+ std::array<struct timespec, 2> times {{{s0, ns0}, {s1, ns1}}};
+ std::filesystem::path p(resolvePath(std::move(path)));
mc.AssertWrite(p);
- if (::utimensat(0, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) {
+ if (::utimensat(0, p.c_str(), times.data(), AT_SYMLINK_NOFOLLOW) != 0) {
throw NetFS::SystemError(errno);
}
}
NetFS::VFS
-VolumeServer::statfs(const NetFS::ReqEnv re, const std::string path, const Ice::Current&)
+// cppcheck-suppress passedByValue;
+VolumeServer::statfs(const NetFS::ReqEnv re, std::string path, const Ice::Current &)
{
ModeCheck mc(re, root, userLookup, groupLookup);
errno = 0;
- struct statvfs s;
- std::filesystem::path p(resolvePath(path));
+ struct statvfs s { };
+ std::filesystem::path p(resolvePath(std::move(path)));
mc.AssertRead(p);
if (::statvfs(p.c_str(), &s) != 0) {
throw NetFS::SystemError(errno);
@@ -202,11 +209,12 @@ VolumeServer::statfs(const NetFS::ReqEnv re, const std::string path, const Ice::
}
void
-VolumeServer::truncate(const NetFS::ReqEnv re, const std::string path, Ice::Long size, const Ice::Current&)
+// cppcheck-suppress passedByValue;
+VolumeServer::truncate(const NetFS::ReqEnv re, std::string path, Ice::Long size, const Ice::Current &)
{
ModeCheck mc(re, root, userLookup, groupLookup);
errno = 0;
- std::filesystem::path p(resolvePath(path));
+ std::filesystem::path p(resolvePath(std::move(path)));
mc.AssertWrite(p);
if (::truncate(p.c_str(), size) != 0) {
throw NetFS::SystemError(errno);
@@ -214,11 +222,12 @@ VolumeServer::truncate(const NetFS::ReqEnv re, const std::string path, Ice::Long
}
void
-VolumeServer::unlink(const NetFS::ReqEnv re, const std::string path, const Ice::Current&)
+// cppcheck-suppress passedByValue;
+VolumeServer::unlink(const NetFS::ReqEnv re, std::string path, const Ice::Current &)
{
ModeCheck mc(re, root, userLookup, groupLookup);
errno = 0;
- std::filesystem::path p(resolvePath(path));
+ std::filesystem::path p(resolvePath(std::move(path)));
mc.AssertWriteParent(p);
if (::unlink(p.c_str()) != 0) {
throw NetFS::SystemError(errno);
@@ -226,11 +235,12 @@ VolumeServer::unlink(const NetFS::ReqEnv re, const std::string path, const Ice::
}
NetFS::FilePrxPtr
-VolumeServer::open(const NetFS::ReqEnv re, const std::string path, Ice::Int flags, const Ice::Current & ice)
+// cppcheck-suppress passedByValue;
+VolumeServer::open(const NetFS::ReqEnv re, std::string path, Ice::Int flags, const Ice::Current & ice)
{
ModeCheck mc(re, root, userLookup, groupLookup);
errno = 0;
- std::filesystem::path p(resolvePath(path));
+ std::filesystem::path p(resolvePath(std::move(path)));
mc.AssertRead(p);
if (flags & O_CREAT) {
throw NetFS::SystemError(EINVAL);
@@ -239,16 +249,17 @@ VolumeServer::open(const NetFS::ReqEnv re, const std::string path, Ice::Int flag
if (fd == -1) {
throw NetFS::SystemError(errno);
}
- return Ice::uncheckedCast<NetFS::FilePrx>(ice.adapter->addFacetWithUUID(
- std::make_shared<FileServer>(fd, converter), "v01"));
+ return Ice::uncheckedCast<NetFS::FilePrx>(
+ ice.adapter->addFacetWithUUID(std::make_shared<FileServer>(fd, converter), "v01"));
}
NetFS::FilePrxPtr
-VolumeServer::create(const NetFS::ReqEnv re, const std::string path, Ice::Int flags, Ice::Int mode, const Ice::Current & ice)
+// cppcheck-suppress passedByValue;
+VolumeServer::create(const NetFS::ReqEnv re, std::string path, Ice::Int flags, Ice::Int mode, const Ice::Current & ice)
{
ModeCheck mc(re, root, userLookup, groupLookup);
errno = 0;
- std::filesystem::path p(resolvePath(path));
+ std::filesystem::path p(resolvePath(std::move(path)));
mc.AssertWriteParent(p);
int fd = ::open(p.c_str(), O_CREAT | O_EXCL | flags, mode);
if (fd == -1) {
@@ -259,33 +270,35 @@ VolumeServer::create(const NetFS::ReqEnv re, const std::string path, Ice::Int fl
::unlink(p.c_str());
throw NetFS::SystemError(errno);
}
- return Ice::uncheckedCast<NetFS::FilePrx>(ice.adapter->addFacetWithUUID(
- std::make_shared<FileServer>(fd, converter), "v01"));
+ return Ice::uncheckedCast<NetFS::FilePrx>(
+ ice.adapter->addFacetWithUUID(std::make_shared<FileServer>(fd, converter), "v01"));
}
NetFS::DirectoryPrxPtr
-VolumeServer::opendir(const NetFS::ReqEnv re, const std::string path, const Ice::Current & ice)
+// cppcheck-suppress passedByValue;
+VolumeServer::opendir(const NetFS::ReqEnv re, std::string path, const Ice::Current & ice)
{
ModeCheck mc(re, root, userLookup, groupLookup);
errno = 0;
- std::filesystem::path p(resolvePath(path));
+ std::filesystem::path p(resolvePath(std::move(path)));
mc.AssertRead(p);
DIR * od = ::opendir(p.c_str());
if (!od) {
throw NetFS::SystemError(errno);
}
- return Ice::uncheckedCast<NetFS::DirectoryPrx>(ice.adapter->addFacetWithUUID(
- std::make_shared<DirectoryServer>(od, converter), "v02"));
+ return Ice::uncheckedCast<NetFS::DirectoryPrx>(
+ ice.adapter->addWithUUID(std::make_shared<DirectoryServer>(od, converter)));
}
void
-VolumeServer::mkdir(const NetFS::ReqEnv re, const std::string path, Ice::Int mode, const Ice::Current&)
+// cppcheck-suppress passedByValue;
+VolumeServer::mkdir(const NetFS::ReqEnv re, std::string path, Ice::Int mode, const Ice::Current &)
{
ModeCheck mc(re, root, userLookup, groupLookup);
errno = 0;
- std::filesystem::path p(resolvePath(path));
+ std::filesystem::path p(resolvePath(std::move(path)));
mc.AssertWriteParent(p);
- if (::mkdir(p.c_str(), mode) != 0) {
+ if (::mkdir(p.c_str(), safe {mode}) != 0) {
throw NetFS::SystemError(errno);
}
if (::chown(p.c_str(), mc.myu, mc.myg) != 0) {
@@ -295,11 +308,12 @@ VolumeServer::mkdir(const NetFS::ReqEnv re, const std::string path, Ice::Int mod
}
void
-VolumeServer::rmdir(const NetFS::ReqEnv re, const std::string path, const Ice::Current&)
+// cppcheck-suppress passedByValue;
+VolumeServer::rmdir(const NetFS::ReqEnv re, std::string path, const Ice::Current &)
{
ModeCheck mc(re, root, userLookup, groupLookup);
errno = 0;
- std::filesystem::path p(resolvePath(path));
+ std::filesystem::path p(resolvePath(std::move(path)));
mc.AssertWriteParent(p);
if (::rmdir(p.c_str()) != 0) {
throw NetFS::SystemError(errno);
@@ -307,10 +321,10 @@ VolumeServer::rmdir(const NetFS::ReqEnv re, const std::string path, const Ice::C
}
std::filesystem::path
-normalizedAppend(std::filesystem::path out, const std::filesystem::path & in)
+normalizedAppend(std::filesystem::path out, const std::filesystem::path && in)
{
unsigned int depth = 0;
- for(auto e : in) {
+ for (const auto & e : in) {
if (e.empty() || e == "." || e == "/") {
continue;
}
@@ -330,8 +344,7 @@ normalizedAppend(std::filesystem::path out, const std::filesystem::path & in)
}
std::filesystem::path
-VolumeServer::resolvePath(const std::string & path) const
+VolumeServer::resolvePath(std::string && path) const
{
- return normalizedAppend(root, path);
+ return normalizedAppend(root, std::move(path));
}
-
diff --git a/netfs/daemon/daemonVolume.h b/netfs/daemon/daemonVolume.h
index 1549f6d..c7d6c56 100644
--- a/netfs/daemon/daemonVolume.h
+++ b/netfs/daemon/daemonVolume.h
@@ -1,54 +1,55 @@
-#ifndef DAEMONVOLUME_H
-#define DAEMONVOLUME_H
+#pragma once
-#include <volume.h>
-#include <shared_mutex>
+#include <entries.h>
+#include <entryResolver.h>
#include <filesystem>
-#include <entCache.h>
+#include <shared_mutex>
#include <typeConverter.h>
+#include <volume.h>
class VolumeServer : public NetFS::Volume {
- public:
- VolumeServer(const std::filesystem::path & root, const EntCache<User> &, const EntCache<Group> &);
- virtual ~VolumeServer();
+public:
+ VolumeServer(const std::filesystem::path & root, const EntryResolverPtr<User> &, const EntryResolverPtr<Group> &);
- virtual NetFS::DirectoryPrxPtr opendir(const NetFS::ReqEnv, const std::string path, const Ice::Current&) override;
+ NetFS::DirectoryPrxPtr opendir(const NetFS::ReqEnv, std::string path, const Ice::Current &) override;
- virtual void mkdir(const NetFS::ReqEnv, const std::string path, Ice::Int id, const Ice::Current&) override;
- virtual void rmdir(const NetFS::ReqEnv, const std::string path, const Ice::Current&) override;
+ void mkdir(const NetFS::ReqEnv, std::string path, Ice::Int id, const Ice::Current &) override;
+ void rmdir(const NetFS::ReqEnv, std::string path, const Ice::Current &) override;
- virtual void truncate(const NetFS::ReqEnv, const std::string path, Ice::Long size, const Ice::Current&) override;
+ void truncate(const NetFS::ReqEnv, std::string path, Ice::Long size, const Ice::Current &) override;
- virtual void unlink(const NetFS::ReqEnv, const std::string path, const Ice::Current&) override;
+ void unlink(const NetFS::ReqEnv, std::string path, const Ice::Current &) override;
- virtual NetFS::FilePrxPtr open(const NetFS::ReqEnv, const std::string path, Ice::Int flags, const Ice::Current&) override;
- virtual NetFS::FilePrxPtr create(const NetFS::ReqEnv, const std::string path, Ice::Int flags, Ice::Int mode, const Ice::Current&) override;
+ NetFS::FilePrxPtr open(const NetFS::ReqEnv, std::string path, Ice::Int flags, const Ice::Current &) override;
+ NetFS::FilePrxPtr create(
+ const NetFS::ReqEnv, std::string path, Ice::Int flags, Ice::Int mode, const Ice::Current &) override;
- virtual NetFS::VFS statfs(const NetFS::ReqEnv, const std::string path, const Ice::Current&) override;
+ NetFS::VFS statfs(const NetFS::ReqEnv, std::string path, const Ice::Current &) override;
- virtual Ice::Int access(const NetFS::ReqEnv, const std::string path, Ice::Int mode, const Ice::Current&) override;
- virtual NetFS::Attr getattr(const NetFS::ReqEnv, const std::string path, const Ice::Current&) override;
- virtual void mknod(const NetFS::ReqEnv, const std::string path, Ice::Int mode, Ice::Int dev, const Ice::Current&) override;
- virtual void symlink(const NetFS::ReqEnv, const std::string path1, const std::string path2, const Ice::Current&) override;
- virtual void link(const NetFS::ReqEnv, const std::string path1, const std::string path2, const Ice::Current&) override;
- virtual void rename(const NetFS::ReqEnv, const std::string path1, const std::string path2, const Ice::Current&) override;
- virtual std::string readlink(const NetFS::ReqEnv, const std::string path, const Ice::Current&) override;
- virtual void chmod(const NetFS::ReqEnv, const std::string path, Ice::Int mode, const Ice::Current&) override;
- virtual void chown(const NetFS::ReqEnv, const std::string path, Ice::Int uid, Ice::Int gid, const Ice::Current&) override;
- virtual void utimens(const NetFS::ReqEnv, const std::string path, Ice::Long, Ice::Long, Ice::Long, Ice::Long, const Ice::Current&) override;
+ Ice::Int access(const NetFS::ReqEnv, std::string path, Ice::Int mode, const Ice::Current &) override;
+ NetFS::Attr getattr(const NetFS::ReqEnv, std::string path, const Ice::Current &) override;
+ void mknod(const NetFS::ReqEnv, std::string path, Ice::Int mode, Ice::Int dev, const Ice::Current &) override;
+ // cppcheck-suppress passedByValue;
+ void symlink(const NetFS::ReqEnv, const std::string path1, const std::string path2, const Ice::Current &) override;
+ void link(const NetFS::ReqEnv, std::string path1, std::string path2, const Ice::Current &) override;
+ // cppcheck-suppress passedByValue;
+ void rename(const NetFS::ReqEnv, std::string path1, const std::string path2, const Ice::optional<Ice::Int>,
+ const Ice::Current &) override;
+ std::string readlink(const NetFS::ReqEnv, std::string path, const Ice::Current &) override;
+ void chmod(const NetFS::ReqEnv, std::string path, Ice::Int mode, const Ice::Current &) override;
+ void chown(const NetFS::ReqEnv, std::string path, Ice::Int uid, Ice::Int gid, const Ice::Current &) override;
+ void utimens(const NetFS::ReqEnv, std::string path, Ice::Long, Ice::Long, Ice::Long, Ice::Long,
+ const Ice::Current &) override;
- virtual void disconnect(const Ice::Current&) override;
+ void disconnect(const Ice::Current &) override;
- protected:
- std::filesystem::path resolvePath(const std::string & path) const;
+protected:
+ inline std::filesystem::path resolvePath(std::string && path) const;
- private:
- const std::filesystem::path root;
+private:
+ const std::filesystem::path root;
- const EntCache<User> & userLookup;
- const EntCache<Group> & groupLookup;
- EntryTypeConverter converter;
+ EntryResolver<User> & userLookup;
+ EntryResolver<Group> & groupLookup;
+ NetFS::EntryTypeConverter converter;
};
-
-#endif
-
diff --git a/netfs/daemon/modeCheck.cpp b/netfs/daemon/modeCheck.cpp
index 91bbe20..a9096fd 100644
--- a/netfs/daemon/modeCheck.cpp
+++ b/netfs/daemon/modeCheck.cpp
@@ -1,13 +1,20 @@
#include "modeCheck.h"
-#include <entCache.h>
#include <exceptions.h>
-ModeCheck::ModeCheck(const NetFS::ReqEnv & re, const std::filesystem::path & r, const EntCache<User> & u, const EntCache<Group> & g) :
- myu(u.getEntry(re.user)->id),
- myg(g.getEntry(re.grp)->id),
- root(r),
- userLookup(u),
- groupLookup(g)
+template<typename Ex, typename T, typename... ExP>
+T &&
+assert_truthy(T && v, ExP &&... p)
+{
+ if (!v) {
+ throw Ex {std::forward<ExP>(p)...};
+ }
+ return std::forward<T>(v);
+}
+
+ModeCheck::ModeCheck(const NetFS::ReqEnv & re, const std::filesystem::path & r, const EntryResolver<User> & u,
+ const EntryResolver<Group> & g) :
+ myu(assert_truthy<NetFS::SystemError>(u.getEntry(re.user), EPERM)->id),
+ myg(assert_truthy<NetFS::SystemError>(g.getEntry(re.grp), EPERM)->id), root(r), userLookup(u), groupLookup(g)
{
}
@@ -20,6 +27,7 @@ ModeCheck::AssertReadParent(const std::filesystem::path & p) const
}
void
+// NOLINTNEXTLINE(misc-no-recursion)
ModeCheck::AssertRead(const std::filesystem::path & p) const
{
if (p != root) {
@@ -67,7 +75,7 @@ ModeCheck::AssertWritePerms(const std::filesystem::path & p) const
struct stat
ModeCheck::lstat(const std::filesystem::path & p)
{
- struct stat s;
+ struct stat s { };
if (::lstat(p.c_str(), &s) != 0) {
throw NetFS::SystemError(errno);
}
@@ -77,30 +85,59 @@ ModeCheck::lstat(const std::filesystem::path & p)
bool
ModeCheck::ReadableBy(const struct stat & s, uid_t u, gid_t g) const
{
- if (u == 0) return true;
- if (s.st_mode & S_IROTH) return true;
- if (s.st_mode & S_IRUSR && s.st_uid == u) return true;
- if (s.st_mode & S_IRGRP && (s.st_gid == g || userLookup.getEntry(u)->group == s.st_gid || groupLookup.getEntry(s.st_gid)->hasMember(u))) return true;
+ if (u == 0) {
+ return true;
+ }
+ if (s.st_mode & S_IROTH) {
+ return true;
+ }
+ if (s.st_mode & S_IRUSR && s.st_uid == u) {
+ return true;
+ }
+ if (s.st_mode & S_IRGRP
+ && (s.st_gid == g || userLookup.getEntry(u)->group == s.st_gid
+ || groupLookup.getEntry(s.st_gid)->hasMember(u))) {
+ return true;
+ }
return false;
}
bool
ModeCheck::WritableBy(const struct stat & s, uid_t u, gid_t g) const
{
- if (u == 0) return true;
- if (s.st_mode & S_IWOTH) return true;
- if (s.st_mode & S_IWUSR && s.st_uid == u) return true;
- if (s.st_mode & S_IWGRP && (s.st_gid == g || userLookup.getEntry(u)->group == s.st_gid || groupLookup.getEntry(s.st_gid)->hasMember(u))) return true;
+ if (u == 0) {
+ return true;
+ }
+ if (s.st_mode & S_IWOTH) {
+ return true;
+ }
+ if (s.st_mode & S_IWUSR && s.st_uid == u) {
+ return true;
+ }
+ if (s.st_mode & S_IWGRP
+ && (s.st_gid == g || userLookup.getEntry(u)->group == s.st_gid
+ || groupLookup.getEntry(s.st_gid)->hasMember(u))) {
+ return true;
+ }
return false;
}
bool
ModeCheck::ExecutableBy(const struct stat & s, uid_t u, gid_t g) const
{
- if (u == 0 && (s.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR))) return true;
- if (s.st_mode & S_IXOTH) return true;
- if (s.st_mode & S_IXUSR && s.st_uid == u) return true;
- if (s.st_mode & S_IXGRP && (s.st_gid == g || userLookup.getEntry(u)->group == s.st_gid || groupLookup.getEntry(s.st_gid)->hasMember(u))) return true;
+ if (u == 0 && (s.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR))) {
+ return true;
+ }
+ if (s.st_mode & S_IXOTH) {
+ return true;
+ }
+ if (s.st_mode & S_IXUSR && s.st_uid == u) {
+ return true;
+ }
+ if (s.st_mode & S_IXGRP
+ && (s.st_gid == g || userLookup.getEntry(u)->group == s.st_gid
+ || groupLookup.getEntry(s.st_gid)->hasMember(u))) {
+ return true;
+ }
return false;
}
-
diff --git a/netfs/daemon/modeCheck.h b/netfs/daemon/modeCheck.h
index 78c1708..4a57317 100644
--- a/netfs/daemon/modeCheck.h
+++ b/netfs/daemon/modeCheck.h
@@ -1,35 +1,33 @@
-#ifndef NETFS_DAEMON_IOHELPERS
-#define NETFS_DAEMON_IOHELPERS
+#pragma once
+#include <entries.h>
+#include <entryResolver.h>
+#include <filesystem>
#include <sys/stat.h>
#include <types.h>
-#include <filesystem>
-#include <entCache.h>
class ModeCheck {
- public:
- ModeCheck(const NetFS::ReqEnv & re, const std::filesystem::path &, const EntCache<User> &, const EntCache<Group> &);
+public:
+ ModeCheck(const NetFS::ReqEnv & re, const std::filesystem::path &, const EntryResolver<User> &,
+ const EntryResolver<Group> &);
- void AssertReadParent(const std::filesystem::path &) const;
- void AssertRead(const std::filesystem::path &) const;
- void AssertWriteParent(const std::filesystem::path &) const;
- void AssertWrite(const std::filesystem::path &) const;
- void AssertWritePerms(const std::filesystem::path &) const;
+ void AssertReadParent(const std::filesystem::path &) const;
+ void AssertRead(const std::filesystem::path &) const;
+ void AssertWriteParent(const std::filesystem::path &) const;
+ void AssertWrite(const std::filesystem::path &) const;
+ void AssertWritePerms(const std::filesystem::path &) const;
- const uid_t myu;
- const gid_t myg;
- const std::filesystem::path & root;
+ const uid_t myu;
+ const gid_t myg;
+ const std::filesystem::path & root;
- bool ReadableBy(const struct stat &, uid_t u, gid_t g) const;
- bool WritableBy(const struct stat &, uid_t u, gid_t g) const;
- bool ExecutableBy(const struct stat &, uid_t u, gid_t g) const;
+ [[nodiscard]] bool ReadableBy(const struct stat &, uid_t u, gid_t g) const;
+ [[nodiscard]] bool WritableBy(const struct stat &, uid_t u, gid_t g) const;
+ [[nodiscard]] bool ExecutableBy(const struct stat &, uid_t u, gid_t g) const;
- private:
- static struct stat lstat(const std::filesystem::path &);
+private:
+ static struct stat lstat(const std::filesystem::path &);
- const EntCache<User> & userLookup;
- const EntCache<Group> & groupLookup;
+ const EntryResolver<User> & userLookup;
+ const EntryResolver<Group> & groupLookup;
};
-
-#endif
-
diff --git a/netfs/fuse/Jamfile.jam b/netfs/fuse/Jamfile.jam
index 6972e3f..4d000be 100644
--- a/netfs/fuse/Jamfile.jam
+++ b/netfs/fuse/Jamfile.jam
@@ -1,27 +1,48 @@
import package ;
-lib fuse : : <name>fuse ;
lib Glacier2 : : <name>Glacier2++11 ;
+constant FUSE_VER : 35 ;
+
+rule iceobj ( name : ice ) {
+ obj $(name) : $(ice) :
+ <toolset>tidy:<checker>none
+ <library>../ice//netfs-api
+ <implicit-dependency>../ice//netfs-api
+ <library>..//slicer
+ <include>.
+ ;
+}
+iceobj fuseConfig : fuseConfig.ice ;
+iceobj fuseMappers : fuseMappers.ice ;
lib netfs-client-configuration :
+ fuseConfig
+ fuseMappers
fuseConfig.ice
+ fuseMappers.ice
fuseConfigImpl.cpp
+ fuseMappersImpl.cpp
:
- <slicer>yes
+ <slicer>pure
<library>../ice//netfs-api
+ <library>../lib//netfs-common
<implicit-dependency>../ice//netfs-api
+ <implicit-dependency>fuseConfig
<library>..//Ice
<library>..//slicer
<library>..//adhocutil
+ <include>.
: :
<library>..//Ice
<library>..//slicer
+ <include>.
;
lib netfs-client :
netfs-client-configuration
[ glob *.cpp : fuseConfigImpl.cpp netfs.cpp ]
:
+ <define>FUSE_USE_VERSION=$(FUSE_VER)
<implicit-dependency>../ice//netfs-api
<library>netfs-client-configuration
<implicit-dependency>netfs-client-configuration
@@ -33,7 +54,9 @@ lib netfs-client :
<library>..//slicer
<library>..//adhocutil
<library>..//slicer-xml
+ <use>../..//fuse
: :
+ <define>FUSE_USE_VERSION=$(FUSE_VER)
<include>.
<library>../ice//netfs-api
<library>Glacier2
@@ -45,7 +68,7 @@ lib netfs-client :
exe netfs :
netfs.cpp :
<library>netfs-client
- <library>fuse
+ <library>../..//fuse
<library>..//adhocutil
;
diff --git a/netfs/fuse/fuseApp.cpp b/netfs/fuse/fuseApp.cpp
index 79fa0dd..c09a7e7 100644
--- a/netfs/fuse/fuseApp.cpp
+++ b/netfs/fuse/fuseApp.cpp
@@ -1,280 +1,320 @@
-#include <Glacier2/Router.h>
-#include <string.h>
#include "fuseApp.h"
-#include "lockHelpers.h"
#include "cache.impl.h"
+#include "fuseDirs.h" // IWYU pragma: keep - OpenDir definition
+#include "fuseFiles.h" // IWYU pragma: keep - OpenFile definition
+#include "fuseMappersImpl.h"
+#include "lockHelpers.h"
+#include <Glacier2/Router.h>
+#include <boost/lexical_cast.hpp>
+#include <compileTimeFormatter.h>
+#include <cstring>
+#include <defaultMapper.h>
#include <entCache.h>
+#include <numeric.h>
+#include <safeMapFind.h>
#include <slicer/slicer.h>
#include <uriParse.h>
-#include <safeMapFind.h>
-#include <compileTimeFormatter.h>
namespace AdHoc {
- template class Cache<struct stat, std::string>;
- template class CallCacheable<struct stat, std::string>;
+ template class Cache<struct stat, std::size_t>;
+ template class CallCacheable<struct stat, std::size_t>;
}
-NetFS::FuseApp::FuseApp(const Ice::StringSeq & a) :
- iceArgs(a),
- sessionOpened(false),
- openHandleId(0),
- converter(userLookup, groupLookup)
-{
-}
+namespace NetFS {
+ constexpr static unsigned short DEFAULT_PORT = 4000;
+ constexpr static unsigned short DEFAULT_MAX_MESSAGE_SIZE = 1024;
+ constexpr static unsigned short KILO_MULTIPLIER = 1024;
+
+ FuseApp::FuseApp(Ice::StringSeq args) : iceArgs(std::move(args)) { }
-NetFS::FuseApp::~FuseApp()
-{
- for (const OpenDirs::value_type & of : openDirs) {
- // LCOV_EXCL_START
- try {
- of.second->remote->close();
+ FuseApp::~FuseApp()
+ {
+ for (const OpenDirs::value_type & openDir : openDirs) {
+ // LCOV_EXCL_START
+ try {
+ openDir.second->remote->close();
+ }
+ catch (...) { // NOLINT(bugprone-empty-catch) - Can't do anything useful here
+ }
+ // LCOV_EXCL_STOP
}
- catch (...) {
- // Can't do anything useful here
+ for (const OpenFiles::value_type & openFile : openFiles) {
+ // LCOV_EXCL_START
+ try {
+ openFile.second->remote->close();
+ }
+ catch (...) { // NOLINT(bugprone-empty-catch) - Can't do anything useful here
+ }
+ // LCOV_EXCL_STOP
}
- // LCOV_EXCL_STOP
- }
- for (const OpenFiles::value_type & of : openFiles) {
- // LCOV_EXCL_START
- try {
- of.second->remote->close();
+ if (volume) {
+ try {
+ volume->disconnect();
+ }
+ catch (...) { // NOLINT(bugprone-empty-catch) - Can't do anything useful here
+ }
}
- catch (...) {
- // Can't do anything useful here
+ if (session) {
+ try {
+ session->destroy();
+ }
+ catch (...) { // NOLINT(bugprone-empty-catch) - Can't do anything useful here
+ }
}
- // LCOV_EXCL_STOP
- }
- if (volume) {
- try {
- volume->disconnect();
- }
- catch (...) {
- // Can't do anything useful here
+ if (ic) {
+ ic->destroy();
}
}
- if (session) {
- try {
- session->destroy();
+
+ void *
+ FuseApp::init(struct fuse_conn_info *, struct fuse_config *)
+ {
+ BOOST_ASSERT(!ic);
+ ic = Ice::initialize(iceArgs);
+ BOOST_ASSERT(ic);
+ if (!iceArgs.empty()) {
+ const auto & arg = iceArgs.front();
+ if (arg.find("://") != std::string::npos) {
+ fcr = configureFromUri(arg);
+ }
+ else if (auto colon = arg.find(':'); colon != std::string::npos) {
+ fcr = configureFromFile(arg.substr(0, colon), arg.substr(colon + 1));
+ }
}
- catch (...) {
- // Can't do anything useful here
+ if (!fcr->Mapper) {
+ fcr->Mapper = std::make_shared<Mapping::DefaultMapper>();
}
+ BOOST_ASSERT(fcr);
+ converter.mapper = fcr->Mapper;
+ clientSettings.MessageSizeMax
+ = ic->getProperties()->getPropertyAsIntWithDefault("Ice.MessageSizeMax", DEFAULT_MAX_MESSAGE_SIZE);
+ return this;
}
- if (ic) {
- ic->destroy();
- }
-}
-
-NetFS::Client::ConfigurationPtr
-NetFS::FuseApp::ReadConfiguration(const std::filesystem::path & path) const
-{
- auto s = Slicer::FileDeserializerFactory::createNew(path.extension().string(), path);
- return Slicer::DeserializeAnyWith<NetFS::Client::ConfigurationPtr>(s);
-}
-void *
-NetFS::FuseApp::init(struct fuse_conn_info *)
-{
- ic = Ice::initialize(iceArgs);
- fcr = configurator();
- return NULL;
-}
+ Client::ResourcePtr
+ FuseApp::configureFromFile(const std::filesystem::path & path, const std::string & resourceName)
+ {
+ auto deserialiser = Slicer::FileDeserializerFactory::createNew(path.extension().string(), path);
+ auto configuration = Slicer::DeserializeAnyWith<NetFS::Client::ConfigurationPtr>(deserialiser);
+ return AdHoc::safeMapLookup<Client::ResourceNotFound>(configuration->Resources, resourceName);
+ }
-NetFS::Client::ResourcePtr
-NetFS::FuseApp::configureFromFile(const std::string & configPath, const std::string & resourceName) const
-{
- return AdHoc::safeMapLookup<Client::ResourceNotFound>(ReadConfiguration(configPath)->Resources, resourceName);
-}
+ AdHocFormatter(IceEndpointFmt, "%? -h %? -p %?");
-AdHocFormatter(IceEndpointFmt, "%? -h %? -p %?");
-NetFS::Client::ResourcePtr
-NetFS::FuseApp::configureFromUri(const std::string & uriString) const
-{
- AdHoc::Uri uri(uriString);
+ Client::ResourcePtr
+ FuseApp::configureFromUri(const std::string & uriString)
+ {
+ const AdHoc::Uri uri(uriString);
- auto r = std::make_shared<NetFS::Client::Resource>();
- r->ExportName = uri.path->string();
- r->Endpoints.push_back(IceEndpointFmt::get(uri.scheme, uri.host, uri.port ? *uri.port : 4000));
- if (uri.password) {
- r->AuthToken = *uri.password;
+ auto resource = std::make_shared<NetFS::Client::Resource>();
+ resource->ExportName = uri.path->string();
+ resource->Endpoints.emplace_back(
+ IceEndpointFmt::get(uri.scheme, uri.host, uri.port ? *uri.port : DEFAULT_PORT));
+ if (uri.password) {
+ resource->AuthToken = *uri.password;
+ }
+ auto set = [&uri](const auto & param, auto & setting) {
+ if (auto value = uri.query.find(param); value != uri.query.end()) {
+ setting = boost::lexical_cast<std::remove_reference_t<decltype(setting)>>(value->second);
+ }
+ };
+ auto setWith = [&uri](const auto & param, auto & setting, auto && func) {
+ if (auto value = uri.query.find(param); value != uri.query.end()) {
+ setting = func(value->second);
+ }
+ };
+ set("async", resource->Async);
+ setWith("mapper", resource->Mapper, [&set, &setWith](auto && mapper) -> NetFS::Mapping::MapperPtr {
+ if (mapper == "hide") {
+ return std::make_shared<NetFS::Client::HideUnknownMapperImpl>();
+ }
+ if (mapper == "mask") {
+ auto mapperImpl = std::make_shared<NetFS::Client::MaskUnknownMapperImpl>();
+ set("mapper.unknownuser", mapperImpl->UnknownUser);
+ set("mapper.unknowngroup", mapperImpl->UnknownGroup);
+ setWith("mapper.usermask", mapperImpl->UserMask, Client::fromOctal);
+ setWith("mapper.groupmask", mapperImpl->GroupMask, Client::fromOctal);
+ return mapperImpl;
+ }
+ return {};
+ });
+ return resource;
}
- return r;
-}
-int
-NetFS::FuseApp::opt_parse(void *, const char * arg, int, struct fuse_args *)
-{
- if (strncmp(arg, "--Ice.", 6) == 0) {
- iceArgs.push_back(arg);
- return 0;
- }
- else if (strncmp(arg, "_netdev", 7) == 0) {
- return 0;
- }
- else if (arg[0] == '-') {
- return 1;
- }
- else if (!configurator) {
- if (strstr(arg, "://")) {
- configurator = std::bind(&NetFS::FuseApp::configureFromUri, this, arg);
+ int
+ FuseApp::opt_parse(void * data, const char * arg, int, struct fuse_args *)
+ {
+ auto & iceArgs = *static_cast<Ice::StringSeq *>(data);
+ std::string_view argV {arg};
+ if (argV.starts_with("--Ice.")) {
+ iceArgs.emplace_back(arg);
+ return 0;
}
- else {
- const char * colon = strchr(arg, ':');
- configurator = std::bind(&NetFS::FuseApp::configureFromFile, this, std::string(arg, colon), colon + 1);
+ if (argV == "_netdev") {
+ return 0;
+ }
+ if (argV.front() == '-') {
+ return 1;
+ }
+ if (iceArgs.empty() || iceArgs.front().starts_with("--Ice.")) {
+ iceArgs.emplace(iceArgs.begin(), arg);
+ return 0;
}
- return 0;
- }
- else if (mountPoint.empty()) {
- mountPoint = arg;
return 1;
}
- return 1;
-}
-void
-NetFS::FuseApp::connectSession()
-{
- if (!sessionOpened && ic->getDefaultRouter()) {
- Lock(_lock);
- auto router = Ice::checkedCast<Glacier2::RouterPrx>(ic->getDefaultRouter());
- session = router->createSession("", "");
- if (int acmTimeout = router->getACMTimeout() > 0) {
- Ice::ConnectionPtr conn = router->ice_getCachedConnection();
- conn->setACM(acmTimeout, IceUtil::None, Ice::ACMHeartbeat::HeartbeatAlways);
+ void
+ FuseApp::connectSession()
+ {
+ Lock(mutex);
+ if (!sessionOpened && ic->getDefaultRouter()) {
+ auto router = Ice::checkedCast<Glacier2::RouterPrx>(ic->getDefaultRouter());
+ session = router->createSession("", "");
+ if (int acmTimeout = router->getACMTimeout() > 0) {
+ Ice::ConnectionPtr conn = router->ice_getCachedConnection();
+ conn->setACM(acmTimeout, IceUtil::None, Ice::ACMHeartbeat::HeartbeatAlways);
+ }
+ sessionOpened = true;
}
- sessionOpened = true;
}
-}
-void
-NetFS::FuseApp::connectToService()
-{
- if (!service) {
- Lock(_lock);
- auto proxyAddr = fcr->ServiceIdentity;
- for (const auto & ep : fcr->Endpoints) {
- proxyAddr += ":" + ep;
- }
- service = Ice::checkedCast<NetFS::ServicePrx>(ic->stringToProxy(proxyAddr));
- if (!service) {
- throw std::runtime_error("Invalid service proxy: " + proxyAddr);
- }
+ FuseApp::CombinedSettings
+ FuseApp::combineSettings(const Settings & daemon, const Settings & client)
+ {
+ return {safe {std::min(daemon.MessageSizeMax.value_or(DEFAULT_MAX_MESSAGE_SIZE),
+ client.MessageSizeMax.value_or(DEFAULT_MAX_MESSAGE_SIZE))
+ * KILO_MULTIPLIER}};
}
-}
-void
-NetFS::FuseApp::connectToVolume()
-{
- if (!volume) {
- Lock(_lock);
- volume = service->connect(fcr->ExportName, fcr->AuthToken);
- if (!volume) {
- throw std::runtime_error("Invalid filesystem proxy");
+ void
+ FuseApp::connectToService()
+ {
+ Lock(mutex);
+ if (!service) {
+ auto proxyAddr = fcr->ServiceIdentity;
+ for (const auto & endpoint : fcr->Endpoints) {
+ proxyAddr += ":" + endpoint;
+ }
+ service = Ice::checkedCast<NetFS::ServicePrx>(ic->stringToProxy(proxyAddr));
+ if (!service) {
+ throw std::runtime_error("Invalid service proxy: " + proxyAddr);
+ }
+ daemonSettings = service->getSettings();
+ combinedSettings = combineSettings(*daemonSettings, clientSettings);
}
}
-}
-void
-NetFS::FuseApp::connectHandles()
-{
- for (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 & !O_CREAT);
- }
- }
- for (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
+ FuseApp::connectToVolume(bool force)
+ {
+ Lock(mutex);
+ if (force || !volume) {
+ volume = service->connect(fcr->ExportName, fcr->AuthToken);
+ if (!volume) {
+ throw std::runtime_error("Invalid filesystem proxy");
+ }
}
}
-}
-void
-NetFS::FuseApp::verifyConnection()
-{
- Lock(_lock);
- if (session) {
- try {
- session->ice_ping();
+ void
+ FuseApp::connectHandles()
+ {
+ for (const OpenFiles::value_type & openFile : openFiles) {
+ try {
+ openFile.second->remote->ice_ping();
+ }
+ catch (const Ice::ObjectNotExistException &) {
+ openFile.second->remote
+ = volume->open(reqEnv(), openFile.second->path, openFile.second->flags & !O_CREAT);
+ }
}
- catch (const Ice::Exception &) {
- session = NULL;
- sessionOpened = false;
+ for (const OpenDirs::value_type & openDir : openDirs) {
+ try {
+ openDir.second->remote->ice_ping();
+ }
+ catch (const Ice::ObjectNotExistException &) {
+ openDir.second->remote = volume->opendir(reqEnv(), openDir.second->path);
+ }
}
}
- if (service) {
- try {
- service->ice_ping();
- }
- catch (const Ice::Exception &) {
- service = NULL;
+
+ void
+ FuseApp::verifyConnection()
+ {
+ Lock(mutex);
+ if (session) {
+ try {
+ session->ice_ping();
+ }
+ catch (const Ice::Exception &) {
+ session = nullptr;
+ sessionOpened = false;
+ }
}
- }
- if (volume) {
- try {
- volume->ice_ping();
+ if (service) {
+ try {
+ service->ice_ping();
+ }
+ catch (const Ice::Exception &) {
+ service = nullptr;
+ }
}
- catch (const Ice::Exception &) {
- volume = NULL;
+ if (volume) {
+ try {
+ volume->ice_ping();
+ }
+ catch (const Ice::Exception &) {
+ volume = nullptr;
+ }
}
}
-}
-int
-NetFS::FuseApp::onError(const std::exception & e) throw()
-{
- if (dynamic_cast<const Ice::SocketException *>(&e) || dynamic_cast<const Ice::TimeoutException *>(&e)) {
- log(LOG_ERR, e.what());
- try {
- verifyConnection();
- connectSession();
- connectToService();
- connectToVolume();
- connectHandles();
- return 0;
+ int
+ FuseApp::onError(const std::exception & error) noexcept
+ {
+ if (dynamic_cast<const Ice::SocketException *>(&error) || dynamic_cast<const Ice::TimeoutException *>(&error)) {
+ log(LOG_ERR, error.what());
+ try {
+ verifyConnection();
+ connectSession();
+ connectToService();
+ connectToVolume(false);
+ connectHandles();
+ return 0;
+ }
+ catch (...) {
+ return -EIO;
+ }
}
- catch (...) {
- return -EIO;
+ if (dynamic_cast<const Ice::RequestFailedException *>(&error)) {
+ try {
+ connectToVolume(true);
+ connectHandles();
+ return 0;
+ }
+ catch (...) {
+ return -EIO;
+ }
}
- }
- if (dynamic_cast<const Ice::RequestFailedException *>(&e)) {
- try {
- volume = NULL;
- connectToVolume();
- connectHandles();
- return 0;
+ if (dynamic_cast<const NetFS::AuthError *>(&error)) {
+ return -EPERM;
}
- catch (...) {
- return -EIO;
- }
- }
- if (dynamic_cast<const NetFS::AuthError *>(&e)) {
- return -EPERM;
+ return FuseAppBase::onError(error);
}
- return FuseAppBase::onError(e);
-}
-void
-NetFS::FuseApp::beforeOperation()
-{
- connectSession();
- connectToService();
- connectToVolume();
-}
+ void
+ FuseApp::beforeOperation()
+ {
+ connectSession();
+ connectToService();
+ connectToVolume(false);
+ }
-NetFS::ReqEnv
-NetFS::FuseApp::reqEnv()
-{
- struct fuse_context * c = fuse_get_context();
- NetFS::ReqEnv re;
- userLookup.getName(c->uid, &re.user);
- groupLookup.getName(c->gid, &re.grp);
- return re;
+ ReqEnv
+ FuseApp::reqEnv()
+ {
+ struct fuse_context * context = fuseGetContext();
+ auto transportContext = converter.mapper->mapFileSystem(safe {context->uid}, safe {context->gid});
+ return {.user = std::move(transportContext.username), .grp = std::move(transportContext.groupname)};
+ }
}
-
diff --git a/netfs/fuse/fuseApp.h b/netfs/fuse/fuseApp.h
index 23738b3..8dcc2ec 100644
--- a/netfs/fuse/fuseApp.h
+++ b/netfs/fuse/fuseApp.h
@@ -1,149 +1,127 @@
-#ifndef NETFS_FUSE_H
-#define NETFS_FUSE_H
+#pragma once
-#include <shared_mutex>
-#include <functional>
-#include <filesystem>
-#include <Ice/Ice.h>
+#include "cache.h"
+#include "fuseAppBase.h"
+#include "fuseConfig.h"
#include <Glacier2/Session.h>
-#include <service.h>
+#include <Ice/Ice.h>
+#include <c++11Helpers.h>
#include <entCache.h>
+#include <filesystem>
+#include <service.h>
+#include <shared_mutex>
#include <typeConverter.h>
-#include "fuseAppBase.h"
-#include "fuseConfig.h"
-#include "cache.h"
#include <visibility.h>
-#include <boost/icl/interval_map.hpp>
namespace NetFS {
- class DLL_PUBLIC FuseApp : public FuseAppBase {
- private:
- class OpenDir {
- public:
- OpenDir(DirectoryPrxPtr remote, const std::string & path);
-
- DirectoryPrxPtr remote;
- DirectoryV2PrxPtr remoteV2;
- const std::string path;
- };
- typedef std::shared_ptr<OpenDir> OpenDirPtr;
- typedef std::map<int, OpenDirPtr> OpenDirs;
-
- class OpenFile {
- public:
- OpenFile(FilePrxPtr remote, const std::string & path, int flags);
-
- void flush();
- void wait() const;
-
- FilePrxPtr remote;
- const std::string path;
- const int flags;
- class WriteState;
- typedef boost::icl::interval_map<Ice::Long, std::shared_ptr<WriteState>> BGs;
- static BGs::interval_type range(off_t, size_t);
- BGs bg;
- mutable std::shared_mutex _lock;
- };
- typedef std::shared_ptr<OpenFile> OpenFilePtr;
- typedef std::map<int, OpenFilePtr> OpenFiles;
-
- public:
- FuseApp(const Ice::StringSeq &);
- ~FuseApp();
-
- private:
- void * init (struct fuse_conn_info * info) override;
- int opt_parse(void *, const char * arg, int key, struct fuse_args *) override;
-
- void connectSession();
- void connectToService();
- void connectToVolume();
- void connectHandles();
- void verifyConnection();
-
- public:
- // misc
- int access(const char * p, int a) override;
- int getattr(const char * p, struct stat * s) override;
- int fgetattr(const char *, struct stat *, struct fuse_file_info *) override;
- int chmod(const char *, mode_t) override;
- int chown(const char *, uid_t, gid_t) override;
- int link(const char *, const char *) override;
- int readlink(const char *, char *, size_t) override;
- int rename(const char *, const char *) override;
- int symlink(const char *, const char *) override;
- int unlink(const char *) override;
- int utimens(const char *, const struct timespec tv[2]) override;
- int mknod(const char *, mode_t, dev_t) override;
- // dirs
- int opendir(const char * p, struct fuse_file_info * fi) override;
- int releasedir(const char *, struct fuse_file_info * fi) override;
- int readdir(const char *, void * buf, fuse_fill_dir_t filler, off_t, struct fuse_file_info * fi) override;
- int mkdir(const char *, mode_t) override;
- int rmdir(const char *) override;
- // files
- int open(const char * p, struct fuse_file_info * fi) override;
- int create(const char *, mode_t, struct fuse_file_info *) override;
- int flush(const char *, struct fuse_file_info * fi) override;
- int release(const char *, struct fuse_file_info * fi) override;
- int read(const char *, char * buf, size_t s, off_t o, struct fuse_file_info * fi) override;
- int write(const char *, const char * buf, size_t s, off_t o, struct fuse_file_info * fi) override;
- int truncate(const char *, off_t) override;
- int ftruncate(const char *, off_t, struct fuse_file_info *) override;
- // fs
- int statfs(const char *, struct statvfs *) override;
- // stuff
- int onError(const std::exception & err) throw() override;
- void beforeOperation() override;
-
- virtual struct fuse_context * fuse_get_context() = 0;
-
- protected:
- typedef std::function<Client::ResourcePtr()> Configurator;
- Configurator configurator;
- virtual NetFS::Client::ConfigurationPtr ReadConfiguration(const std::filesystem::path &) const;
- virtual NetFS::Client::ResourcePtr configureFromFile(const std::string &, const std::string &) const;
- virtual NetFS::Client::ResourcePtr configureFromUri(const std::string &) const;
-
- private:
- template<typename Handle, typename ... Params>
- void setProxy(uint64_t & fh, const Params & ...);
- template<typename Handle>
- Handle getProxy(uint64_t localID);
- template<typename Handle>
- void clearProxy(uint64_t localID);
- template<typename Handle>
- std::map<int, Handle> & getMap();
-
- template<typename Rtn, typename F> static inline Rtn
- waitOnWriteRangeAndThen(size_t s, off_t o, const OpenFilePtr & of, const F & f);
-
- ReqEnv reqEnv();
-
- Ice::StringSeq iceArgs;
- Ice::CommunicatorPtr ic;
- Client::ResourcePtr fcr;
- mutable std::shared_mutex _proxymaplock;
-
- NetFS::VolumePrxPtr volume;
- NetFS::ServicePrxPtr service;
- Glacier2::SessionPrxPtr session;
- bool sessionOpened;
-
- std::string mountPoint;
-
- OpenDirs openDirs;
- OpenFiles openFiles;
- int openHandleId;
-
- EntCache<User> userLookup;
- EntCache<Group> groupLookup;
- EntryTypeConverter converter;
-
- typedef AdHoc::Cache<struct stat, std::string> StatCache;
- StatCache statCache;
+ class DLL_PUBLIC FuseApp : public FuseAppBaseT<FuseApp> {
+ public:
+ using FuseHandleTypeId = decltype(fuse_file_info::fh);
+ class OpenDir;
+ using OpenDirPtr = std::shared_ptr<OpenDir>;
+ using OpenDirs = std::map<FuseHandleTypeId, OpenDirPtr>;
+
+ class OpenFile;
+ using OpenFilePtr = std::shared_ptr<OpenFile>;
+ using OpenFiles = std::map<FuseHandleTypeId, OpenFilePtr>;
+
+ explicit FuseApp(Ice::StringSeq);
+ SPECIAL_MEMBERS_DELETE(FuseApp);
+ ~FuseApp() override;
+
+ // lifecycle
+ void * init(struct fuse_conn_info * info, struct fuse_config * cfg) override;
+ // NOLINTNEXTLINE(readability-identifier-naming)
+ static int opt_parse(void * data, const char * arg, int, struct fuse_args *);
+
+ protected:
+ void connectSession();
+ virtual void connectToService();
+ void connectToVolume(bool force);
+ void connectHandles();
+ void verifyConnection();
+
+ struct CombinedSettings {
+ size_t messageSizeMax {};
+ };
+
+ public:
+ // misc
+ int access(const char * path, int accessMode) override;
+ int getattr(const char * path, struct stat *, struct fuse_file_info *) override;
+ int chmod(const char * path, mode_t, struct fuse_file_info *) override;
+ int chown(const char * path, uid_t, gid_t, struct fuse_file_info *) override;
+ int link(const char * path1, const char * path2) override;
+ int readlink(const char * path, char *, size_t) override;
+ int rename(const char * path1, const char * path2, unsigned int) override;
+ int symlink(const char * path1, const char * path2) override;
+ int unlink(const char * path) override;
+ // NOLINTNEXTLINE(*-c-arrays)
+ int utimens(const char * path, const struct timespec times[2], struct fuse_file_info *) override;
+ int mknod(const char * path, mode_t, dev_t) override;
+ // dirs
+ int opendir(const char * path, struct fuse_file_info *) override;
+ int releasedir(const char * path, struct fuse_file_info *) override;
+ int readdir(const char * path, void * buf, fuse_fill_dir_t filler, off_t, struct fuse_file_info *,
+ enum fuse_readdir_flags) override;
+ int mkdir(const char * path, mode_t) override;
+ int rmdir(const char * path) override;
+ // files
+ int open(const char * path, struct fuse_file_info *) override;
+ int create(const char * path, mode_t, struct fuse_file_info *) override;
+ int flush(const char * path, struct fuse_file_info *) override;
+ int release(const char * path, struct fuse_file_info *) override;
+ int read(const char * path, char * buf, size_t size, off_t offset, struct fuse_file_info *) override;
+ int write(const char * path, const char * buf, size_t size, off_t offset, struct fuse_file_info *) override;
+ ssize_t copy_file_range(const char * path, struct fuse_file_info *, off_t, const char *,
+ struct fuse_file_info *, off_t, size_t, int) override;
+ int truncate(const char * path, off_t, struct fuse_file_info *) override;
+ // fs
+ int statfs(const char * path, struct statvfs *) override;
+ // stuff
+ int onError(const std::exception & err) noexcept override;
+ void beforeOperation() override;
+
+ virtual struct fuse_context * fuseGetContext() = 0;
+
+ static NetFS::Client::ResourcePtr configureFromFile(const std::filesystem::path &, const std::string &);
+ static NetFS::Client::ResourcePtr configureFromUri(const std::string &);
+ static CombinedSettings combineSettings(const Settings & daemon, const Settings & client);
+
+ protected:
+ template<typename Handle, typename... Params> void setProxy(FuseHandleTypeId &, const Params &...);
+ template<typename Handle> Handle getProxy(FuseHandleTypeId localID);
+ template<typename Handle> void clearProxy(FuseHandleTypeId localID);
+ template<typename Handle> std::map<FuseHandleTypeId, Handle> & getMap();
+
+ template<typename Callback>
+ static inline auto waitOnWriteRangeAndThen(
+ size_t size, off_t offset, const OpenFilePtr &, const Callback & callback);
+
+ ReqEnv reqEnv();
+
+ Ice::StringSeq iceArgs;
+ Ice::CommunicatorPtr ic;
+ Client::ResourcePtr fcr;
+ mutable std::shared_mutex proxyMapMutex;
+
+ NetFS::VolumePrxPtr volume;
+ NetFS::ServicePrxPtr service;
+ Glacier2::SessionPrxPtr session;
+ NetFS::SettingsPtr daemonSettings;
+ NetFS::Settings clientSettings;
+ CombinedSettings combinedSettings;
+ bool sessionOpened {false};
+
+ std::string mountPoint;
+
+ OpenDirs openDirs;
+ OpenFiles openFiles;
+ FuseHandleTypeId openHandleId {};
+
+ EntryTypeConverter converter;
+
+ using StatCache = AdHoc::Cache<struct stat, std::size_t>;
+ StatCache statCache;
};
}
-
-#endif
diff --git a/netfs/fuse/fuseApp.impl.h b/netfs/fuse/fuseApp.impl.h
index 8dd9f1e..322ed46 100644
--- a/netfs/fuse/fuseApp.impl.h
+++ b/netfs/fuse/fuseApp.impl.h
@@ -1,43 +1,38 @@
-#ifndef NETFS_FUSE_COMMON_H
-#define NETFS_FUSE_COMMON_H
+#pragma once
#include "fuseApp.h"
#include <lockHelpers.h>
#include <safeMapFind.h>
namespace NetFS {
- template<typename Handle, typename ... Params>
+ template<typename Handle, typename... Params>
void
- FuseApp::setProxy(uint64_t & fh, const Params & ... params)
+ FuseApp::setProxy(uint64_t & fileHandle, const Params &... params)
{
auto & map = getMap<Handle>();
- Lock(_proxymaplock);
- while (map.find(fh = ++openHandleId) != map.end()) ;
- map.emplace(fh, std::make_shared<typename Handle::element_type>(params...));
+ Lock(proxyMapMutex);
+ while (map.find(fileHandle = ++openHandleId) != map.end()) { }
+ map.emplace(fileHandle, std::make_shared<typename Handle::element_type>(params...));
}
template<typename Handle>
Handle
- FuseApp::getProxy(uint64_t localID)
+ FuseApp::getProxy(uint64_t localId)
{
const auto & map = getMap<Handle>();
- SharedLock(_proxymaplock);
- auto i = map.find(localID);
- if (i != map.end()) {
- return i->second;
+ SharedLock(proxyMapMutex);
+ if (auto proxyIter = map.find(localId); proxyIter != map.end()) {
+ return proxyIter->second;
}
throw NetFS::SystemError(EBADF);
}
template<typename Handle>
void
- FuseApp::clearProxy(uint64_t localID)
+ FuseApp::clearProxy(uint64_t localId)
{
auto & map = getMap<Handle>();
- Lock(_proxymaplock);
- map.erase(localID);
+ Lock(proxyMapMutex);
+ map.erase(localId);
}
}
-
-#endif
-
diff --git a/netfs/fuse/fuseAppBase.cpp b/netfs/fuse/fuseAppBase.cpp
index 3eadd8f..838f1d9 100644
--- a/netfs/fuse/fuseAppBase.cpp
+++ b/netfs/fuse/fuseAppBase.cpp
@@ -1,198 +1,288 @@
#include "fuseAppBase.h"
-#include <errno.h>
-#include <assert.h>
-#include <stdio.h>
+#include <boost/assert.hpp>
+#include <cassert>
+#include <cerrno>
+#include <cstdio>
+#include <cstdlib>
#include <unistd.h>
-#include <stdlib.h>
-#include <typeinfo>
FuseAppBase * FuseAppBase::fuseApp;
FuseAppBase::FuseAppBase()
{
+ BOOST_ASSERT(!fuseApp);
+ BOOST_ASSERT(this);
+ fuseApp = this;
}
+
FuseAppBase::~FuseAppBase()
{
+ BOOST_ASSERT(fuseApp);
+ fuseApp = nullptr;
}
+
// LCOV_EXCL_START
// These are all excluded from coverage because it is impossible
// to call them in a realistic manner. They exist only as the default
// implementation of the function which is never passed to libfuse
// unless it is overridden.
-void * FuseAppBase::init(fuse_conn_info*)
-{
- return NULL;
-}
-int FuseAppBase::opt_parse(void*, const char *, int, fuse_args*)
+void *
+FuseAppBase::init(fuse_conn_info *, fuse_config *)
{
- return 1;
+ return nullptr;
}
-int FuseAppBase::access(const char *, int)
+
+int
+FuseAppBase::access(const char *, int)
{
return -ENOSYS;
}
-int FuseAppBase::chmod(const char *, mode_t)
+
+int
+FuseAppBase::chmod(const char *, mode_t, struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::chown(const char *, uid_t, gid_t)
+
+int
+FuseAppBase::chown(const char *, uid_t, gid_t, struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::create(const char *, mode_t, struct fuse_file_info *)
+
+int
+FuseAppBase::create(const char *, mode_t, struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::fgetattr(const char *, struct stat *, struct fuse_file_info *)
+
+int
+FuseAppBase::getattr(const char *, struct stat *, struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::flush(const char *, struct fuse_file_info *)
+
+int
+FuseAppBase::flush(const char *, struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::fsync(const char *, int, struct fuse_file_info *)
+
+int
+FuseAppBase::fsync(const char *, int, struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::fsyncdir(const char *, int, struct fuse_file_info *)
+
+int
+FuseAppBase::fsyncdir(const char *, int, struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::ftruncate(const char *, off_t, struct fuse_file_info *)
+
+int
+FuseAppBase::truncate(const char *, off_t, struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::getattr(const char *, struct stat *)
+
+int
+FuseAppBase::getxattr(const char *, const char *, char *, size_t)
{
return -ENOSYS;
}
-int FuseAppBase::getxattr(const char *, const char *, char *, size_t)
+
+int
+FuseAppBase::link(const char *, const char *)
{
return -ENOSYS;
}
-int FuseAppBase::link(const char *, const char *)
+
+int
+FuseAppBase::listxattr(const char *, char *, size_t)
{
return -ENOSYS;
}
-int FuseAppBase::listxattr(const char *, char *, size_t)
+
+int
+FuseAppBase::mkdir(const char *, mode_t)
{
return -ENOSYS;
}
-int FuseAppBase::mkdir(const char *, mode_t)
+
+int
+FuseAppBase::mknod(const char *, mode_t, dev_t)
{
return -ENOSYS;
}
-int FuseAppBase::mknod(const char *, mode_t, dev_t)
+
+int
+FuseAppBase::open(const char *, struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::open(const char *, struct fuse_file_info *)
+
+int
+FuseAppBase::opendir(const char *, struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::opendir(const char *, struct fuse_file_info *)
+
+int
+FuseAppBase::read(const char *, char *, size_t, off_t, struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::read(const char *, char *, size_t, off_t, struct fuse_file_info *)
+
+int
+FuseAppBase::readdir(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *, enum fuse_readdir_flags)
{
return -ENOSYS;
}
-int FuseAppBase::readdir(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *)
+
+int
+FuseAppBase::readlink(const char *, char *, size_t)
{
return -ENOSYS;
}
-int FuseAppBase::readlink(const char *, char *, size_t)
+
+int
+FuseAppBase::release(const char *, struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::release(const char *, struct fuse_file_info *)
+
+int
+FuseAppBase::releasedir(const char *, struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::releasedir(const char *, struct fuse_file_info *)
+
+int
+FuseAppBase::removexattr(const char *, const char *)
{
return -ENOSYS;
}
-int FuseAppBase::removexattr(const char *, const char *)
+
+int
+FuseAppBase::rename(const char *, const char *, unsigned int)
{
return -ENOSYS;
}
-int FuseAppBase::rename(const char *, const char *)
+
+int
+FuseAppBase::rmdir(const char *)
{
return -ENOSYS;
}
-int FuseAppBase::rmdir(const char *)
+
+int
+FuseAppBase::setxattr(const char *, const char *, const char *, size_t, int)
{
return -ENOSYS;
}
-int FuseAppBase::setxattr(const char *, const char *, const char *, size_t, int)
+
+int
+FuseAppBase::statfs(const char *, struct statvfs *)
{
return -ENOSYS;
}
-int FuseAppBase::statfs(const char *, struct statvfs *)
+
+int
+FuseAppBase::symlink(const char *, const char *)
{
return -ENOSYS;
}
-int FuseAppBase::symlink(const char *, const char *)
+
+int
+FuseAppBase::unlink(const char *)
{
return -ENOSYS;
}
-int FuseAppBase::truncate(const char *, off_t)
+
+int
+FuseAppBase::write(const char *, const char *, size_t, off_t, struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::unlink(const char *)
+
+int
+FuseAppBase::lock(const char *, struct fuse_file_info *, int, struct flock *)
{
return -ENOSYS;
}
-int FuseAppBase::write(const char *, const char *, size_t, off_t, struct fuse_file_info *)
+
+int
+// NOLINTNEXTLINE(*-c-arrays)
+FuseAppBase::utimens(const char *, const struct timespec[2], struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::lock(const char *, struct fuse_file_info *, int, struct flock *)
+
+int
+FuseAppBase::bmap(const char *, size_t, uint64_t *)
{
return -ENOSYS;
}
-int FuseAppBase::utimens(const char *, const struct timespec[2])
+
+int
+FuseAppBase::ioctl(const char *, unsigned int, void *, struct fuse_file_info *, unsigned int, void *)
{
return -ENOSYS;
}
-int FuseAppBase::bmap(const char *, size_t, uint64_t *)
+
+int
+FuseAppBase::poll(const char *, struct fuse_file_info *, struct fuse_pollhandle *, unsigned *)
{
return -ENOSYS;
}
-int FuseAppBase::ioctl(const char *, int, void *, struct fuse_file_info *, unsigned int, void *)
+
+int
+FuseAppBase::write_buf(const char *, struct fuse_bufvec *, off_t, struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::poll(const char *, struct fuse_file_info *, struct fuse_pollhandle *, unsigned *)
+
+int
+FuseAppBase::read_buf(const char *, struct fuse_bufvec **, size_t, off_t, struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::write_buf(const char *, struct fuse_bufvec *, off_t, struct fuse_file_info *)
+
+int
+FuseAppBase::flock(const char *, struct fuse_file_info *, int)
{
return -ENOSYS;
}
-int FuseAppBase::read_buf(const char *, struct fuse_bufvec **, size_t, off_t, struct fuse_file_info *)
+
+int
+FuseAppBase::fallocate(const char *, int, off_t, off_t, struct fuse_file_info *)
{
return -ENOSYS;
}
-int FuseAppBase::flock(const char *, struct fuse_file_info *, int)
+
+ssize_t
+FuseAppBase::copy_file_range(
+ const char *, struct fuse_file_info *, off_t, const char *, struct fuse_file_info *, off_t, size_t, int)
{
return -ENOSYS;
}
-int FuseAppBase::fallocate(const char *, int, off_t, off_t, struct fuse_file_info *)
+
+off_t
+FuseAppBase::lseek(const char *, off_t, int, struct fuse_file_info *)
{
return -ENOSYS;
}
+
// LCOV_EXCL_STOP
-void FuseAppBase::log(int level, const char * message) const throw()
+void
+FuseAppBase::log(int level, const char * message) const noexcept
{
logf(level, "%s", message);
}
-void FuseAppBase::logf(int level, const char * fmt, ...) const throw()
+
+void
+FuseAppBase::logf(int level, const char * fmt, ...) const noexcept
{
va_list args;
va_start(args, fmt);
@@ -200,37 +290,14 @@ void FuseAppBase::logf(int level, const char * fmt, ...) const throw()
va_end(args);
}
-int FuseAppBase::onError(const std::exception & e) throw()
+int
+FuseAppBase::onError(const std::exception & error) noexcept
{
- logf(LOG_ERR, "Unknown exception (what: %s)", e.what());
+ logf(LOG_ERR, "Unknown exception (what: %s)", error.what());
return -ENOSYS;
}
-void FuseAppBase::beforeOperation()
-{
-}
-
-void * FuseAppBase::fuseInit (struct fuse_conn_info *conn)
+void
+FuseAppBase::beforeOperation()
{
- return fuseApp->init(conn);
}
-void FuseAppBase::fuseDestroy(void *)
-{
- delete fuseApp;
-}
-
-struct fuse_args
-FuseAppBase::runint(int argc, char ** argv)
-{
- struct fuse_opt fuse_opts[] = {
- { NULL, 0, 0 }
- };
- fuseApp = this;
- struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
- if (fuse_opt_parse(&args, fuseApp, fuse_opts,
- &internalHelper<decltype(&FuseAppBase::opt_parse), &FuseAppBase::opt_parse>) == -1) {
- exit(1);
- }
- return args;
-}
-
diff --git a/netfs/fuse/fuseAppBase.h b/netfs/fuse/fuseAppBase.h
index f0866bb..8f3bffe 100644
--- a/netfs/fuse/fuseAppBase.h
+++ b/netfs/fuse/fuseAppBase.h
@@ -1,186 +1,195 @@
-#ifndef FUSEAPP_H
-#define FUSEAPP_H
+#pragma once
-#define FUSE_USE_VERSION 26
-#include <fuse.h>
-#include <typeinfo>
+#include <c++11Helpers.h>
+#include <cerrno>
+#include <cstdio>
#include <exception>
-#include <stdio.h>
-#include <errno.h>
+#include <fuse.h>
+#include <lockHelpers.h>
+#include <shared_mutex>
#include <syslog.h>
+#include <typeinfo>
#include <visibility.h>
-#include <buffer.h>
-#include <shared_mutex>
-#include <lockHelpers.h>
class DLL_PUBLIC FuseAppBase {
- public:
- FuseAppBase();
- virtual ~FuseAppBase() = 0;
- virtual void * init (struct fuse_conn_info * info);
- virtual int opt_parse(void *, const char * arg, int key, struct fuse_args *);
- virtual int access(const char *, int);
- virtual int chmod(const char *, mode_t);
- virtual int chown(const char *, uid_t, gid_t);
- virtual int create(const char *, mode_t, struct fuse_file_info *);
- virtual int fgetattr(const char *, struct stat *, struct fuse_file_info *);
- virtual int flush(const char *, struct fuse_file_info *);
- virtual int fsync(const char *, int, struct fuse_file_info *);
- virtual int fsyncdir(const char *, int, struct fuse_file_info *);
- virtual int ftruncate(const char *, off_t, struct fuse_file_info *);
- virtual int getattr(const char *, struct stat *);
- virtual int getxattr(const char *, const char *, char *, size_t);
- virtual int link(const char *, const char *);
- virtual int listxattr(const char *, char *, size_t);
- virtual int mkdir(const char *, mode_t);
- virtual int mknod(const char *, mode_t, dev_t);
- virtual int open(const char *, struct fuse_file_info *);
- virtual int opendir(const char *, struct fuse_file_info *);
- virtual int read(const char *, char *, size_t, off_t, struct fuse_file_info *);
- virtual int readdir(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *);
- virtual int readlink(const char *, char *, size_t);
- virtual int release(const char *, struct fuse_file_info *);
- virtual int releasedir(const char *, struct fuse_file_info *);
- virtual int removexattr(const char *, const char *);
- virtual int rename(const char *, const char *);
- virtual int rmdir(const char *);
- virtual int setxattr(const char *, const char *, const char *, size_t, int);
- virtual int statfs(const char *, struct statvfs *);
- virtual int symlink(const char *, const char *);
- virtual int truncate(const char *, off_t);
- virtual int unlink(const char *);
- virtual int write(const char *, const char *, size_t, off_t, struct fuse_file_info *);
- virtual int lock(const char *, struct fuse_file_info *, int cmd, struct flock *);
- virtual int utimens(const char *, const struct timespec tv[2]);
- virtual int bmap(const char *, size_t blocksize, uint64_t *idx);
- virtual int ioctl(const char *, int cmd, void *arg, struct fuse_file_info *, unsigned int flags, void * data);
- virtual int poll(const char *, struct fuse_file_info *, struct fuse_pollhandle *, unsigned *);
- virtual int write_buf(const char *, struct fuse_bufvec *buf, off_t off, struct fuse_file_info *);
- virtual int read_buf(const char *, struct fuse_bufvec **bufp, size_t size, off_t off, struct fuse_file_info *);
- virtual int flock(const char *, struct fuse_file_info *, int op);
- virtual int fallocate(const char *, int, off_t, off_t, struct fuse_file_info *);
- virtual int onError(const std::exception & err) throw();
- virtual void beforeOperation();
- void log(int level, const char * message) const throw();
- void logf(int level, const char * fmt, ...) const throw() __attribute__ ((__format__ (__printf__, 3, 4)));
- virtual void vlogf(int level, const char * fmt, va_list) const throw() __attribute__ ((__format__ (__printf__, 3, 0))) = 0;
+public:
+ FuseAppBase();
+ SPECIAL_MEMBERS_DELETE(FuseAppBase);
+ virtual ~FuseAppBase();
+
+ virtual void * init(struct fuse_conn_info * info, struct fuse_config * cfg);
+ virtual int access(const char *, int);
+ virtual int chmod(const char *, mode_t, struct fuse_file_info *);
+ virtual int chown(const char *, uid_t, gid_t, struct fuse_file_info *);
+ virtual int create(const char *, mode_t, struct fuse_file_info *);
+ virtual int getattr(const char *, struct stat *, struct fuse_file_info *);
+ virtual int flush(const char *, struct fuse_file_info *);
+ virtual int fsync(const char *, int, struct fuse_file_info *);
+ virtual int fsyncdir(const char *, int, struct fuse_file_info *);
+ virtual int truncate(const char *, off_t, struct fuse_file_info *);
+ virtual int getxattr(const char *, const char *, char *, size_t);
+ virtual int link(const char *, const char *);
+ virtual int listxattr(const char *, char *, size_t);
+ virtual int mkdir(const char *, mode_t);
+ virtual int mknod(const char *, mode_t, dev_t);
+ virtual int open(const char *, struct fuse_file_info *);
+ virtual int opendir(const char *, struct fuse_file_info *);
+ virtual int read(const char *, char *, size_t, off_t, struct fuse_file_info *);
+ virtual int readdir(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *, enum fuse_readdir_flags);
+ virtual int readlink(const char *, char *, size_t);
+ virtual int release(const char *, struct fuse_file_info *);
+ virtual int releasedir(const char *, struct fuse_file_info *);
+ virtual int removexattr(const char *, const char *);
+ virtual int rename(const char *, const char *, unsigned int);
+ virtual int rmdir(const char *);
+ virtual int setxattr(const char *, const char *, const char *, size_t, int);
+ virtual int statfs(const char *, struct statvfs *);
+ virtual int symlink(const char *, const char *);
+ virtual int unlink(const char *);
+ virtual int write(const char *, const char *, size_t, off_t, struct fuse_file_info *);
+ virtual int lock(const char *, struct fuse_file_info *, int cmd, struct flock *);
+ // NOLINTNEXTLINE(*-c-arrays)
+ virtual int utimens(const char *, const struct timespec times[2], struct fuse_file_info *);
+ virtual int bmap(const char *, size_t blocksize, uint64_t * idx);
+ virtual int ioctl(
+ const char *, unsigned int cmd, void * arg, struct fuse_file_info *, unsigned int flags, void * data);
+ virtual int poll(const char *, struct fuse_file_info *, struct fuse_pollhandle *, unsigned *);
+ // NOLINTNEXTLINE(readability-identifier-naming)
+ virtual int write_buf(const char *, struct fuse_bufvec * buf, off_t off, struct fuse_file_info *);
+ // NOLINTNEXTLINE(readability-identifier-naming)
+ virtual int read_buf(const char *, struct fuse_bufvec ** bufp, size_t size, off_t off, struct fuse_file_info *);
+ virtual int flock(const char *, struct fuse_file_info *, int operation);
+ virtual int fallocate(const char *, int, off_t, off_t, struct fuse_file_info *);
+ // NOLINTNEXTLINE(readability-identifier-naming)
+ virtual ssize_t copy_file_range(
+ const char *, struct fuse_file_info *, off_t, const char *, struct fuse_file_info *, off_t, size_t, int);
+ virtual off_t lseek(const char *, off_t off, int whence, struct fuse_file_info *);
+ virtual int onError(const std::exception & err) noexcept;
+ virtual void beforeOperation();
+ void log(int level, const char * message) const noexcept;
+ void logf(int level, const char * fmt, ...) const noexcept __attribute__((__format__(__printf__, 3, 4)));
+ virtual void vlogf(int level, const char * fmt, va_list) const noexcept
+ __attribute__((__format__(__printf__, 3, 0)))
+ = 0;
- virtual int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc) = 0;
- virtual int main(int, char **, const struct fuse_operations *) = 0;
+ mutable std::shared_mutex mutex;
-#define GetHelper(func) getHelper<decltype(&FuseAppBase::func), decltype(&FuseApp::func), &FuseAppBase::func>(&FuseAppBase::func)
- template <typename FuseApp>
- static int run(int argc, char ** argv, FuseApp * fa)
- {
- auto args = fa->runint(argc, argv);
- struct fuse_operations operations = {
- GetHelper(getattr),
- GetHelper(readlink),
- NULL, // getdir deprecated
- GetHelper(mknod),
- GetHelper(mkdir),
- GetHelper(unlink),
- GetHelper(rmdir),
- GetHelper(symlink),
- GetHelper(rename),
- GetHelper(link),
- GetHelper(chmod),
- GetHelper(chown),
- GetHelper(truncate),
- NULL, // utime deprecated
- GetHelper(open),
- GetHelper(read),
- GetHelper(write),
- GetHelper(statfs),
- GetHelper(flush),
- GetHelper(release),
- GetHelper(fsync),
- GetHelper(setxattr),
- GetHelper(getxattr),
- GetHelper(listxattr),
- GetHelper(removexattr),
- GetHelper(opendir),
- GetHelper(readdir),
- GetHelper(releasedir),
- GetHelper(fsyncdir),
- fuseInit,
- fuseDestroy,
- GetHelper(access),
- GetHelper(create),
- GetHelper(ftruncate),
- GetHelper(fgetattr),
-#if (FUSE_MINOR_VERSION >= 6)
- GetHelper(lock),
- GetHelper(utimens),
- GetHelper(bmap),
-#if (FUSE_MINOR_VERSION >= 8)
- 0, // flag_nullpath_ok
-#if (FUSE_MINOR_VERSION >= 9)
- 0, // flag_nopath
- 0, // flag_utime_omit_ok
-#endif
- 0, // flag_reserved
- GetHelper(ioctl),
- GetHelper(poll),
-#if (FUSE_MINOR_VERSION >= 9)
- GetHelper(write_buf),
- GetHelper(read_buf),
- GetHelper(flock),
- GetHelper(fallocate),
-#endif
-#endif
-#endif
+protected:
+ static FuseAppBase * fuseApp;
+
+ template<bool Implemented, auto BaseFunc>
+ constexpr static auto
+ getInternalHelper()
+ {
+ if constexpr (Implemented) {
+ return [](auto... arg) {
+ SharedLock(fuseApp->mutex);
+ return (fuseApp->*BaseFunc)(arg...);
};
- return fa->main(args.argc, args.argv, &operations);
}
- struct fuse_args runint(int, char **);
-
- private:
- static void * fuseInit(struct fuse_conn_info *conn);
- static void fuseDestroy(void *);
+ else {
+ return nullptr;
+ }
+ }
- template <typename BFunc, typename IFunc, BFunc bfunc, typename ... Args>
- static int(*getHelper(int(FuseAppBase::*)(Args...)))(Args...)
- {
- if constexpr (!std::is_same<BFunc, IFunc>::value) {
- return [](Args ... a) {
- for (int t = 0; ; ++t) {
- try {
- fuseApp->beforeOperation();
- SharedLock(fuseApp->_lock);
- return (fuseApp->*bfunc)(a...);
- }
- catch (const std::exception & ex) {
- if (t < 10) {
- if (int rtn = fuseApp->onError(ex)) {
- return rtn;
- }
- }
- else {
- fuseApp->logf(LOG_ERR, "Retries expired with %s calling %s", ex.what(), typeid(bfunc).name());
- return -EIO;
+ template<bool Implemented, auto BaseFunc>
+ constexpr static auto
+ getHelper()
+ {
+ constexpr auto ATTEMPTS = 10;
+ if constexpr (Implemented) {
+ return [](auto... arg) {
+ using Return = decltype((fuseApp->*BaseFunc)(arg...));
+ for (int attempt = 0;; ++attempt) {
+ try {
+ fuseApp->beforeOperation();
+ SharedLock(fuseApp->mutex);
+ return (fuseApp->*BaseFunc)(arg...);
+ }
+ catch (const std::exception & ex) {
+ if (attempt < ATTEMPTS) {
+ if (Return rtn = fuseApp->onError(ex)) {
+ return rtn;
}
}
- catch (...) {
- fuseApp->logf(LOG_ERR, "Unknown exception calling %s", typeid(bfunc).name());
- return -EIO;
+ else {
+ fuseApp->logf(
+ LOG_ERR, "Retries expired with %s calling %s", ex.what(), typeid(BaseFunc).name());
+ return static_cast<Return>(-EIO);
}
}
- };
- }
- return nullptr;
+ catch (...) {
+ fuseApp->logf(LOG_ERR, "Unknown exception calling %s", typeid(BaseFunc).name());
+ return static_cast<Return>(-EIO);
+ }
+ }
+ };
}
- template <typename Func, Func f, typename ... Args>
- static int internalHelper(Args ... a)
- {
- return (fuseApp->*f)(a...);
+ else {
+ return nullptr;
}
-
- static FuseAppBase * fuseApp;
-
- protected:
- mutable std::shared_mutex _lock;
+ }
};
-#endif
+template<typename FuseApp> class FuseAppBaseT : public FuseAppBase {
+private:
+public:
+#define GetIntHelper(func) \
+ getInternalHelper<!std::is_same_v<decltype(&FuseAppBase::func), decltype(&FuseApp::func)>, &FuseAppBase::func>()
+#define GetHelper(func) \
+ getHelper<!std::is_same_v<decltype(&FuseAppBase::func), decltype(&FuseApp::func)>, &FuseAppBase::func>()
+ constexpr const static fuse_operations OPERATIONS {
+ GetHelper(getattr),
+ GetHelper(readlink),
+ GetHelper(mknod),
+ GetHelper(mkdir),
+ GetHelper(unlink),
+ GetHelper(rmdir),
+ GetHelper(symlink),
+ GetHelper(rename),
+ GetHelper(link),
+ GetHelper(chmod),
+ GetHelper(chown),
+ GetHelper(truncate),
+ GetHelper(open),
+ GetHelper(read),
+ GetHelper(write),
+ GetHelper(statfs),
+ GetHelper(flush),
+ GetHelper(release),
+ GetHelper(fsync),
+ GetHelper(setxattr),
+ GetHelper(getxattr),
+ GetHelper(listxattr),
+ GetHelper(removexattr),
+ GetHelper(opendir),
+ GetHelper(readdir),
+ GetHelper(releasedir),
+ GetHelper(fsyncdir),
+ GetIntHelper(init),
+ nullptr, // fuseDestroy
+ GetHelper(access),
+ GetHelper(create),
+ GetHelper(lock),
+ GetHelper(utimens),
+ GetHelper(bmap),
+ GetHelper(ioctl),
+ GetHelper(poll),
+ GetHelper(write_buf),
+ GetHelper(read_buf),
+ GetHelper(flock),
+ GetHelper(fallocate),
+ GetHelper(copy_file_range),
+ GetHelper(lseek),
+ };
+#undef GetHelper
+#undef GetIntHelper
+protected:
+ template<typename Func, Func AppFunc, typename... Args>
+ static int
+ internalHelper(Args... arg)
+ {
+ return (fuseApp->*AppFunc)(arg...);
+ }
+};
diff --git a/netfs/fuse/fuseConfig.ice b/netfs/fuse/fuseConfig.ice
index e425c21..ea489ac 100644
--- a/netfs/fuse/fuseConfig.ice
+++ b/netfs/fuse/fuseConfig.ice
@@ -1,4 +1,5 @@
#include <exceptions.ice>
+#include <mapper.ice>
module NetFS {
module Client {
@@ -21,8 +22,8 @@ module NetFS {
["slicer:name:async"]
bool Async = false;
- ["slicer:name:listdir"]
- bool ListDir = true;
+ ["slicer:name:mapper"]
+ Mapping::Mapper Mapper;
};
["slicer:key:name","slicer:value:resource","slicer:item:resource"]
diff --git a/netfs/fuse/fuseConfigImpl.cpp b/netfs/fuse/fuseConfigImpl.cpp
index 9129bfc..351a794 100644
--- a/netfs/fuse/fuseConfigImpl.cpp
+++ b/netfs/fuse/fuseConfigImpl.cpp
@@ -1,10 +1,12 @@
-#include <fuseConfig.h>
#include <compileTimeFormatter.h>
+#include <fuseConfig.h>
AdHocFormatter(ResourceNotFoundMsg, "No such resource: %?");
-void
-NetFS::Client::ResourceNotFound::ice_print(std::ostream & s) const
-{
- ResourceNotFoundMsg::write(s, resourceName);
-}
+namespace NetFS {
+ void
+ Client::ResourceNotFound::ice_print(std::ostream & stream) const
+ {
+ ResourceNotFoundMsg::write(stream, resourceName);
+ }
+}
diff --git a/netfs/fuse/fuseDirs.cpp b/netfs/fuse/fuseDirs.cpp
index 3d77101..1b98048 100644
--- a/netfs/fuse/fuseDirs.cpp
+++ b/netfs/fuse/fuseDirs.cpp
@@ -1,98 +1,101 @@
-#include "fuseApp.impl.h"
-#include <lockHelpers.h>
+#include "fuseDirs.h"
#include <entCache.h>
+#include <lockHelpers.h>
+#include <numeric.h>
namespace NetFS {
-FuseApp::OpenDir::OpenDir(DirectoryPrxPtr r, const std::string & p) :
- remote(r),
- remoteV2(r->ice_getFacet() >= "v02" ? Ice::uncheckedCast<DirectoryV2Prx>(r) : nullptr),
- path(p)
-{
-}
-
-template<>
-std::map<int, FuseApp::OpenDirPtr> &
-FuseApp::getMap<FuseApp::OpenDirPtr>()
-{
- return openDirs;
-}
-
-int
-FuseApp::opendir(const char * p, struct fuse_file_info * fi)
-{
- try {
- auto remote = volume->opendir(reqEnv(), p);
- setProxy<OpenDirPtr>(fi->fh, remote, p);
- return 0;
+ FuseApp::OpenDir::OpenDir(DirectoryPrxPtr remotePrx, std::string remotePath) :
+ remote(std::move(remotePrx)), path(std::move(remotePath))
+ {
}
- catch (SystemError & e) {
- return -e.syserrno;
+
+ template<>
+ std::map<FuseApp::FuseHandleTypeId, FuseApp::OpenDirPtr> &
+ FuseApp::getMap<FuseApp::OpenDirPtr>()
+ {
+ return openDirs;
}
-}
-int
-FuseApp::releasedir(const char *, struct fuse_file_info * fi)
-{
- try {
- auto remote = getProxy<OpenDirPtr>(fi->fh)->remote;
- remote->close();
- clearProxy<OpenDirPtr>(fi->fh);
- return 0;
+ int
+ FuseApp::opendir(const char * path, struct fuse_file_info * fileInfo)
+ {
+ try {
+ auto remote = volume->opendir(reqEnv(), path);
+ setProxy<OpenDirPtr>(fileInfo->fh, remote, path);
+ return 0;
+ }
+ catch (SystemError & e) {
+ return -e.syserrno;
+ }
}
- catch (SystemError & e) {
- clearProxy<OpenDirPtr>(fi->fh);
- return -e.syserrno;
+
+ int
+ FuseApp::releasedir(const char *, struct fuse_file_info * fileInfo)
+ {
+ try {
+ auto remote = getProxy<OpenDirPtr>(fileInfo->fh)->remote;
+ remote->close();
+ clearProxy<OpenDirPtr>(fileInfo->fh);
+ return 0;
+ }
+ catch (SystemError & e) {
+ clearProxy<OpenDirPtr>(fileInfo->fh);
+ return -e.syserrno;
+ }
}
-}
-int
-FuseApp::readdir(const char * p, void * buf, fuse_fill_dir_t filler, off_t, struct fuse_file_info * fi)
-{
- try {
- auto od = getProxy<OpenDirPtr>(fi->fh);
- std::string path(p);
- path += "/";
- auto expiry = time(NULL) + 2;
- if (fcr->ListDir && od->remoteV2) {
- for (const auto & e : od->remoteV2->listdir()) {
- filler(buf, e.first.c_str(), NULL, 0);
- statCache.add(path + e.first, converter.convert(e.second), expiry);
+ int
+ FuseApp::readdir(const char * path, void * buf, fuse_fill_dir_t filler, off_t, struct fuse_file_info * fileInfo,
+ enum fuse_readdir_flags flags)
+ {
+ try {
+ auto openDir = getProxy<OpenDirPtr>(fileInfo->fh);
+ const std::filesystem::path fspath {path};
+ auto expiry = time(nullptr) + 2;
+ if (flags & FUSE_READDIR_PLUS) {
+ for (const auto & entry : openDir->remote->listdir()) {
+ if (auto stat = converter.convert(entry.second); stat.st_mode) {
+ filler(buf, entry.first.c_str(), nullptr, 0, FUSE_FILL_DIR_PLUS);
+ const auto key {std::filesystem::hash_value(fspath / entry.first)};
+ statCache.remove(key);
+ statCache.add(key, stat, expiry);
+ }
+ }
}
- }
- else {
- for (const auto & e : od->remote->readdir()) {
- filler(buf, e.c_str(), NULL, 0);
+ else {
+ // Standard read dir cannot know the local system cannot represent the inode
+ for (const auto & entry : openDir->remote->readdir()) {
+ filler(buf, entry.c_str(), nullptr, 0, fuse_fill_dir_flags {});
+ }
}
+ return 0;
+ }
+ catch (SystemError & e) {
+ return -e.syserrno;
}
- return 0;
- }
- catch (SystemError & e) {
- return -e.syserrno;
}
-}
-int
-FuseApp::mkdir(const char * p, mode_t m)
-{
- try {
- volume->mkdir(reqEnv(), p, m);
- return 0;
- }
- catch (SystemError & e) {
- return -e.syserrno;
+ int
+ FuseApp::mkdir(const char * path, mode_t mode)
+ {
+ try {
+ volume->mkdir(reqEnv(), path, safe {mode});
+ return 0;
+ }
+ catch (SystemError & e) {
+ return -e.syserrno;
+ }
}
-}
-int
-FuseApp::rmdir(const char * p)
-{
- try {
- volume->rmdir(reqEnv(), p);
- return 0;
- }
- catch (SystemError & e) {
- return -e.syserrno;
+ int
+ FuseApp::rmdir(const char * path)
+ {
+ try {
+ volume->rmdir(reqEnv(), path);
+ return 0;
+ }
+ catch (SystemError & e) {
+ return -e.syserrno;
+ }
}
}
-}
-
diff --git a/netfs/fuse/fuseDirs.h b/netfs/fuse/fuseDirs.h
new file mode 100644
index 0000000..4b04027
--- /dev/null
+++ b/netfs/fuse/fuseDirs.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "fuseApp.impl.h" // IWYU pragma: keep
+
+namespace NetFS {
+ class FuseApp::OpenDir {
+ public:
+ OpenDir(DirectoryPrxPtr remote, std::string path);
+
+ DirectoryPrxPtr remote;
+ const std::string path;
+ };
+}
diff --git a/netfs/fuse/fuseFiles.cpp b/netfs/fuse/fuseFiles.cpp
index b8eb014..fd313e5 100644
--- a/netfs/fuse/fuseFiles.cpp
+++ b/netfs/fuse/fuseFiles.cpp
@@ -1,261 +1,354 @@
-#include <string.h>
-#include "fuseApp.impl.h"
#include "fuseFiles.h"
+#include "fuseApp.impl.h" // IWYU pragma: keep - getProxy definition
#include "lockHelpers.h"
+#include <Ice/BuiltinSequences.h>
+#include <algorithm>
+#include <cstring>
#include <entCache.h>
+#include <future>
+#include <memory>
#include <mutex>
+#include <numeric.h>
+#include <numeric>
+#include <ranges>
+#include <span>
+#include <utility>
+#include <vector>
namespace NetFS {
-FuseApp::OpenFile::WriteState::WriteState() :
- future(promise.get_future().share())
-{
-}
-
-FuseApp::OpenFile::OpenFile(FilePrxPtr r, const std::string & p, int f) :
- remote(r),
- path(p),
- flags(f)
-{
-}
+ constexpr size_t MESSAGE_SIZE_HEADROOM = 1024;
-template<>
-std::map<int, FuseApp::OpenFilePtr> &
-FuseApp::getMap<FuseApp::OpenFilePtr>()
-{
- return openFiles;
-}
+ FuseApp::OpenFile::WriteState::WriteState() : future(promise.get_future().share()) { }
-void
-FuseApp::OpenFile::wait() const
-{
- auto cbg = [this](){
- SharedLock(_lock);
- return bg;
- }();
- for (const auto & w : cbg) {
- w.second->future.wait();
+ FuseApp::OpenFile::OpenFile(FilePrxPtr remotePrx, std::string remotePath, int openFlags, size_t messageMaxSize) :
+ remote(std::move(remotePrx)), path(std::move(remotePath)), flags(openFlags),
+ bodyMaxSize(messageMaxSize - MESSAGE_SIZE_HEADROOM)
+ {
}
-}
-void
-FuseApp::OpenFile::flush()
-{
- auto first = [this]() {
- SharedLock(_lock);
- return bg.empty() ? nullptr : bg.begin()->second;
- };
- while (auto w = first()) {
- // background operations are void, so no need to actually get the return value
- w->future.wait();
+ template<>
+ std::map<FuseApp::FuseHandleTypeId, FuseApp::OpenFilePtr> &
+ FuseApp::getMap<FuseApp::OpenFilePtr>()
+ {
+ return openFiles;
}
-}
-FuseApp::OpenFile::BGs::interval_type
-FuseApp::OpenFile::range(off_t o, size_t s)
-{
- return OpenFile::BGs::interval_type::right_open(o, o + s);
-}
-
-int
-FuseApp::open(const char * p, struct fuse_file_info * fi)
-{
- try {
- auto remote = volume->open(reqEnv(), p, fi->flags);
- setProxy<OpenFilePtr>(fi->fh, remote, p, fi->flags);
- return 0;
- }
- catch (SystemError & e) {
- return -e.syserrno;
+ void
+ FuseApp::OpenFile::wait() const
+ {
+ auto cbg = [this]() {
+ SharedLock(mutex);
+ return bg;
+ }();
+ for (const auto & operation : cbg) {
+ operation.second->future.get();
+ }
}
-}
-int
-FuseApp::create(const char * p, mode_t m, struct fuse_file_info * fi)
-{
- try {
- auto remote = volume->create(reqEnv(), p, fi->flags, m);
- setProxy<OpenFilePtr>(fi->fh, remote, p, fi->flags);
- return 0;
+ void
+ FuseApp::OpenFile::flush()
+ {
+ auto getFirstBackgroundOp = [this]() {
+ SharedLock(mutex);
+ return bg.empty() ? nullptr : bg.begin()->second;
+ };
+ while (auto operation = getFirstBackgroundOp()) {
+ operation->future.get();
+ }
}
- catch (SystemError & e) {
- return -e.syserrno;
+
+ FuseApp::OpenFile::BGs::interval_type
+ FuseApp::OpenFile::range(off_t offset, size_t size)
+ {
+ return OpenFile::BGs::interval_type::right_open(safe {offset}, safe {offset} + size);
}
-}
-int
-FuseApp::release(const char *, struct fuse_file_info * fi)
-{
- try {
- auto of = getProxy<OpenFilePtr>(fi->fh);
- auto remote = of->remote;
+ int
+ FuseApp::open(const char * path, struct fuse_file_info * fileInfo)
+ {
try {
- of->flush();
+ auto remote = volume->open(reqEnv(), path, fileInfo->flags);
+ setProxy<OpenFilePtr>(fileInfo->fh, remote, path, fileInfo->flags, combinedSettings.messageSizeMax);
+ return 0;
}
catch (SystemError & e) {
+ return -e.syserrno;
}
- remote->close();
- clearProxy<OpenFilePtr>(fi->fh);
- return 0;
- }
- catch (SystemError & e) {
- clearProxy<OpenFilePtr>(fi->fh);
- return -e.syserrno;
}
-}
-int
-FuseApp::flush(const char *, struct fuse_file_info * fi)
-{
- try {
- getProxy<OpenFilePtr>(fi->fh)->flush();
- return 0;
+ int
+ FuseApp::create(const char * path, mode_t mode, struct fuse_file_info * fileInfo)
+ {
+ try {
+ auto remote = volume->create(reqEnv(), path, fileInfo->flags, safe {mode});
+ setProxy<OpenFilePtr>(fileInfo->fh, remote, path, fileInfo->flags, combinedSettings.messageSizeMax);
+ return 0;
+ }
+ catch (SystemError & e) {
+ return -e.syserrno;
+ }
}
- catch (SystemError & e) {
- return -e.syserrno;
+
+ int
+ FuseApp::release(const char *, struct fuse_file_info * fileInfo)
+ {
+ try {
+ auto openFile = getProxy<OpenFilePtr>(fileInfo->fh);
+ auto remote = openFile->remote;
+ try {
+ openFile->flush();
+ clearProxy<OpenFilePtr>(fileInfo->fh);
+ remote->close();
+ }
+ catch (SystemError & e) {
+ clearProxy<OpenFilePtr>(fileInfo->fh);
+ remote->close();
+ throw;
+ }
+ return 0;
+ }
+ catch (SystemError & e) {
+ clearProxy<OpenFilePtr>(fileInfo->fh);
+ return -e.syserrno;
+ }
}
-}
-template<typename Rtn, typename F>
-inline
-Rtn
-FuseApp::waitOnWriteRangeAndThen(size_t s, off_t o, const OpenFilePtr & of, const F & f)
-{
- const auto key = OpenFile::range(o, s);
- while (true) {
- std::unique_lock<decltype(of->_lock)> _l(of->_lock);
- // Acquire operations to wait for
- auto R = of->bg.equal_range(key);
- if (R.first == R.second) {
- // Perform operation
- return f(key);
+ int
+ FuseApp::flush(const char *, struct fuse_file_info * fileInfo)
+ {
+ try {
+ getProxy<OpenFilePtr>(fileInfo->fh)->flush();
+ return 0;
}
- else {
+ catch (SystemError & e) {
+ return -e.syserrno;
+ }
+ }
+
+ template<typename Callback>
+ inline auto
+ FuseApp::waitOnWriteRangeAndThen(size_t size, off_t offset, const OpenFilePtr & openFile, const Callback & callback)
+ {
+ const auto key = OpenFile::range(offset, size);
+ while (true) {
+ std::unique_lock lock(openFile->mutex);
+ // Acquire operations to wait for
+ const auto pendingRange = openFile->bg.equal_range(key);
+ if (pendingRange.first == pendingRange.second) {
+ // Perform operation
+ return callback(key);
+ }
+ const auto overlap = [pendingRange]() {
+ std::vector<std::shared_future<void>> out;
+ out.reserve(safe<ptrdiff_t> {std::distance(pendingRange.first, pendingRange.second)});
+ std::transform(pendingRange.first, pendingRange.second, std::back_inserter(out), [](auto && operation) {
+ return operation.second->future;
+ });
+ return out;
+ }();
// Wait for them whilst unlocked
- _l.release()->unlock();
- std::vector<std::shared_ptr<OpenFile::WriteState>> overlap;
- overlap.reserve(std::distance(R.first, R.second));
- for (auto i = R.first; i != R.second; i++) {
- overlap.push_back(i->second);
+ lock.release()->unlock();
+ try {
+ std::for_each(overlap.begin(), overlap.end(), [](auto && operationFuture) {
+ operationFuture.get();
+ });
+ }
+ catch (const SystemError &) {
+ throw;
}
- for (const auto & r : overlap) {
- r->future.wait();
+ catch (...) {
+ throw SystemError {ECOMM};
}
- // Cause this thread to yield so the callback can acquire _lock
+ // Cause this thread to yield so the callback can lock mutex
usleep(0);
}
}
-}
-int
-FuseApp::read(const char *, char * buf, size_t s, off_t o, struct fuse_file_info * fi)
-{
- try {
- auto cpy = [buf](const auto && data) {
- memcpy(buf, &data.front(), data.size());
- return data.size();
- };
- auto of = getProxy<OpenFilePtr>(fi->fh);
- auto remote = of->remote;
- if (fcr->Async) {
- auto p = waitOnWriteRangeAndThen<std::future<Buffer>>(s, o, of, [o, s, &remote](const auto &){
- return remote->readAsync(o, s);
- });
- return cpy(p.get());
+ int
+ FuseApp::read(const char *, char * buf, size_t size, off_t offset, struct fuse_file_info * fileInfo)
+ {
+ try {
+ const std::span out(buf, size);
+ using BackgroundOps = std::vector<std::promise<int>>;
+ const auto cpy = [out](off_t blockOffset, const auto && data) -> int {
+ std::ranges::copy(data, out.begin() + blockOffset);
+ return safe {data.size()};
+ };
+ const auto collateTotal = [](auto && ops) {
+ return std::accumulate(ops.begin(), ops.end(), 0, [](auto && total, auto & operation) {
+ return total += operation.get_future().get();
+ });
+ };
+ auto openFile = getProxy<OpenFilePtr>(fileInfo->fh);
+ auto remote = openFile->remote;
+ if (fcr->Async) {
+ const auto blocks = out | std::views::chunk(openFile->bodyMaxSize);
+ BackgroundOps ops(blocks.size());
+ std::ranges::for_each(
+ blocks, [offset, &openFile, &remote, &cpy, opIter = ops.begin(), out](auto && block) mutable {
+ waitOnWriteRangeAndThen(block.size(), offset, openFile,
+ [thisOp = opIter++, offset, &remote, &cpy, block, out](const auto &) {
+ const auto outPosition = block.begin() - out.begin();
+ const auto position = offset + outPosition;
+ return remote->readAsync(
+ position, safe(block.size()),
+ [cpy, thisOp, outPosition](auto && resultBuf) {
+ thisOp->set_value(
+ cpy(outPosition, std::forward<Ice::ByteSeq>(resultBuf)));
+ },
+ [thisOp](auto error) {
+ thisOp->set_exception(std::move(error));
+ });
+ });
+ });
+ return collateTotal(ops);
+ }
+ if (openFile->bodyMaxSize < size) {
+ const auto blocks = out | std::views::chunk(openFile->bodyMaxSize);
+ BackgroundOps ops(blocks.size());
+ std::ranges::for_each(
+ blocks, [offset, &remote, out, &cpy, opIter = ops.begin()](auto && block) mutable {
+ auto thisOp = opIter++;
+ const auto outPosition = block.begin() - out.begin();
+ const auto position = offset + outPosition;
+ remote->readAsync(
+ position, safe(block.size()),
+ [cpy, thisOp, outPosition](auto && resultBuf) {
+ thisOp->set_value(cpy(outPosition, std::forward<Ice::ByteSeq>(resultBuf)));
+ },
+ [thisOp](auto error) {
+ thisOp->set_exception(std::move(error));
+ });
+ });
+ return collateTotal(ops);
+ }
+ return cpy(0, remote->read(offset, safe {size}));
}
- else {
- return cpy(remote->read(o, s));
+ catch (SystemError & e) {
+ return -e.syserrno;
}
}
- catch (SystemError & e) {
- return -e.syserrno;
- }
-}
-int
-FuseApp::write(const char *, const char * buf, size_t s, off_t o, struct fuse_file_info * fi)
-{
- try {
- auto of = getProxy<OpenFilePtr>(fi->fh);
- auto remote = of->remote;
- if (fcr->Async) {
- waitOnWriteRangeAndThen<void>(s, o, of, [o, s, buf, &of, &remote](const auto & key){
- auto p = std::make_shared<OpenFile::WriteState>();
- remote->writeAsync(o, s, Buffer(buf, buf + s), [p,of,key]() {
- p->promise.set_value();
- ScopeLock(of->_lock) {
- of->bg.erase(key);
- }
- }, [p,of,key](auto e) {
- p->promise.set_exception(e);
- ScopeLock(of->_lock) {
- of->bg.erase(key);
- }
+ int
+ FuseApp::write(const char *, const char * buf, size_t size, off_t offset, struct fuse_file_info * fileInfo)
+ {
+ static auto toBuffer = [](auto block) {
+ return std::make_pair(std::to_address(block.begin()), std::to_address(block.end()));
+ };
+ try {
+ auto openFile = getProxy<OpenFilePtr>(fileInfo->fh);
+ auto remote = openFile->remote;
+ const std::span bytes {reinterpret_cast<const ::Ice::Byte *>(buf), size};
+ if (fcr->Async) {
+ const auto blocks = bytes | std::views::chunk(openFile->bodyMaxSize);
+ std::ranges::for_each(blocks, [&remote, bytes, offset, &openFile](auto && block) {
+ const auto position = offset + (block.begin() - bytes.begin());
+ waitOnWriteRangeAndThen(
+ block.size(), position, openFile, [position, block, &openFile, remote](const auto & key) {
+ auto pendingWrite = std::make_shared<OpenFile::WriteState>();
+ openFile->bg.insert({key, pendingWrite});
+ remote->writeAsync(
+ position, safe(block.size()), toBuffer(block),
+ [pendingWrite, openFile, key]() {
+ pendingWrite->promise.set_value();
+ ScopeLock(openFile->mutex) {
+ openFile->bg.erase(key);
+ }
+ },
+ [pendingWrite, openFile](auto error) {
+ pendingWrite->promise.set_exception(std::move(error));
+ });
+ });
});
- of->bg.insert({key, p});
- });
+ }
+ else if (openFile->bodyMaxSize < size) {
+ const auto blocks = bytes | std::views::chunk(openFile->bodyMaxSize);
+ std::vector<std::future<void>> ops;
+ ops.reserve(blocks.size());
+ std::ranges::transform(blocks, std::back_inserter(ops), [&remote, bytes, offset](auto && block) {
+ const auto position = offset + (block.begin() - bytes.begin());
+ return remote->writeAsync(position, safe(block.size()), toBuffer(block));
+ });
+ std::ranges::for_each(ops, &std::future<void>::get);
+ }
+ else {
+ remote->write(offset, safe {size}, toBuffer(bytes));
+ }
+ return safe {size};
}
- else {
- remote->write(o, s, Buffer(buf, buf + s));
+ catch (SystemError & e) {
+ return -e.syserrno;
}
- return s;
}
- catch (SystemError & e) {
- return -e.syserrno;
- }
-}
-int
-FuseApp::truncate(const char * p, off_t o)
-{
- try {
- volume->truncate(reqEnv(), p, o);
- return 0;
- }
- catch (SystemError & e) {
- return -e.syserrno;
+ ssize_t
+ FuseApp::copy_file_range(const char *, struct fuse_file_info * fileInfoIn, off_t offsetIn, const char *,
+ struct fuse_file_info * fileInfoOut, off_t offsetOut, size_t size, int flags)
+ {
+ try {
+ auto openFileIn = getProxy<OpenFilePtr>(fileInfoIn->fh);
+ auto openFileOut = getProxy<OpenFilePtr>(fileInfoOut->fh);
+ openFileIn->wait();
+ openFileOut->wait();
+ return openFileIn->remote->copyrange(openFileOut->remote, offsetIn, offsetOut, safe {size}, flags);
+ }
+ catch (SystemError & e) {
+ return -e.syserrno;
+ }
}
-}
-int
-FuseApp::ftruncate(const char *, off_t o, fuse_file_info * fi)
-{
- try {
- auto of = getProxy<OpenFilePtr>(fi->fh);
- of->wait();
- auto remote = of->remote;
- remote->ftruncate(reqEnv(), o);
- return 0;
- }
- catch (SystemError & e) {
- return -e.syserrno;
+ int
+ FuseApp::truncate(const char * path, off_t offset, fuse_file_info * fileInfo)
+ {
+ try {
+ if (fileInfo) {
+ auto openFile = getProxy<OpenFilePtr>(fileInfo->fh);
+ openFile->wait();
+ openFile->remote->ftruncate(offset);
+ }
+ else {
+ volume->truncate(reqEnv(), path, offset);
+ }
+ return 0;
+ }
+ catch (SystemError & e) {
+ return -e.syserrno;
+ }
}
-}
-int
-FuseApp::fgetattr(const char *, struct stat * s, fuse_file_info * fi)
-{
- try {
- auto of = getProxy<OpenFilePtr>(fi->fh);
- of->wait();
- auto remote = of->remote;
- *s = converter.convert(remote->fgetattr(reqEnv()));
- return 0;
- }
- catch (SystemError & e) {
- return -e.syserrno;
+ int
+ FuseApp::getattr(const char * path, struct stat * stat, fuse_file_info * fileInfo)
+ {
+ try {
+ if (fileInfo) {
+ auto openFile = getProxy<OpenFilePtr>(fileInfo->fh);
+ openFile->wait();
+ *stat = converter.convert(openFile->remote->fgetattr());
+ }
+ else {
+ if (auto cacehedStat = statCache.get(std::filesystem::hash_value(path))) {
+ *stat = *cacehedStat;
+ }
+ else {
+ *stat = converter.convert(volume->getattr(reqEnv(), path));
+ }
+ }
+ return stat->st_mode ? 0 : -ENOENT;
+ }
+ catch (SystemError & e) {
+ return -e.syserrno;
+ }
}
-}
-int
-FuseApp::unlink(const char * p)
-{
- try {
- volume->unlink(reqEnv(), p);
- return 0;
- }
- catch (SystemError & e) {
- return -e.syserrno;
+ int
+ FuseApp::unlink(const char * path)
+ {
+ try {
+ volume->unlink(reqEnv(), path);
+ return 0;
+ }
+ catch (SystemError & e) {
+ return -e.syserrno;
+ }
}
}
-}
-
diff --git a/netfs/fuse/fuseFiles.h b/netfs/fuse/fuseFiles.h
index 5e05ad2..672d30f 100644
--- a/netfs/fuse/fuseFiles.h
+++ b/netfs/fuse/fuseFiles.h
@@ -1,17 +1,32 @@
-#ifndef NETFS_FUSE_FILES_H
-#define NETFS_FUSE_FILES_H
+#pragma once
#include "fuseApp.h"
+#include <boost/icl/interval_map.hpp>
namespace NetFS {
- class FuseApp::OpenFile::WriteState {
+ class FuseApp::OpenFile {
+ public:
+ OpenFile(FilePrxPtr remotePrx, std::string remotePath, int openFlags, size_t messageMaxSize);
+
+ void flush();
+ void wait() const;
+
+ FilePrxPtr remote;
+ const std::string path;
+ const int flags;
+ const size_t bodyMaxSize;
+
+ class WriteState {
public:
WriteState();
std::promise<void> promise;
std::shared_future<void> future;
+ };
+
+ using BGs = boost::icl::interval_map<size_t, std::shared_ptr<WriteState>>;
+ static BGs::interval_type range(off_t, size_t);
+ BGs bg;
+ mutable std::shared_mutex mutex;
};
}
-
-#endif
-
diff --git a/netfs/fuse/fuseMappers.ice b/netfs/fuse/fuseMappers.ice
new file mode 100644
index 0000000..c404472
--- /dev/null
+++ b/netfs/fuse/fuseMappers.ice
@@ -0,0 +1,23 @@
+#include <mapper.ice>
+
+["slicer:include:fuseMappersImpl.h"]
+module NetFS {
+ module Client {
+ ["slicer:implementation:NetFS.Client.HideUnknownMapperImpl","slicer:typename:hide"]
+ local class HideUnknownMapper extends Mapping::Mapper {
+
+ };
+ ["slicer:implementation:NetFS.Client.MaskUnknownMapperImpl","slicer:typename:mask"]
+ local class MaskUnknownMapper extends Mapping::Mapper {
+ ["slicer:name:unknownuser"]
+ string UnknownUser = "nobody";
+ ["slicer:name:unknowngroup"]
+ string UnknownGroup = "nogroup";
+ ["slicer:name:usermask","slicer:conversion:std.string:NetFS.Client.fromOctal:NetFS.Client.toOctal:nodeclare"]
+ int UserMask = 0700;
+ ["slicer:name:groupmask","slicer:conversion:std.string:NetFS.Client.fromOctal:NetFS.Client.toOctal:nodeclare"]
+ int GroupMask = 0070;
+ };
+ };
+};
+
diff --git a/netfs/fuse/fuseMappersImpl.cpp b/netfs/fuse/fuseMappersImpl.cpp
new file mode 100644
index 0000000..a6ac148
--- /dev/null
+++ b/netfs/fuse/fuseMappersImpl.cpp
@@ -0,0 +1,74 @@
+#include "fuseMappersImpl.h"
+#include <entCache.h>
+#include <format>
+#include <numeric.h>
+
+namespace NetFS::Client {
+ constexpr int MASK_EVERYTHING = ~0;
+
+ Mapping::FileSystem
+ HideUnknownMapperImpl::mapTransport(const std::string & userName, const std::string & groupName)
+ {
+ auto user = users->getEntry(userName);
+ auto group = groups->getEntry(groupName);
+ if (!user || !group) {
+ return {.uid = 0, .gid = 0, .mask = MASK_EVERYTHING};
+ }
+ return {.uid = safe(user->id), .gid = safe(group->id), .mask = 0};
+ }
+
+ Mapping::Transport
+ HideUnknownMapperImpl::mapFileSystem(int uid, int gid)
+ {
+ auto user = users->getEntry(safe {uid});
+ auto group = groups->getEntry(safe {gid});
+ if (!user || !group) {
+ throw NetFS::SystemError(EPERM);
+ }
+ return {.username = user->name, .groupname = group->name, .mask = 0};
+ }
+
+ Mapping::FileSystem
+ MaskUnknownMapperImpl::mapTransport(const std::string & userName, const std::string & groupName)
+ {
+ int mask = 0;
+ auto apply = [&mask](const auto & resolver, const auto & entry, const auto & fallbackentry, auto entrymask) {
+ auto resolvedEntry = resolver->getEntry(entry);
+ if (!resolvedEntry) {
+ resolvedEntry = resolver->getEntry(fallbackentry);
+ if (!resolvedEntry) {
+ throw NetFS::SystemError(EPERM);
+ }
+ mask |= entrymask;
+ }
+ return resolvedEntry;
+ };
+ auto user = apply(users, userName, UnknownUser, UserMask);
+ auto group = apply(groups, groupName, UnknownGroup, GroupMask);
+ return {.uid = safe(user->id), .gid = safe(group->id), .mask = mask};
+ }
+
+ Mapping::Transport
+ MaskUnknownMapperImpl::mapFileSystem(int uid, int gid)
+ {
+ auto user = users->getEntry(safe {uid});
+ auto group = groups->getEntry(safe {gid});
+ if (!user || !group) {
+ throw NetFS::SystemError(EPERM);
+ }
+ return {.username = user->name, .groupname = group->name, .mask = 0};
+ }
+
+ Ice::Int
+ fromOctal(const std::string & input)
+ {
+ static constexpr int OCTAL_BASE = 8;
+ return std::stoi(input, nullptr, OCTAL_BASE);
+ }
+
+ std::string
+ toOctal(const Ice::Int & input)
+ {
+ return std::format("{:o}", input);
+ }
+}
diff --git a/netfs/fuse/fuseMappersImpl.h b/netfs/fuse/fuseMappersImpl.h
new file mode 100644
index 0000000..f97acb3
--- /dev/null
+++ b/netfs/fuse/fuseMappersImpl.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "baseMapper.h"
+#include <fuseMappers.h>
+#include <visibility.h>
+
+namespace NetFS::Client {
+ std::string toOctal(const Ice::Int &);
+ Ice::Int fromOctal(const std::string &);
+
+ class DLL_PUBLIC HideUnknownMapperImpl : public HideUnknownMapper, Mapping::BaseMapper {
+ public:
+ using BaseMapper::BaseMapper;
+
+ Mapping::Transport mapFileSystem(int uid, int gid) override;
+ Mapping::FileSystem mapTransport(const std::string & userName, const std::string & groupName) override;
+ };
+
+ class DLL_PUBLIC MaskUnknownMapperImpl : public MaskUnknownMapper, Mapping::BaseMapper {
+ public:
+ using BaseMapper::BaseMapper;
+
+ Mapping::Transport mapFileSystem(int uid, int gid) override;
+ Mapping::FileSystem mapTransport(const std::string & userName, const std::string & groupName) override;
+ };
+}
diff --git a/netfs/fuse/fuseMisc.cpp b/netfs/fuse/fuseMisc.cpp
index 5ece9a1..f5d5f67 100644
--- a/netfs/fuse/fuseMisc.cpp
+++ b/netfs/fuse/fuseMisc.cpp
@@ -1,127 +1,116 @@
#include "fuseApp.h"
-#include <string.h>
+#include <cstring>
#include <entCache.h>
+#include <numeric.h>
+#include <span>
-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 {
- auto cacehedStat = statCache.get(p);
- if (cacehedStat) {
- *s = *cacehedStat;
- }
- else {
- *s = converter.convert(volume->getattr(reqEnv(), p));
- }
- return 0;
- }
- catch (NetFS::SystemError & e) {
- return -e.syserrno;
+namespace NetFS {
+ int
+ FuseApp::access(const char * path, int accessMode)
+ {
+ return -volume->access(reqEnv(), path, accessMode);
}
-}
-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
+ FuseApp::chmod(const char * path, mode_t mode, fuse_file_info *)
+ {
+ try {
+ volume->chmod(reqEnv(), path, safe {mode});
+ 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
+ FuseApp::chown(const char * path, uid_t uid, gid_t gid, fuse_file_info *)
+ {
+ try {
+ volume->chown(reqEnv(), path, safe {uid}, safe {gid});
+ 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
+ FuseApp::link(const char * path1, const char * path2)
+ {
+ try {
+ volume->link(reqEnv(), path1, path2);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
}
-}
-int
-NetFS::FuseApp::mknod(const char * p, mode_t mode, dev_t dev)
-{
- try {
- volume->mknod(reqEnv(), p, mode, dev);
- return 0;
- }
- catch (NetFS::SystemError & e) {
- return -e.syserrno;
+ int
+ FuseApp::mknod(const char * path, mode_t mode, dev_t dev)
+ {
+ try {
+ volume->mknod(reqEnv(), path, safe {mode}, safe {dev});
+ 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
+ FuseApp::symlink(const char * path1, const char * path2)
+ {
+ try {
+ volume->symlink(reqEnv(), path1, path2);
+ 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
+ FuseApp::readlink(const char * path, char * path2, size_t size)
+ {
+ try {
+ const auto link = volume->readlink(reqEnv(), path);
+ if (size <= link.length()) {
+ return -ENAMETOOLONG;
+ }
+ link.copy(path2, size);
+ 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
+ FuseApp::rename(const char * path1, const char * path2, unsigned int flags)
+ {
+ try {
+ volume->rename(reqEnv(), path1, path2, safe {flags});
+ 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;
+ int
+ // NOLINTNEXTLINE(*-c-arrays)
+ FuseApp::utimens(const char * path, const struct timespec times[2], fuse_file_info *)
+ {
+ try {
+ std::span<const struct timespec, 2> timesSpan(times, 2);
+ volume->utimens(reqEnv(), path, timesSpan.front().tv_sec, timesSpan.front().tv_nsec,
+ timesSpan.back().tv_sec, timesSpan.back().tv_nsec);
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
}
}
-
diff --git a/netfs/fuse/fuseSystem.cpp b/netfs/fuse/fuseSystem.cpp
index ac87d14..3af1b36 100644
--- a/netfs/fuse/fuseSystem.cpp
+++ b/netfs/fuse/fuseSystem.cpp
@@ -1,14 +1,15 @@
#include "fuseApp.h"
-int
-NetFS::FuseApp::statfs(const char * p, struct statvfs * vfs)
-{
- try {
- *vfs = converter.TypeConverter::convert(volume->statfs(reqEnv(), p));
- return 0;
- }
- catch (NetFS::SystemError & e) {
- return -e.syserrno;
+namespace NetFS {
+ int
+ FuseApp::statfs(const char * path, struct statvfs * vfs)
+ {
+ try {
+ *vfs = converter.TypeConverter::convert(volume->statfs(reqEnv(), path));
+ return 0;
+ }
+ catch (NetFS::SystemError & e) {
+ return -e.syserrno;
+ }
}
}
-
diff --git a/netfs/fuse/netfs.cpp b/netfs/fuse/netfs.cpp
index 2039eed..a28ab16 100644
--- a/netfs/fuse/netfs.cpp
+++ b/netfs/fuse/netfs.cpp
@@ -1,43 +1,53 @@
#include "fuseApp.h"
+#include <c++11Helpers.h>
#include <syslog.h>
-class FuseImpl : public NetFS::FuseApp {
- public:
- FuseImpl(const Ice::StringSeq & a) :
- NetFS::FuseApp(a)
- {
- openlog("netfs", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
- }
-
- ~FuseImpl()
- {
- closelog();
- }
-
- struct fuse_context * fuse_get_context() override
- {
- return ::fuse_get_context();
- }
-
- int fuse_opt_parse(struct fuse_args * args, void * data, const struct fuse_opt opts[], fuse_opt_proc_t proc) override
- {
- return ::fuse_opt_parse(args, data, opts, proc);
- }
-
- int main(int argc, char ** argv, const struct fuse_operations * ops) override
- {
- return ::fuse_main(argc, argv, ops, this);
- }
-
- void vlogf(int priority, const char * fmt, va_list args) const throw() override
- {
- vsyslog(priority, fmt, args);
- }
+class FuseImpl : public fuse_args, public NetFS::FuseApp {
+public:
+ FuseImpl(int argcIn, char ** argvIn) :
+ fuse_args(FUSE_ARGS_INIT(argcIn, argvIn)), NetFS::FuseApp([this]() {
+ Ice::StringSeq rtn;
+ if (fuse_opt_parse(this, &rtn, nullptr, opt_parse) == -1) {
+ exit(-1);
+ }
+ return rtn;
+ }())
+ {
+ openlog("netfs", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
+ }
+
+ ~FuseImpl() override
+ {
+ closelog();
+ }
+
+ SPECIAL_MEMBERS_DELETE(FuseImpl);
+
+ struct fuse_context *
+ fuseGetContext() override
+ {
+ return ::fuse_get_context();
+ }
+
+ int
+ run()
+ {
+ return ::fuse_main(argc, argv, &OPERATIONS, this);
+ }
+
+ void
+ vlogf(int priority, const char * fmt, va_list args) const noexcept override
+ {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+ // NOLINTNEXTLINE(clang-diagnostic-format-nonliteral)
+ vsyslog(priority, fmt, args);
+#pragma GCC diagnostic push
+ }
};
int
-main(int argc, char* argv[])
+main(int argc, char * argv[])
{
- return FuseAppBase::run(argc, argv, new FuseImpl(Ice::argsToStringSeq(argc, argv)));
+ return FuseImpl(argc, argv).run();
}
-
diff --git a/netfs/ice/Jamfile.jam b/netfs/ice/Jamfile.jam
index 6f5036f..a695712 100644
--- a/netfs/ice/Jamfile.jam
+++ b/netfs/ice/Jamfile.jam
@@ -1,14 +1,28 @@
import package ;
+obj directory : directory.ice : <toolset>tidy:<checker>none ;
+obj exceptions : exceptions.ice : <toolset>tidy:<checker>none ;
+obj file : file.ice : <toolset>tidy:<checker>none ;
+obj service : service.ice : <toolset>tidy:<checker>none ;
+obj types : types.ice : <toolset>tidy:<checker>none ;
+obj volume : volume.ice : <toolset>tidy:<checker>none ;
+obj mapper : mapper.ice : <toolset>tidy:<checker>none ;
+alias gen : directory exceptions file service types volume mapper ;
lib netfs-api :
- [ glob *.cpp *.ice ] :
+ gen
+ mapper.ice
+ [ glob *.cpp ] :
<library>..//Ice
<library>..//pthread
<library>..//adhocutil
+ <library>..//slicer
+ <implicit-dependency>gen
+ <slicer>pure
: :
<include>.
<library>..//Ice
<library>..//pthread
+ <implicit-dependency>gen
;
explicit install ;
diff --git a/netfs/ice/directory.ice b/netfs/ice/directory.ice
index 3718f69..4f5ce79 100644
--- a/netfs/ice/directory.ice
+++ b/netfs/ice/directory.ice
@@ -1,5 +1,4 @@
-#ifndef _DIRECTORY
-#define _DIRECTORY
+#pragma once
#include "exceptions.ice"
#include "types.ice"
@@ -9,11 +8,6 @@ module NetFS {
void close() throws AuthError, SystemError;
idempotent NameList readdir() throws AuthError, SystemError;
- };
- interface DirectoryV2 extends Directory {
idempotent DirectoryContents listdir() throws AuthError, SystemError;
};
};
-
-#endif
-
diff --git a/netfs/ice/entryResolver.h b/netfs/ice/entryResolver.h
deleted file mode 100644
index 1a20ec3..0000000
--- a/netfs/ice/entryResolver.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef NETFS_ENTRYRESOLVER_H
-#define NETFS_ENTRYRESOLVER_H
-
-#include <string>
-#include <stdlib.h>
-
-template <class entry_t>
-class EntryResolver {
- public:
- typedef std::string name_t;
- virtual void getID(const name_t &, id_t *) const = 0;
- virtual void getName(const id_t &, name_t *) const = 0;
-};
-
-#endif
-
diff --git a/netfs/ice/exceptions.ice b/netfs/ice/exceptions.ice
index 02a42e0..4f12321 100644
--- a/netfs/ice/exceptions.ice
+++ b/netfs/ice/exceptions.ice
@@ -1,5 +1,4 @@
-#ifndef _EXCEPTIONS
-#define _EXCEPTIONS
+#pragma once
module NetFS {
// Exceptions
@@ -18,6 +17,3 @@ module NetFS {
string exportName;
};
};
-
-#endif
-
diff --git a/netfs/ice/exceptionsImpl.cpp b/netfs/ice/exceptionsImpl.cpp
index 93f5930..0c68630 100644
--- a/netfs/ice/exceptionsImpl.cpp
+++ b/netfs/ice/exceptionsImpl.cpp
@@ -1,10 +1,12 @@
-#include <exceptions.h>
#include <compileTimeFormatter.h>
+#include <exceptions.h>
AdHocFormatter(ExportNotFoundMsg, "Export [%?] not found on remote host");
-void
-NetFS::ExportNotFound::ice_print(std::ostream & s) const
-{
- ExportNotFoundMsg::write(s, exportName);
-}
+namespace NetFS {
+ void
+ ExportNotFound::ice_print(std::ostream & stream) const
+ {
+ ExportNotFoundMsg::write(stream, exportName);
+ }
+}
diff --git a/netfs/ice/file.ice b/netfs/ice/file.ice
index a622745..9fc2bf8 100644
--- a/netfs/ice/file.ice
+++ b/netfs/ice/file.ice
@@ -1,5 +1,4 @@
-#ifndef _FILES
-#define _FILES
+#pragma once
#include "exceptions.ice"
#include "types.ice"
@@ -8,12 +7,10 @@ module NetFS {
interface File {
void close() throws AuthError, SystemError;
- idempotent Attr fgetattr(ReqEnv env) throws AuthError, SystemError;
+ idempotent Attr fgetattr() throws AuthError, SystemError;
idempotent Buffer read(long offset, long size) throws AuthError, SystemError;
- idempotent void ftruncate(ReqEnv env, long size) throws AuthError, SystemError;
- idempotent void write(long offset, long size, Buffer data) throws AuthError, SystemError;
+ idempotent void ftruncate(long size) throws AuthError, SystemError;
+ idempotent void write(long offset, long size, ["cpp:array"] Buffer data) throws AuthError, SystemError;
+ idempotent long copyrange(File * to, long offsetsrc, long offsetdest, long size, int flags) throws AuthError, SystemError;
};
};
-
-#endif
-
diff --git a/netfs/ice/mapper.ice b/netfs/ice/mapper.ice
new file mode 100644
index 0000000..ce2ecb2
--- /dev/null
+++ b/netfs/ice/mapper.ice
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "exceptions.ice"
+
+module NetFS {
+ module Mapping {
+ ["slicer:ignore"]
+ local struct Transport {
+ string username;
+ string groupname;
+ int mask;
+ };
+
+ ["slicer:ignore"]
+ local struct FileSystem {
+ int uid;
+ int gid;
+ int mask;
+ };
+
+ ["slicer:typename:default","slicer:typeid:type"]
+ local class Mapper {
+ Transport mapFileSystem(int uid, int gid) throws SystemError;
+ FileSystem mapTransport(string un, string gn) throws SystemError;
+ };
+ };
+
+}
diff --git a/netfs/ice/numeric.cpp b/netfs/ice/numeric.cpp
new file mode 100644
index 0000000..3d86f28
--- /dev/null
+++ b/netfs/ice/numeric.cpp
@@ -0,0 +1,21 @@
+#include "numeric.h"
+#include <stdexcept>
+#include <version>
+
+#ifdef __cpp_lib_source_location
+# include <string>
+
+void
+safe_base::out_of_range(std::string desc)
+{
+ throw std::out_of_range {std::move(desc)};
+}
+
+#else
+
+void
+safe_base::out_of_range(const char * const function)
+{
+ throw std::out_of_range {function};
+}
+#endif
diff --git a/netfs/ice/numeric.h b/netfs/ice/numeric.h
new file mode 100644
index 0000000..e47dc27
--- /dev/null
+++ b/netfs/ice/numeric.h
@@ -0,0 +1,113 @@
+#pragma once
+
+#include <Ice/Optional.h>
+#include <concepts>
+#include <limits>
+#include <optional>
+#include <utility>
+#include <version>
+#include <visibility.h>
+
+#ifdef __cpp_lib_source_location
+# include <compileTimeFormatter.h>
+# include <source_location>
+# include <string>
+AdHocFormatter(ConvPos, "%?:%?(value: %?)");
+#endif
+
+struct DLL_PUBLIC safe_base {
+protected:
+#ifdef __cpp_lib_source_location
+ [[noreturn]] static void out_of_range(std::string);
+#else
+ [[noreturn]] static void out_of_range(const char * const);
+#endif
+};
+
+template<std::integral T> struct safe : safe_base {
+#ifdef __cpp_lib_source_location
+ // cppcheck-suppress noExplicitConstructor; NOLINTNEXTLINE(hicpp-explicit-conversions)
+ constexpr safe(T v, const std::source_location w = std::source_location::current()) noexcept : value {v}, where {w}
+ {
+ }
+#else
+ // cppcheck-suppress noExplicitConstructor; NOLINTNEXTLINE(hicpp-explicit-conversions)
+ constexpr safe(T v) noexcept : value {v} { }
+#endif
+
+ // NOLINTNEXTLINE(hicpp-explicit-conversions)
+ template<std::integral R> constexpr inline operator R() const
+ {
+ if constexpr (std::cmp_less_equal(std::numeric_limits<R>::min(), min)
+ && std::cmp_greater_equal(std::numeric_limits<R>::max(), max)) {
+ return value;
+ }
+ else {
+ if (!std::in_range<R>(value)) {
+ [[unlikely]]
+#ifdef __cpp_lib_source_location
+ out_of_range(ConvPos::get(where.function_name(), where.line(), value));
+#else
+ out_of_range(__PRETTY_FUNCTION__);
+#endif
+ }
+ return static_cast<R>(value);
+ }
+ }
+
+ template<std::integral R> constexpr inline operator std::optional<R>() const
+ {
+ return this->operator R();
+ }
+
+ template<std::integral R> constexpr inline operator Ice::optional<R>() const
+ {
+ return this->operator R();
+ }
+
+private:
+ T value;
+#ifdef __cpp_lib_source_location
+ const std::source_location where;
+#endif
+ constexpr static T min {std::numeric_limits<T>::min()};
+ constexpr static T max {std::numeric_limits<T>::max()};
+};
+
+template<std::integral Left, std::integral Right>
+auto
+operator+(const Left left, const safe<Right> right)
+{
+ return left + static_cast<Left>(right);
+}
+
+template<std::integral Left, std::integral Right>
+auto
+operator+=(Left & left, const safe<Right> right)
+{
+ return left += static_cast<Left>(right);
+}
+
+template<typename T>
+concept pointer = std::is_pointer_v<T>;
+
+template<pointer Left, std::integral Right>
+auto
+operator+=(Left & left, const safe<Right> right)
+{
+ return left += static_cast<Right>(right);
+}
+
+template<pointer Left, std::integral Right>
+auto
+operator+(const Left left, const safe<Right> right)
+{
+ return left + static_cast<Right>(right);
+}
+
+template<std::integral Left, std::integral Right>
+auto
+operator+(const safe<Left> left, const Right right)
+{
+ return static_cast<Right>(left) + right;
+}
diff --git a/netfs/ice/service.ice b/netfs/ice/service.ice
index d691ae2..1d13e8d 100644
--- a/netfs/ice/service.ice
+++ b/netfs/ice/service.ice
@@ -1,5 +1,4 @@
-#ifndef _SERVICE
-#define _SERVICE
+#pragma once
#include "exceptions.ice"
#include "types.ice"
@@ -7,10 +6,7 @@
module NetFS {
interface Service {
- // NameList volumes() throws ConfigError;
Volume * connect(string volume, string auth) throws AuthError, ConfigError;
+ Settings getSettings();
};
};
-
-#endif
-
diff --git a/netfs/ice/typeConverter.cpp b/netfs/ice/typeConverter.cpp
index 46e8ff1..5db7315 100644
--- a/netfs/ice/typeConverter.cpp
+++ b/netfs/ice/typeConverter.cpp
@@ -1,90 +1,84 @@
#include "typeConverter.h"
+#include "numeric.h"
-EntryTypeConverter::EntryTypeConverter(const EntryResolver<uid_t> & u, const EntryResolver<gid_t> & g) :
- userLookup(u),
- groupLookup(g)
-{
-}
+namespace NetFS {
+ EntryTypeConverter::EntryTypeConverter(NetFS::Mapping::MapperPtr mapperImpl) : mapper(std::move(mapperImpl)) { }
-struct stat
-EntryTypeConverter::convert(const NetFS::Attr & a) const
-{
- struct stat s;
- s.st_dev = a.dev;
- s.st_ino = a.inode;
- s.st_mode = a.mode;
- s.st_nlink = a.links;
- userLookup.getID(a.uid, &s.st_uid);
- groupLookup.getID(a.gid, &s.st_gid);
- s.st_rdev = a.rdev;
- s.st_size = a.size;
- s.st_blksize = a.blockSize;
- s.st_blocks = a.blocks;
- s.st_atime = a.atime;
- s.st_atim.tv_sec = a.atime;
- s.st_atim.tv_nsec = 0;
- s.st_mtime = a.mtime;
- s.st_mtim.tv_sec = a.mtime;
- s.st_mtim.tv_nsec = 0;
- s.st_ctime = a.ctime;
- s.st_ctim.tv_sec = a.ctime;
- s.st_ctim.tv_nsec = 0;
- return s;
-}
+ struct stat
+ EntryTypeConverter::convert(const NetFS::Attr & attr) const
+ {
+ auto map = mapper->mapTransport(attr.uid, attr.gid);
+ struct stat stat {};
+ static_assert(sizeof(stat.st_ino) == sizeof(attr.inode));
+ stat.st_dev = safe {attr.dev};
+ std::memcpy(&stat.st_ino, &attr.inode, sizeof(stat.st_ino));
+ stat.st_mode = safe {attr.mode & ~map.mask};
+ stat.st_nlink = safe {attr.links};
+ stat.st_uid = safe {map.uid};
+ stat.st_gid = safe {map.gid};
+ stat.st_rdev = safe {attr.rdev};
+ stat.st_size = safe {attr.size};
+ stat.st_blksize = safe {attr.blockSize};
+ stat.st_blocks = safe {attr.blocks};
+ stat.st_atime = safe {attr.atime};
+ stat.st_mtime = safe {attr.mtime};
+ stat.st_ctime = safe {attr.ctime};
+ return stat;
+ }
-struct statvfs
-TypeConverter::convert(const NetFS::VFS & v) const
-{
- struct statvfs vfs;
- vfs.f_bsize = v.blockSize;
- vfs.f_frsize = v.fragmentSize;
- vfs.f_blocks = v.blocks;
- vfs.f_bfree = v.freeBlocks;
- vfs.f_bavail = v.availBlocks;
- vfs.f_files = v.files;
- vfs.f_ffree = v.freeFiles;
- vfs.f_favail = v.availFiles;
- vfs.f_fsid = v.FSID;
- vfs.f_flag = v.flags;
- vfs.f_namemax = v.maxNameLen;
- return vfs;
-}
+ struct statvfs
+ TypeConverter::convert(const NetFS::VFS & vfs)
+ {
+ struct statvfs statvfs {};
+ statvfs.f_bsize = safe {vfs.blockSize};
+ statvfs.f_frsize = safe {vfs.fragmentSize};
+ statvfs.f_blocks = safe {vfs.blocks};
+ statvfs.f_bfree = safe {vfs.freeBlocks};
+ statvfs.f_bavail = safe {vfs.availBlocks};
+ statvfs.f_files = safe {vfs.files};
+ statvfs.f_ffree = safe {vfs.freeFiles};
+ statvfs.f_favail = safe {vfs.availFiles};
+ statvfs.f_flag = safe {vfs.flags};
+ statvfs.f_namemax = safe {vfs.maxNameLen};
+ return statvfs;
+ }
-NetFS::Attr
-EntryTypeConverter::convert(const struct stat & s) const
-{
- NetFS::Attr a;
- a.dev = s.st_dev;
- a.inode = s.st_ino;
- a.mode = s.st_mode;
- a.links = s.st_nlink;
- userLookup.getName(s.st_uid, &a.uid);
- groupLookup.getName(s.st_gid, &a.gid);
- a.rdev = s.st_rdev;
- a.size = s.st_size;
- a.blockSize = s.st_blksize;
- a.blocks = s.st_blocks;
- a.atime = s.st_atime;
- a.mtime = s.st_mtime;
- a.ctime = s.st_ctime;
- return a;
-}
+ NetFS::Attr
+ EntryTypeConverter::convert(const struct stat & stat) const
+ {
+ auto map = mapper->mapFileSystem(safe {stat.st_uid}, safe {stat.st_gid});
+ NetFS::Attr attr {};
+ static_assert(sizeof(stat.st_ino) == sizeof(attr.inode));
+ attr.dev = safe {stat.st_dev};
+ std::memcpy(&attr.inode, &stat.st_ino, sizeof(attr.inode));
+ attr.mode = safe {stat.st_mode & static_cast<decltype(stat.st_mode)>(~map.mask)};
+ attr.links = safe {stat.st_nlink};
+ attr.uid = std::move(map.username);
+ attr.gid = std::move(map.groupname);
+ attr.rdev = safe {stat.st_rdev};
+ attr.size = safe {stat.st_size};
+ attr.blockSize = safe {stat.st_blksize};
+ attr.blocks = safe {stat.st_blocks};
+ attr.atime = safe {stat.st_atime};
+ attr.mtime = safe {stat.st_mtime};
+ attr.ctime = safe {stat.st_ctime};
+ return attr;
+ }
-NetFS::VFS
-TypeConverter::convert(const struct statvfs & s) const
-{
- NetFS::VFS t;
- t.blockSize = s.f_bsize;
- t.fragmentSize = s.f_frsize;
- t.blocks = s.f_blocks;
- t.freeBlocks = s.f_bfree;
- t.availBlocks = s.f_bavail;
- t.files = s.f_files;
- t.freeFiles = s.f_ffree;
- t.availFiles = s.f_favail;
- t.FSID = s.f_fsid;
- t.flags = s.f_flag;
- t.maxNameLen = s.f_namemax;
- return t;
+ NetFS::VFS
+ TypeConverter::convert(const struct statvfs & statvfs)
+ {
+ NetFS::VFS vfs {};
+ vfs.blockSize = safe {statvfs.f_bsize};
+ vfs.fragmentSize = safe {statvfs.f_frsize};
+ vfs.blocks = safe {statvfs.f_blocks};
+ vfs.freeBlocks = safe {statvfs.f_bfree};
+ vfs.availBlocks = safe {statvfs.f_bavail};
+ vfs.files = safe {statvfs.f_files};
+ vfs.freeFiles = safe {statvfs.f_ffree};
+ vfs.availFiles = safe {statvfs.f_favail};
+ vfs.flags = safe {statvfs.f_flag};
+ vfs.maxNameLen = safe {statvfs.f_namemax};
+ return vfs;
+ }
}
-
diff --git a/netfs/ice/typeConverter.h b/netfs/ice/typeConverter.h
index b8d6fcd..ef8a35a 100644
--- a/netfs/ice/typeConverter.h
+++ b/netfs/ice/typeConverter.h
@@ -1,31 +1,28 @@
-#ifndef NETFS_TYPECONVERT_H
-#define NETFS_TYPECONVERT_H
+#pragma once
-#include <types.h>
+#include <mapper.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
+#include <types.h>
#include <visibility.h>
-#include "entryResolver.h"
-class DLL_PUBLIC TypeConverter {
+namespace NetFS {
+ class DLL_PUBLIC TypeConverter {
public:
// VFS
- struct statvfs convert(const NetFS::VFS &) const;
- NetFS::VFS convert(const struct statvfs &) const;
-};
+ [[nodiscard]] static struct statvfs convert(const NetFS::VFS &);
+ [[nodiscard]] static NetFS::VFS convert(const struct statvfs &);
+ };
-class DLL_PUBLIC EntryTypeConverter : public TypeConverter {
+ class DLL_PUBLIC EntryTypeConverter : public TypeConverter {
public:
- EntryTypeConverter(const EntryResolver<uid_t> &, const EntryResolver<gid_t> &);
+ EntryTypeConverter() = default;
+ explicit EntryTypeConverter(NetFS::Mapping::MapperPtr);
// Attributes
- struct stat convert(const NetFS::Attr &) const;
- NetFS::Attr convert(const struct stat &) const;
-
- protected:
- const EntryResolver<uid_t> & userLookup;
- const EntryResolver<gid_t> & groupLookup;
-};
-
-#endif
+ [[nodiscard]] struct stat convert(const NetFS::Attr &) const;
+ [[nodiscard]] NetFS::Attr convert(const struct stat &) const;
+ NetFS::Mapping::MapperPtr mapper;
+ };
+}
diff --git a/netfs/ice/types.ice b/netfs/ice/types.ice
index de83f6f..3ca6b07 100644
--- a/netfs/ice/types.ice
+++ b/netfs/ice/types.ice
@@ -1,5 +1,4 @@
-#ifndef _TYPES
-#define _TYPES
+#pragma once
module NetFS {
struct VFS {
@@ -37,12 +36,13 @@ module NetFS {
string grp;
};
+ class Settings {
+ optional(0) int MessageSizeMax;
+ };
+
sequence<byte> Buffer;
sequence<string> NameList;
dictionary<string, Attr> DirectoryContents;
};
-
-#endif
-
diff --git a/netfs/ice/volume.ice b/netfs/ice/volume.ice
index 8d5d9a7..a41953a 100644
--- a/netfs/ice/volume.ice
+++ b/netfs/ice/volume.ice
@@ -1,5 +1,4 @@
-#ifndef _VOLUME
-#define _VOLUME
+#pragma once
#include "exceptions.ice"
#include "types.ice"
@@ -28,12 +27,9 @@ module NetFS {
void mknod(ReqEnv env, string path, int mode, int dev) throws AuthError, SystemError;
void symlink(ReqEnv env, string path1, string path2) throws AuthError, SystemError;
void link(ReqEnv env, string path1, string path2) throws AuthError, SystemError;
- void rename(ReqEnv env, string from, string to) throws AuthError, SystemError;
+ void rename(ReqEnv env, string from, string to, optional(0) int flags) throws AuthError, SystemError;
idempotent void chmod(ReqEnv env, string path, int mode) throws AuthError, SystemError;
idempotent void chown(ReqEnv env, string path, int uid, int gid) throws AuthError, SystemError;
idempotent void utimens(ReqEnv env, string path, long atime, long atimens, long mtime, long mtimens) throws AuthError, SystemError;
};
};
-
-#endif
-
diff --git a/netfs/lib/Jamfile.jam b/netfs/lib/Jamfile.jam
index a2d3454..b3a3285 100644
--- a/netfs/lib/Jamfile.jam
+++ b/netfs/lib/Jamfile.jam
@@ -2,10 +2,14 @@ lib netfs-common :
[ glob *.cpp ]
:
<include>../ice
- <library>../ice//netfs-api
- <library>..//adhocutil
- <implicit-dependency>../ice//netfs-api
+ <link>static
+ <use>..//adhocutil/<link>shared
+ <implicit-dependency>../ice//netfs-api/<link>shared
+ <toolset>gcc:<cxxflags>-fPIC
+ <toolset>clang:<cxxflags>-fPIC
: :
<include>.
+ <library>../ice//netfs-api/<link>shared
+ <library>..//adhocutil/<link>shared
;
diff --git a/netfs/lib/baseMapper.cpp b/netfs/lib/baseMapper.cpp
new file mode 100644
index 0000000..4c2b3f0
--- /dev/null
+++ b/netfs/lib/baseMapper.cpp
@@ -0,0 +1,13 @@
+#include "baseMapper.h"
+#include "entCache.h"
+
+namespace NetFS::Mapping {
+ BaseMapper::BaseMapper() : BaseMapper(std::make_shared<UserEntCache>()) { }
+
+ BaseMapper::BaseMapper(const EntryResolverPtr<User> & u) : BaseMapper(u, std::make_shared<GroupEntCache>(u)) { }
+
+ BaseMapper::BaseMapper(EntryResolverPtr<User> u, EntryResolverPtr<Group> g) :
+ users(std::move(u)), groups(std::move(g))
+ {
+ }
+}
diff --git a/netfs/lib/baseMapper.h b/netfs/lib/baseMapper.h
new file mode 100644
index 0000000..b5bb900
--- /dev/null
+++ b/netfs/lib/baseMapper.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "entries.h"
+#include "entryResolver.h"
+#include <mapper.h>
+
+namespace NetFS {
+ namespace Mapping {
+ class BaseMapper {
+ public:
+ BaseMapper();
+ explicit BaseMapper(const EntryResolverPtr<User> & users);
+ BaseMapper(EntryResolverPtr<User> users, EntryResolverPtr<Group> groups);
+
+ protected:
+ EntryResolverPtr<User> users;
+ EntryResolverPtr<Group> groups;
+ };
+ }
+}
diff --git a/netfs/lib/defaultMapper.cpp b/netfs/lib/defaultMapper.cpp
new file mode 100644
index 0000000..1845933
--- /dev/null
+++ b/netfs/lib/defaultMapper.cpp
@@ -0,0 +1,27 @@
+#include "defaultMapper.h"
+#include <exceptions.h>
+#include <numeric.h>
+
+namespace NetFS::Mapping {
+ FileSystem
+ DefaultMapper::mapTransport(const std::string & un, const std::string & gn)
+ {
+ auto u = users->getEntry(un);
+ auto g = groups->getEntry(gn);
+ if (!u || !g) {
+ throw NetFS::SystemError(EPERM);
+ }
+ return {safe {u->id}, safe {g->id}, 0};
+ }
+
+ Transport
+ DefaultMapper::mapFileSystem(int uid, int gid)
+ {
+ auto u = users->getEntry(safe {uid});
+ auto g = groups->getEntry(safe {gid});
+ if (!u || !g) {
+ throw NetFS::SystemError(EPERM);
+ }
+ return {u->name, g->name, 0};
+ }
+}
diff --git a/netfs/lib/defaultMapper.h b/netfs/lib/defaultMapper.h
new file mode 100644
index 0000000..d4fb77f
--- /dev/null
+++ b/netfs/lib/defaultMapper.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "baseMapper.h"
+#include "entries.h"
+#include "entryResolver.h"
+#include <mapper.h>
+
+namespace NetFS {
+ namespace Mapping {
+ class DefaultMapper : public Mapper, BaseMapper {
+ public:
+ using BaseMapper::BaseMapper;
+
+ Transport mapFileSystem(int uid, int gid) override;
+ FileSystem mapTransport(const std::string & un, const std::string & gn) override;
+ };
+ }
+}
diff --git a/netfs/lib/entCache.cpp b/netfs/lib/entCache.cpp
index fbdba3e..a2ea685 100644
--- a/netfs/lib/entCache.cpp
+++ b/netfs/lib/entCache.cpp
@@ -1,143 +1,46 @@
-#include "entCache.h"
-#include <exceptions.h>
-#include <lockHelpers.h>
-#include <pwd.h>
-#include <grp.h>
-#include <visibility.h>
+#include "entCache.impl.h"
+#include "entries.h"
-template<class entry_t>
-EntCache<entry_t>::EntCache() :
- fillTime(0)
-{
-}
-
-template<class entry_t>
-EntCache<entry_t>::~EntCache()
-{
-}
-
-template<class entry_t>
-void
-EntCache<entry_t>::getID(const EntCache<entry_t>::name_t & u, EntCache<entry_t>::id_t * target) const
-{
- auto e = getEntry(u);
- *target = e->id;
-}
-
-template<class entry_t>
-void
-EntCache<entry_t>::getName(const EntCache<entry_t>::id_t & u, EntCache<entry_t>::name_t * target) const
-{
- auto e = getEntry(u);
- *target = e->name;
-}
-
-template<class entry_t>
-template<class key_t>
-typename EntCache<entry_t>::entry_ptr
-EntCache<entry_t>::getEntry(const key_t & key) const
-{
- typename EntCache<entry_t>::entry_ptr ent;
- if (fillTime + 60 > time(NULL)) {
- ent = getEntryNoFill<key_t>(key);
- }
- if (!ent) {
- fillCache();
- ent = getEntryNoFill<key_t>(key);
- }
- if (!ent) {
- throw NetFS::SystemError(EPERM);
- }
- return ent;
-}
-
-template<class entry_t>
-template<class key_t>
-typename EntCache<entry_t>::entry_ptr
-EntCache<entry_t>::getEntryNoFill(const key_t & key) const
-{
- SharedLock(lock);
- auto & collection = idcache.template get<key_t>();
- auto i = collection.find(key);
- if (i != collection.end()) {
- return *i;
- }
- return NULL;
-}
-
-User::User(uid_t u, const std::string & n, gid_t g) :
- id(u),
- name(n),
- group(g)
-{
-}
+template class EntCache<User>;
+template class EntCache<Group>;
const int BUFLEN = 8196;
-template<>
void
-EntCache<User>::fillCache() const
+UserEntCache::fillCache() const noexcept
{
- Lock(lock);
setpwent();
- idcache.clear();
- char buf[BUFLEN];
- struct passwd pwbuf, * pwp;
- while (getpwent_r(&pwbuf, buf, BUFLEN, &pwp) == 0) {
- idcache.insert(std::make_shared<User>(pwp->pw_uid, pwp->pw_name, pwp->pw_gid));
+ idcache->clear();
+ std::array<char, BUFLEN> buf {};
+ struct passwd pwbuf {
+ }, *pwp;
+ while (getpwent_r(&pwbuf, buf.data(), buf.size(), &pwp) == 0) {
+ idcache->insert(std::make_shared<User>(pwp->pw_uid, pwp->pw_name, pwp->pw_gid));
}
endpwent();
time(&fillTime);
}
-Group::Group(gid_t g, const std::string & n) :
- id(g),
- name(n)
-{
-}
+GroupEntCache::GroupEntCache(EntryResolverPtr<User> u) : users(std::move(u)) { }
-template<>
void
-EntCache<Group>::fillCache() const
+GroupEntCache::fillCache() const noexcept
{
- Lock(lock);
setgrent();
- char buf[BUFLEN];
- idcache.clear();
- struct group grpbuf, * grp;
- EntCache<User> instance;
- while (getgrent_r(&grpbuf, buf, BUFLEN, &grp) == 0) {
+ std::array<char, BUFLEN> buf {};
+ idcache->clear();
+ struct group grpbuf {
+ }, *grp;
+ while (getgrent_r(&grpbuf, buf.data(), buf.size(), &grp) == 0) {
auto g = std::make_shared<Group>(grp->gr_gid, grp->gr_name);
for (auto member = grp->gr_mem; *member; member++) {
- try {
- g->members.insert(instance.getEntry((const name_t &)*member)->id);
- }
- catch (const NetFS::SystemError &) {
+ if (auto ent = users->getEntry(*member)) {
+ g->members.insert(ent->id);
}
}
- idcache.insert(g);
+ g->members.insert(g->id);
+ idcache->insert(std::move(g));
}
endgrent();
time(&fillTime);
}
-
-bool
-DLL_PUBLIC Group::hasMember(uid_t u) const
-{
- return (members.find(u) != members.end());
-}
-
-template DLL_PUBLIC EntCache<User>::EntCache();
-template DLL_PUBLIC EntCache<User>::~EntCache();
-template std::shared_ptr<User> DLL_PUBLIC EntCache<User>::getEntry<std::string>(const std::string &) const;
-template std::shared_ptr<User> DLL_PUBLIC EntCache<User>::getEntry<uid_t>(const uid_t &) const;
-template DLL_PUBLIC void EntCache<User>::getName(const gid_t &, std::string *) const;
-template DLL_PUBLIC void EntCache<User>::getID(const std::string &, gid_t *) const;
-
-template DLL_PUBLIC EntCache<Group>::EntCache();
-template DLL_PUBLIC EntCache<Group>::~EntCache();
-template std::shared_ptr<Group> DLL_PUBLIC EntCache<Group>::getEntry<std::string>(const std::string &) const;
-template std::shared_ptr<Group> DLL_PUBLIC EntCache<Group>::getEntry<gid_t>(const gid_t &) const;
-template DLL_PUBLIC void EntCache<Group>::getName(const gid_t &, std::string *) const;
-template DLL_PUBLIC void EntCache<Group>::getID(const std::string &, gid_t *) const;
-
diff --git a/netfs/lib/entCache.h b/netfs/lib/entCache.h
index 4c34a32..2d6d37d 100644
--- a/netfs/lib/entCache.h
+++ b/netfs/lib/entCache.h
@@ -1,67 +1,51 @@
-#ifndef ENTCACHE_H
-#define ENTCACHE_H
+#pragma once
-#include <string>
-#include <set>
-#include <boost/multi_index_container.hpp>
-#include <boost/multi_index/member.hpp>
-#include <boost/multi_index/ordered_index.hpp>
+#include "entries.h"
+#include "entryResolver.h"
+#include <c++11Helpers.h>
+#include <memory>
#include <shared_mutex>
-#include <entryResolver.h>
-class User {
- public:
- User(uid_t, const std::string &, gid_t);
- uid_t id;
- std::string name;
- gid_t group;
+template<class entry_t> class EntCache : public EntryResolver<entry_t> {
+public:
+ EntCache();
+ ~EntCache() noexcept override;
+
+ SPECIAL_MEMBERS_DEFAULT_MOVE_NO_COPY(EntCache);
+
+ using id_t = decltype(entry_t::id);
+ using name_t = decltype(entry_t::name);
+ using entry_ptr = std::shared_ptr<entry_t>;
+
+ [[nodiscard]] entry_ptr inline getEntry(const id_t & i) const noexcept override
+ {
+ return getEntryInternal<id_t>(i);
+ }
+ [[nodiscard]] entry_ptr inline getEntry(const name_t & n) const noexcept override
+ {
+ return getEntryInternal<name_t>(n);
+ };
+
+protected:
+ virtual void fillCache() const noexcept = 0;
+ template<class key_t> [[nodiscard]] entry_ptr getEntryInternal(const key_t &) const noexcept;
+ template<class key_t> [[nodiscard]] entry_ptr getEntryNoFill(const key_t &) const noexcept;
+
+ class Ids;
+ std::unique_ptr<Ids> idcache;
+ mutable std::shared_mutex lock;
+ mutable time_t fillTime {0};
};
-class Group {
- public:
- Group(gid_t, const std::string &);
-
- bool hasMember(uid_t) const;
-
- gid_t id;
- std::string name;
- std::set<uid_t> members;
+class UserEntCache : public EntCache<User> {
+ void fillCache() const noexcept override;
};
-template<class entry_t>
-class EntCache : public EntryResolver<decltype(entry_t::id)> {
- public:
- typedef decltype(entry_t::id) id_t;
- typedef decltype(entry_t::name) name_t;
- typedef std::shared_ptr<entry_t> entry_ptr;
-
- EntCache();
- virtual ~EntCache();
-
- void getID(const name_t &, id_t *) const override;
- void getName(const id_t &, name_t *) const override;
- template<class key_t>
- entry_ptr getEntry(const key_t &) const;
+class GroupEntCache : public EntCache<Group> {
+public:
+ explicit GroupEntCache(EntryResolverPtr<User>);
- protected:
- void fillCache() const;
- template<class key_t>
- entry_ptr getEntryNoFill(const key_t &) const;
-
- typedef boost::multi_index::multi_index_container<std::shared_ptr<entry_t>,
- boost::multi_index::indexed_by<
- boost::multi_index::ordered_unique<
- boost::multi_index::tag<id_t>, BOOST_MULTI_INDEX_MEMBER(entry_t, const id_t, id), std::less<>>,
- boost::multi_index::ordered_unique<
- boost::multi_index::tag<std::string>, BOOST_MULTI_INDEX_MEMBER(entry_t, const std::string, name), std::less<>>
- > > IDs;
- mutable IDs idcache;
- mutable std::shared_mutex lock;
- mutable time_t fillTime;
+private:
+ void fillCache() const noexcept override;
+ EntryResolverPtr<User> users;
};
-
-typedef EntCache<User> UserEntCache;
-typedef EntCache<Group> GroupEntCache;
-
-#endif
-
diff --git a/netfs/lib/entCache.impl.h b/netfs/lib/entCache.impl.h
new file mode 100644
index 0000000..64d5025
--- /dev/null
+++ b/netfs/lib/entCache.impl.h
@@ -0,0 +1,58 @@
+#pragma once
+
+#include "entCache.h"
+#include "entries.h"
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index_container.hpp>
+#include <grp.h>
+#include <lockHelpers.h>
+#include <mutex>
+#include <pwd.h>
+#include <type_traits>
+
+template<class entry_t>
+class EntCache<entry_t>::Ids :
+ public boost::multi_index::multi_index_container<std::shared_ptr<entry_t>,
+ boost::multi_index::indexed_by<boost::multi_index::ordered_unique<boost::multi_index::tag<id_t>,
+ BOOST_MULTI_INDEX_MEMBER(entry_t, const id_t, id), std::less<>>,
+ boost::multi_index::ordered_unique<boost::multi_index::tag<std::string>,
+ BOOST_MULTI_INDEX_MEMBER(entry_t, const std::string, name), std::less<>>>> {
+};
+
+template<class entry_t> EntCache<entry_t>::EntCache() : idcache(std::make_unique<Ids>()) { }
+template<class entry_t> EntCache<entry_t>::~EntCache() noexcept = default;
+
+template<class entry_t>
+template<class key_t>
+typename EntCache<entry_t>::entry_ptr
+EntCache<entry_t>::getEntryInternal(const key_t & key) const noexcept
+{
+ SharedScopeLock(lock) {
+ if (fillTime + 60 > time(nullptr)) {
+ if (auto ent = getEntryNoFill<key_t>(key)) {
+ return ent;
+ }
+ }
+ }
+ ScopeLock(lock) {
+ fillCache();
+ if (auto ent = getEntryNoFill<key_t>(key)) {
+ return ent;
+ }
+ }
+ return getEntryNoFill<key_t>(key);
+}
+
+template<class entry_t>
+template<class key_t>
+typename EntCache<entry_t>::entry_ptr
+EntCache<entry_t>::getEntryNoFill(const key_t & key) const noexcept
+{
+ auto & collection = idcache->template get<key_t>();
+ auto i = collection.find(key);
+ if (i != collection.end()) {
+ return *i;
+ }
+ return nullptr;
+}
diff --git a/netfs/lib/entries.cpp b/netfs/lib/entries.cpp
new file mode 100644
index 0000000..bde15e6
--- /dev/null
+++ b/netfs/lib/entries.cpp
@@ -0,0 +1,16 @@
+#include "entries.h"
+
+static_assert(std::is_nothrow_move_constructible_v<User>);
+static_assert(std::is_nothrow_move_assignable_v<User>);
+static_assert(std::is_nothrow_move_constructible_v<Group>);
+static_assert(std::is_nothrow_move_assignable_v<Group>);
+
+User::User(uid_t u, std::string n, gid_t g) : id(u), name(std::move(n)), group(g) { }
+
+Group::Group(gid_t g, std::string n) : id(g), name(std::move(n)) { }
+
+bool
+Group::hasMember(uid_t u) const noexcept
+{
+ return (members.find(u) != members.end());
+}
diff --git a/netfs/lib/entries.h b/netfs/lib/entries.h
new file mode 100644
index 0000000..7da8b99
--- /dev/null
+++ b/netfs/lib/entries.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <set>
+#include <string>
+#include <unistd.h>
+
+class User {
+public:
+ User(uid_t, std::string, gid_t);
+ uid_t id;
+ std::string name;
+ gid_t group;
+};
+
+class Group {
+public:
+ Group(gid_t, std::string);
+
+ bool hasMember(uid_t) const noexcept;
+
+ gid_t id;
+ std::string name;
+ std::set<uid_t> members;
+};
diff --git a/netfs/lib/entryResolver.h b/netfs/lib/entryResolver.h
new file mode 100644
index 0000000..bb5baa3
--- /dev/null
+++ b/netfs/lib/entryResolver.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <memory>
+
+template<typename entry_t> class EntryResolver {
+public:
+ virtual ~EntryResolver() noexcept = default;
+ using EntryPtr = std::shared_ptr<entry_t>;
+
+ virtual EntryPtr getEntry(const decltype(entry_t::id) &) const noexcept = 0;
+ virtual EntryPtr getEntry(const decltype(entry_t::name) &) const noexcept = 0;
+};
+
+template<typename entry_t> using EntryResolverPtr = std::shared_ptr<EntryResolver<entry_t>>;
diff --git a/netfs/unittests/Jamfile.jam b/netfs/unittests/Jamfile.jam
index a5dfbdf..53d600f 100644
--- a/netfs/unittests/Jamfile.jam
+++ b/netfs/unittests/Jamfile.jam
@@ -1,6 +1,18 @@
-import testing ;
-
lib boost_utf : : <name>boost_unit_test_framework ;
+lib benchmark ;
+
+path-constant leak-san-suppressions : leak-suppressions.txt ;
+path-constant thread-san-suppressions : thread-suppressions.txt ;
+
+project
+ : requirements
+ <toolset>tidy:<xcheckxx>misc-non-private-member-variables-in-classes
+ <toolset>tidy:<xcheckxx>hicpp-special-member-functions
+ <toolset>tidy:<xcheckxx>hicpp-explicit-conversions
+ <toolset>tidy:<xcheckxx>hicpp-member-init
+ <leak-sanitizer>on:<testing.launcher>LSAN_OPTIONS=suppressions=$(leak-san-suppressions)
+ <thread-sanitizer>on:<testing.launcher>TSAN_OPTIONS=suppressions=$(thread-san-suppressions)
+ ;
path-constant me : . ;
@@ -13,7 +25,7 @@ lib testMocks :
<library>../fuse//netfs-client
<library>../ice//netfs-api
<library>..//adhocutil
- <define>BOOST_TEST_DYN_LINK
+ <define>BOOST_TEST_NO_MAIN
<library>boost_utf
<define>ROOT=\"$(me)\"
: :
@@ -26,33 +38,72 @@ lib testMocks :
run testCore.cpp
- : : :
- <dependency>defaultDaemon.xml
- <dependency>defaultFuse.xml
- <dependency>altDaemon.xml
- <dependency>altFuse.xml
- <dependency>secureDaemon.xml
- <dependency>secureFuse.xml
+ : -- :
+ altDaemon.xml
+ defaultDaemon.xml
+ defaultFuse.xml
+ secureDaemon.xml
+ secureFuse.xml
+ :
<define>BOOST_TEST_DYN_LINK
<library>boost_utf
<library>testMocks
: testCore ;
run testGlacier.cpp
- : : :
- <dependency>defaultDaemon.xml
- <dependency>defaultFuse.xml
+ : -- :
+ defaultDaemon.xml
+ defaultFuse.xml
+ :
<define>BOOST_TEST_DYN_LINK
<library>boost_utf
<library>testMocks
: testGlacier ;
run testEdgeCases.cpp
- : --log_level=all : :
- <dependency>defaultDaemon.xml
- <dependency>defaultFuse.xml
+ : -- :
+ defaultDaemon.xml
+ defaultFuse.xml
+ :
<define>BOOST_TEST_DYN_LINK
<library>boost_utf
<library>testMocks
: testEdgeCases ;
+run testLib.cpp
+ : -- :
+ defaultDaemon.xml
+ defaultFuse.xml
+ :
+ <define>BOOST_TEST_DYN_LINK
+ <library>boost_utf
+ <library>testMocks
+ ;
+
+run testFuse.cpp
+ : -- :
+ defaultDaemon.xml
+ defaultFuseHide.xml
+ defaultFuseMask.xml
+ :
+ <define>BOOST_TEST_DYN_LINK
+ <library>boost_utf
+ <library>testMocks
+ <dependency>testCore
+ ;
+
+run testDaemon.cpp ../daemon//modeCheck
+ : -- : :
+ <define>BOOST_TEST_DYN_LINK
+ <library>boost_utf
+ <library>testMocks
+ <dependency>testCore
+ ;
+
+explicit testPerf ;
+run testPerf.cpp
+ : : :
+ <library>benchmark
+ <library>testMocks
+ ;
+
diff --git a/netfs/unittests/altFuse.xml b/netfs/unittests/altFuse.xml
deleted file mode 100644
index 22b791a..0000000
--- a/netfs/unittests/altFuse.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="ascii"?>
-<config>
- <resources>
- <resource>
- <name>testvol</name>
- <resource>
- <export>testvol</export>
- <listdir>false</listdir>
- <endpoints>
- <endpoint>overridden</endpoint>
- </endpoints>
- </resource>
- </resource>
- </resources>
-</config>
-
diff --git a/netfs/unittests/defaultFuseHide.xml b/netfs/unittests/defaultFuseHide.xml
new file mode 100644
index 0000000..098a597
--- /dev/null
+++ b/netfs/unittests/defaultFuseHide.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="ascii"?>
+<config>
+ <resources>
+ <resource>
+ <name>testvol</name>
+ <resource>
+ <export>testvol</export>
+ <endpoints>
+ <endpoint>overridden</endpoint>
+ </endpoints>
+ <mapper type="hide"/>
+ </resource>
+ </resource>
+ </resources>
+</config>
diff --git a/netfs/unittests/defaultFuseMask.xml b/netfs/unittests/defaultFuseMask.xml
new file mode 100644
index 0000000..5d45a53
--- /dev/null
+++ b/netfs/unittests/defaultFuseMask.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="ascii"?>
+<config>
+ <resources>
+ <resource>
+ <name>testvol</name>
+ <resource>
+ <export>testvol</export>
+ <endpoints>
+ <endpoint>overridden</endpoint>
+ </endpoints>
+ <mapper type="mask">
+ <unknownuser>uu</unknownuser>
+ <unknowngroup>ug</unknowngroup>
+ <usermask>0100</usermask>
+ <groupmask>0010</groupmask>
+ </mapper>
+ </resource>
+ </resource>
+ </resources>
+</config>
diff --git a/netfs/unittests/defaultFuseNoAsync.xml b/netfs/unittests/defaultFuseNoAsync.xml
new file mode 100644
index 0000000..5350180
--- /dev/null
+++ b/netfs/unittests/defaultFuseNoAsync.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="ascii"?>
+<config>
+ <resources>
+ <resource>
+ <name>testvol</name>
+ <resource>
+ <export>testvol</export>
+ <async>false</async>
+ <endpoints>
+ <endpoint>overridden</endpoint>
+ </endpoints>
+ </resource>
+ </resource>
+ </resources>
+</config>
diff --git a/netfs/unittests/leak-suppressions.txt b/netfs/unittests/leak-suppressions.txt
new file mode 100644
index 0000000..42eae90
--- /dev/null
+++ b/netfs/unittests/leak-suppressions.txt
@@ -0,0 +1,3 @@
+# OpenLDAP leaks when calling getpwent/getgrent
+leak:ber_memalloc_x
+leak:ber_memcalloc_x
diff --git a/netfs/unittests/mockDaemon.cpp b/netfs/unittests/mockDaemon.cpp
index 14b843f..dd8d3fd 100644
--- a/netfs/unittests/mockDaemon.cpp
+++ b/netfs/unittests/mockDaemon.cpp
@@ -1,31 +1,25 @@
#include "mockDaemon.h"
-#include <definedDirs.h>
#include <buffer.h>
+#include <definedDirs.h>
-const std::filesystem::path TestExportRoot(binDir /
- UniqueExport::get(getpid()));
+const std::filesystem::path MockDaemonHost::TestExportRoot {
+ getenv("XDG_RUNTIME_DIR") / selfExe.filename() / UniqueExport::get(getpid())};
-MockDaemon::MockDaemon(const std::string & ep) :
- NetFSDaemon(),
- testEndpoint(ep)
-{
-}
+MockDaemon::MockDaemon(std::string ep) : NetFSDaemon(), testEndpoint(std::move(ep)) { }
NetFS::Daemon::ConfigurationPtr
MockDaemon::ReadConfiguration(const std::filesystem::path & path) const
{
auto c = NetFSDaemon::ReadConfiguration(path);
- for(auto e : c->Exports) {
- e.second->RootPath = TestExportRoot.string();
+ for (const auto & e : c->Exports) {
+ e.second->RootPath = MockDaemonHost::TestExportRoot.string();
}
- c->Hosts.insert({ "unittest", std::make_shared<NetFS::Daemon::Host>(testEndpoint) });
+ c->Hosts.insert({"unittest", std::make_shared<NetFS::Daemon::Host>(testEndpoint)});
return c;
}
-MockDaemonHost::MockDaemonHost(const std::string & ep, const Ice::StringSeq & ps) :
- testEndpoint(ep),
- params(ps),
- daemon(nullptr)
+MockDaemonHost::MockDaemonHost(std::string ep, Ice::StringSeq ps) :
+ testEndpoint(std::move(ep)), params(std::move(ps)), daemon(nullptr)
{
std::filesystem::remove_all(TestExportRoot);
std::filesystem::create_directories(TestExportRoot);
@@ -43,7 +37,6 @@ MockDaemonHost::stop()
{
if (daemon) {
daemon->stop();
- delete daemon;
}
if (ic) {
ic->destroy();
@@ -58,7 +51,7 @@ MockDaemonHost::start()
id.properties->parseCommandLineOptions("", params);
ic = Ice::initialize(id);
ic->getProperties()->setProperty("NetFSD.HostNameOverride", "unittest");
- daemon = new MockDaemon(testEndpoint);
+ daemon = std::make_unique<MockDaemon>(testEndpoint);
daemon->start("NetFSDaemonAdapter", ic, {});
}
@@ -68,4 +61,3 @@ MockDaemonHost::restart()
stop();
start();
}
-
diff --git a/netfs/unittests/mockDaemon.h b/netfs/unittests/mockDaemon.h
index dd0a0ad..ab33c72 100644
--- a/netfs/unittests/mockDaemon.h
+++ b/netfs/unittests/mockDaemon.h
@@ -1,39 +1,36 @@
-#ifndef MOCKDAEMON_H
-#define MOCKDAEMON_H
+#pragma once
+#include <compileTimeFormatter.h>
#include <daemon.h>
#include <visibility.h>
-#include <compileTimeFormatter.h>
AdHocFormatter(UniqueExport, "testExport-%?");
class DLL_PUBLIC MockDaemon : public NetFSDaemon {
- public:
- MockDaemon(const std::string & ep);
+public:
+ explicit MockDaemon(std::string ep);
- const std::string testEndpoint;
+ const std::string testEndpoint;
- protected:
- virtual NetFS::Daemon::ConfigurationPtr ReadConfiguration(const std::filesystem::path &) const override;
+protected:
+ NetFS::Daemon::ConfigurationPtr ReadConfiguration(const std::filesystem::path &) const override;
};
class DLL_PUBLIC MockDaemonHost {
- public:
- MockDaemonHost(const std::string & ep, const Ice::StringSeq & ps = {});
- ~MockDaemonHost();
+public:
+ explicit MockDaemonHost(std::string ep, Ice::StringSeq ps = {});
+ ~MockDaemonHost();
- void restart();
+ void restart();
- Ice::CommunicatorPtr ic;
+ Ice::CommunicatorPtr ic;
+ static const std::filesystem::path TestExportRoot;
- private:
- void start();
- void stop();
+private:
+ void start();
+ void stop();
- const std::string testEndpoint;
- Ice::StringSeq params;
- NetFSDaemon * daemon;
+ const std::string testEndpoint;
+ Ice::StringSeq params;
+ std::unique_ptr<NetFSDaemon> daemon;
};
-
-#endif
-
diff --git a/netfs/unittests/mockFuse.cpp b/netfs/unittests/mockFuse.cpp
index d7b66a6..36a5aed 100644
--- a/netfs/unittests/mockFuse.cpp
+++ b/netfs/unittests/mockFuse.cpp
@@ -1,9 +1,10 @@
#include "mockFuse.h"
#include <boost/test/test_tools.hpp>
+#include <buffer.h>
+#include <random>
-FuseMock::FuseMock(const std::string & ep, const Ice::StringSeq & a) :
- NetFS::FuseApp(a),
- testEndpoint(ep)
+FuseMock::FuseMock(std::string ep, Ice::StringSeq a) :
+ NetFS::FuseApp(std::move(a)), testEndpoint(std::move(ep)), context({})
{
::memset(&context, 0, sizeof(fuse_context));
context.pid = getpid();
@@ -12,59 +13,57 @@ FuseMock::FuseMock(const std::string & ep, const Ice::StringSeq & a) :
}
struct fuse_context *
-FuseMock::fuse_get_context()
+FuseMock::fuseGetContext()
{
return &context;
}
-int
-FuseMock::fuse_opt_parse(struct fuse_args * args, void * data, const struct fuse_opt [], fuse_opt_proc_t proc)
+void
+FuseMock::connectToService()
{
- for (int n = 0; n < args->argc; n += 1) {
- proc(data, args->argv[n], n, args);
+ if (testEndpoint.empty()) {
+ return FuseApp::connectToService();
}
- return 0;
-}
-
-int
-FuseMock::main(int, char **, const struct fuse_operations * o)
-{
- o->init(NULL);
- ops = *o;
- return 0;
-}
-NetFS::Client::ConfigurationPtr
-FuseMock::ReadConfiguration(const std::filesystem::path & path) const
-{
- auto c = FuseApp::ReadConfiguration(path);
- for(auto r : c->Resources) {
- for(auto & e : r.second->Endpoints) {
- e = testEndpoint;
+ if (!service) {
+ Lock(mutex);
+ std::string addr = fcr->ServiceIdentity + ":" + testEndpoint;
+ service = Ice::checkedCast<NetFS::ServicePrx>(ic->stringToProxy(addr));
+ if (!service) {
+ throw std::runtime_error("Invalid service proxy: " + testEndpoint);
}
+ daemonSettings = service->getSettings();
+ combinedSettings = combineSettings(*daemonSettings, clientSettings);
}
- return c;
}
-void
-FuseMock::vlogf(int, const char * fmt, va_list args) const throw()
+std::vector<char>
+FuseMock::genRandomData(size_t size)
{
- BOOST_TEST_MESSAGE(vstringf(fmt, args));
+ std::vector<char> buf(size);
+ std::generate(begin(buf), end(buf),
+ [dist = std::uniform_int_distribution<char>(
+ std::numeric_limits<char>::min(), std::numeric_limits<char>::max()),
+ mersenne_engine = std::mt19937(std::random_device()())]() mutable {
+ return dist(mersenne_engine);
+ });
+ return buf;
}
-FuseMockHost::FuseMockHost(const std::string & ep, const Ice::StringSeq & a) :
- app(new FuseMock(ep, a)),
- fuse(&app->ops)
+void
+FuseMock::vlogf(int, const char * fmt, va_list args) const noexcept
{
- std::vector<char *> argv;
- for (auto & arg : a) {
- argv.push_back(const_cast<char *>(arg.c_str()));
+ static std::mutex btm;
+
+ ScopeLock(btm) {
+ BOOST_TEST_MESSAGE(vstringf(fmt, args));
}
- FuseAppBase::run(a.size(), &argv.front(), app);
}
-FuseMockHost::~FuseMockHost()
+FuseMockHost::FuseMockHost(std::string ep, const Ice::StringSeq & a) :
+ app(std::make_unique<FuseMock>(std::move(ep), a)), fuse(&app->OPERATIONS)
{
- delete app;
+ if (app->OPERATIONS.init) {
+ app->OPERATIONS.init(nullptr, nullptr);
+ }
}
-
diff --git a/netfs/unittests/mockFuse.h b/netfs/unittests/mockFuse.h
index 9b3d2e0..cd15cad 100644
--- a/netfs/unittests/mockFuse.h
+++ b/netfs/unittests/mockFuse.h
@@ -1,40 +1,31 @@
-#ifndef MOCKFUSE_H
-#define MOCKFUSE_H
+#pragma once
#include <fuseApp.h>
+#include <vector>
#include <visibility.h>
class DLL_PUBLIC FuseMock : public NetFS::FuseApp {
- public:
- FuseMock(const std::string &, const Ice::StringSeq &);
+public:
+ FuseMock(std::string, Ice::StringSeq);
- struct fuse_context * fuse_get_context() override;
- int fuse_opt_parse(struct fuse_args * args, void * data, const struct fuse_opt [], fuse_opt_proc_t proc) override;
- int main(int, char **, const struct fuse_operations * o) override;
- void vlogf(int, const char *, va_list) const throw() override;
+ struct fuse_context * fuseGetContext() override;
+ void vlogf(int, const char *, va_list) const throw() override;
- fuse_operations ops;
+ void connectToService() override;
+ [[nodiscard]] static std::vector<char> genRandomData(size_t);
- protected:
- virtual NetFS::Client::ConfigurationPtr ReadConfiguration(const std::filesystem::path &) const override;
-
- private:
- const std::string testEndpoint;
- fuse_context context;
+private:
+ const std::string testEndpoint;
+ fuse_context context;
};
class DLL_PUBLIC FuseMockHost {
- public:
- FuseMockHost(const std::string &, const Ice::StringSeq &);
- ~FuseMockHost();
+public:
+ FuseMockHost(std::string, const Ice::StringSeq &);
- private:
- FuseMock * app;
+private:
+ std::unique_ptr<FuseMock> app;
- public:
- const fuse_operations * fuse;
+public:
+ const fuse_operations * fuse;
};
-
-
-#endif
-
diff --git a/netfs/unittests/mockGlacier.cpp b/netfs/unittests/mockGlacier.cpp
new file mode 100644
index 0000000..f1e8e72
--- /dev/null
+++ b/netfs/unittests/mockGlacier.cpp
@@ -0,0 +1,29 @@
+#include "mockGlacier.h"
+#include <boost/test/test_tools.hpp>
+#include <csignal>
+#include <sys/file.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+
+Glacier::Glacier() : lockdir(open("/tmp", O_DIRECTORY))
+{
+ BOOST_REQUIRE_GE(lockdir, 0);
+ BOOST_REQUIRE_EQUAL(0, flock(lockdir, LOCK_EX));
+ if (glacier = fork(); glacier == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGINT);
+ execl("/usr/bin/glacier2router", "--Glacier2.Client.Endpoints=tcp -p 14063",
+ "--Glacier2.PermissionsVerifier=Glacier2/NullPermissionsVerifier", nullptr);
+ BOOST_FAIL("Should never get here");
+ }
+ BOOST_REQUIRE_GT(glacier, 0);
+ sleep(1);
+}
+
+Glacier::~Glacier()
+{
+ BOOST_CHECK_EQUAL(0, kill(glacier, SIGINT));
+ int status {};
+ BOOST_CHECK_EQUAL(glacier, waitpid(glacier, &status, 0));
+ BOOST_CHECK_EQUAL(0, status);
+ BOOST_CHECK_EQUAL(0, close(lockdir));
+}
diff --git a/netfs/unittests/mockGlacier.h b/netfs/unittests/mockGlacier.h
new file mode 100644
index 0000000..61adcee
--- /dev/null
+++ b/netfs/unittests/mockGlacier.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <c++11Helpers.h>
+#include <sys/types.h>
+#include <visibility.h>
+
+class DLL_PUBLIC Glacier {
+public:
+ Glacier();
+ ~Glacier();
+ SPECIAL_MEMBERS_DELETE(Glacier);
+
+private:
+ const int lockdir;
+ pid_t glacier;
+};
diff --git a/netfs/unittests/mockMount.cpp b/netfs/unittests/mockMount.cpp
new file mode 100644
index 0000000..81ba0d2
--- /dev/null
+++ b/netfs/unittests/mockMount.cpp
@@ -0,0 +1,76 @@
+#include "mockMount.h"
+#include <boost/test/included/unit_test.hpp>
+#include <filesystem>
+
+const std::string testEndpoint("tcp -h localhost -p 12015");
+const std::string testUri("tcp://localhost:12015/testvol");
+
+MockFuseApp::MockFuseApp() : NetFS::FuseApp({::testUri}), fargs {}
+{
+ ::fuse_opt_add_arg(&fargs, selfExe.c_str());
+ ::fuse_opt_add_arg(&fargs, "-onoauto_cache");
+ ::fuse_opt_add_arg(&fargs, "-oattr_timeout=0");
+ ::fuse_opt_add_arg(&fargs, "-oac_attr_timeout=0");
+ fs = ::fuse_new(&fargs, &OPERATIONS, sizeof(fuse_operations), this);
+ BOOST_REQUIRE(fs);
+}
+
+FuseMountPoint::FuseMountPoint() :
+ MockDaemonHost(::testEndpoint, {"--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string()})
+{
+ std::filesystem::remove_all(mntpnt);
+ std::filesystem::create_directory(mntpnt);
+ BOOST_REQUIRE_EQUAL(0, ::fuse_mount(fuseApp.fs, mntpnt.c_str()));
+ th.emplace(
+ [](auto fs) {
+ ::fuse_set_signal_handlers(fuse_get_session(fs));
+ ::fuse_loop(fs);
+ },
+ fuseApp.fs);
+ BOOST_REQUIRE(th);
+}
+
+FuseMountPoint::~FuseMountPoint()
+{
+ ::fuse_exit(fuseApp.fs);
+ pthread_sigqueue(th->native_handle(), SIGINT, {});
+ th->join();
+ ::fuse_unmount(fuseApp.fs);
+ std::filesystem::remove(mntpnt);
+}
+
+MockFuseApp::~MockFuseApp()
+{
+ ::fuse_destroy(fs);
+ ::fuse_opt_free_args(&fargs);
+}
+
+struct fuse_context *
+MockFuseApp::fuseGetContext()
+{
+ return ::fuse_get_context();
+}
+
+char *
+MockFuseApp::vstrdupf(const char * fmt, va_list args)
+{
+ char * out {};
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+ // NOLINTNEXTLINE(clang-diagnostic-format-nonliteral)
+ BOOST_REQUIRE_GE(vasprintf(&out, fmt, args), 0);
+#pragma GCC diagnostic push
+ BOOST_REQUIRE(out);
+ return out;
+}
+
+void
+MockFuseApp::vlogf(int, const char * fmt, va_list args) const noexcept
+{
+ std::unique_ptr<char, void (*)(void *)> msg(vstrdupf(fmt, args), std::free);
+ static std::mutex btm;
+
+ ScopeLock(btm) {
+ BOOST_TEST_MESSAGE(msg.get());
+ }
+}
diff --git a/netfs/unittests/mockMount.h b/netfs/unittests/mockMount.h
new file mode 100644
index 0000000..2fec87a
--- /dev/null
+++ b/netfs/unittests/mockMount.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include "mockDaemon.h"
+#include <definedDirs.h>
+#include <fuseApp.h>
+#include <memory>
+#include <thread>
+#include <visibility.h>
+
+inline const std::filesystem::path mntpnt {binDir / "mnt"};
+
+class DLL_PUBLIC MockFuseApp : public NetFS::FuseApp {
+public:
+ MockFuseApp();
+ ~MockFuseApp() override;
+ SPECIAL_MEMBERS_DELETE(MockFuseApp);
+
+ struct fuse_context * fuseGetContext() override;
+
+ void vlogf(int, const char * fmt, va_list args) const noexcept override;
+
+ const auto &
+ getStatCache() const
+ {
+ return statCache;
+ }
+
+private:
+ friend class FuseMountPoint;
+ static char * vstrdupf(const char * fmt, va_list args);
+ struct fuse * fs;
+ struct fuse_args fargs;
+};
+
+class DLL_PUBLIC FuseMountPoint : public MockDaemonHost {
+public:
+ FuseMountPoint();
+ ~FuseMountPoint();
+ SPECIAL_MEMBERS_DELETE(FuseMountPoint);
+
+ MockFuseApp fuseApp;
+
+private:
+ std::optional<std::thread> th;
+};
diff --git a/netfs/unittests/testCore.cpp b/netfs/unittests/testCore.cpp
index b56b3d5..6f4511f 100644
--- a/netfs/unittests/testCore.cpp
+++ b/netfs/unittests/testCore.cpp
@@ -1,20 +1,20 @@
#define BOOST_TEST_MODULE TestNetFSCore
#include <boost/test/unit_test.hpp>
-#define private public
+
#include "mockDaemon.h"
#include "mockFuse.h"
+#include <algorithm>
+#include <climits>
#include <definedDirs.h>
#include <filesystem>
-#include <ostream>
-#include <algorithm>
#include <fuseFiles.h>
+#include <ostream>
+const std::filesystem::path tmpDir {getenv("XDG_RUNTIME_DIR") / selfExe.filename()};
const auto testExport = UniqueExport::get(getpid());
const std::string testEndpoint("tcp -h localhost -p 12012");
const std::string testUri("tcp://localhost:12012/testvol");
-BOOST_TEST_DONT_PRINT_LOG_VALUE(NetFS::FuseApp::OpenFile::BGs::iterator);
-
bool
operator==(const struct stat & a, const struct stat & b)
{
@@ -22,73 +22,85 @@ operator==(const struct stat & a, const struct stat & b)
}
namespace std {
+ template<size_t l>
+ bool
+ operator==(const std::array<char, l> & a, const char * b)
+ {
+ return strncmp(a.data(), b, l) == 0;
+ }
+
AdHocFormatter(StatDebug, "dev: %? inode: %?");
+
// LCOV_EXCL_START
- ostream & operator<<(ostream & s, const struct stat & ss)
+ template<typename T, size_t l>
+ ostream &
+ operator<<(ostream & s, const std::array<T, l> & a)
+ {
+ std::copy(a.begin(), a.end(), std::ostream_iterator<T>(s));
+ return s;
+ }
+
+ ostream &
+ operator<<(ostream & s, const struct stat & ss)
{
return StatDebug::write(s, ss.st_dev, ss.st_ino);
}
+
// LCOV_EXCL_STOP
}
class Core {
- public:
- Core(const std::string & daemonConfig = "defaultDaemon.xml", const std::string & fuseConfig = "defaultFuse.xml") :
- daemonHost(testEndpoint, {
- "--Ice.ThreadPool.Client.Size=4",
- "--Ice.ThreadPool.Client.SizeMax=20",
- "--Ice.ThreadPool.Server.Size=5",
- "--Ice.ThreadPool.Server.SizeMax=20",
- "--NetFSD.ConfigPath=" + (rootDir / daemonConfig).string()
- }),
- fuseHost(testEndpoint, {
- "--Ice.ThreadPool.Client.Size=6",
- "--Ice.ThreadPool.Client.SizeMax=20",
- (rootDir / fuseConfig).string() + ":testvol",
- (binDir / "test").string()
- }),
- ic(daemonHost.ic),
- fuse(fuseHost.fuse)
- {
- }
-
- static
- int
- nameListAdd(void *buf, const char *name, const struct stat *, off_t)
- {
- static_cast<NetFS::NameList *>(buf)->push_back(name);
- return 0;
- }
+public:
+ Core(const std::string & daemonConfig = "defaultDaemon.xml", const std::string & fuseConfig = "defaultFuse.xml") :
+ daemonHost(testEndpoint,
+ {"--Ice.ThreadPool.Client.Size=4", "--Ice.ThreadPool.Client.SizeMax=20",
+ "--Ice.ThreadPool.Server.Size=5", "--Ice.ThreadPool.Server.SizeMax=20",
+ "--NetFSD.ConfigPath=" + (rootDir / daemonConfig).string()}),
+ fuseHost(testEndpoint,
+ {"--Ice.ThreadPool.Client.Size=6", "--Ice.ThreadPool.Client.SizeMax=20",
+ (rootDir / fuseConfig).string() + ":testvol", (tmpDir / "test").string()}),
+ ic(daemonHost.ic), fuse(fuseHost.fuse)
+ {
+ }
+ static int
+ nameListAdd(void * buf, const char * name, const struct stat *, off_t, fuse_fill_dir_flags)
+ {
+ static_cast<NetFS::NameList *>(buf)->push_back(name);
+ return 0;
+ }
- protected:
- MockDaemonHost daemonHost;
- FuseMockHost fuseHost;
+protected:
+ MockDaemonHost daemonHost;
+ FuseMockHost fuseHost;
- Ice::CommunicatorPtr ic;
+ Ice::CommunicatorPtr ic;
- public:
- const fuse_operations * fuse;
+public:
+ const fuse_operations * fuse;
};
class AltCore : public Core {
- public:
- AltCore() : Core("altDaemon.xml", "altFuse.xml") { }
+public:
+ AltCore() : Core("altDaemon.xml", "defaultFuse.xml") { }
};
-BOOST_FIXTURE_TEST_SUITE( NetfsCore, Core );
+BOOST_FIXTURE_TEST_SUITE(NetfsCore, Core);
BOOST_AUTO_TEST_CASE(fuse_operations_correct)
{
+ BOOST_CHECK(NetFS::FuseApp::OPERATIONS.access);
+ BOOST_CHECK(!NetFS::FuseApp::OPERATIONS.destroy);
+
// open should be defined
BOOST_REQUIRE(fuse->open);
- // getdir should not be defined (Not supported)
- BOOST_REQUIRE(!fuse->getdir);
+ // bmap should not be defined (Not supported)
+ BOOST_REQUIRE(!fuse->bmap);
// fsync should not be defined (Not implemented)
BOOST_REQUIRE(!fuse->fsync);
}
-BOOST_AUTO_TEST_CASE ( daemonInitialised )
+BOOST_AUTO_TEST_CASE(daemonInitialised)
{
auto service = Ice::checkedCast<NetFS::ServicePrx>(ic->stringToProxy("Service"));
BOOST_REQUIRE(service);
@@ -99,123 +111,124 @@ BOOST_AUTO_TEST_CASE ( daemonInitialised )
volume->disconnect();
}
-BOOST_AUTO_TEST_CASE ( clientInitialised )
+BOOST_AUTO_TEST_CASE(daemonSettings)
+{
+ auto service = Ice::checkedCast<NetFS::ServicePrx>(ic->stringToProxy("Service"));
+ auto settings = service->getSettings();
+ BOOST_REQUIRE(settings);
+ BOOST_REQUIRE(settings->MessageSizeMax);
+ BOOST_CHECK_EQUAL(1024, *settings->MessageSizeMax);
+}
+
+BOOST_AUTO_TEST_CASE(clientInitialised)
{
struct statvfs s;
- BOOST_REQUIRE_EQUAL(0, fuse->statfs("/", &s));
- BOOST_REQUIRE_EQUAL(-ENOENT, fuse->statfs("/missing", &s));
+ BOOST_REQUIRE_EQUAL(0, fuse->statfs("/", &s));
+ BOOST_REQUIRE_EQUAL(s.f_fsid, 0);
+ BOOST_REQUIRE_EQUAL(-ENOENT, fuse->statfs("/missing", &s));
}
-BOOST_AUTO_TEST_CASE( testNavigation )
+BOOST_AUTO_TEST_CASE(testNavigation)
{
- struct stat attr, rootattr, insideattr;
- memset(&attr, 0, sizeof(attr));
- memset(&rootattr, 0, sizeof(rootattr));
- memset(&insideattr, 0, sizeof(insideattr));
- struct fuse_file_info fi;
- memset(&fi, 0, sizeof(fi));
+ struct stat attr {}, rootattr {}, insideattr {};
+ struct fuse_file_info fi {};
int fd = fuse->create("/inside", 0666, &fi);
BOOST_REQUIRE(fd >= 0);
fuse->release("/inside", &fi);
- BOOST_REQUIRE(std::filesystem::exists(binDir / testExport / "inside"));
- BOOST_REQUIRE_EQUAL(lstat((binDir / testExport).c_str(), &rootattr), 0);
- BOOST_REQUIRE_EQUAL(lstat((binDir / testExport / "inside").c_str(), &insideattr), 0);
- BOOST_REQUIRE_EQUAL(0, fuse->getattr("/", &attr));
+ BOOST_REQUIRE(std::filesystem::exists(tmpDir / testExport / "inside"));
+ BOOST_REQUIRE_EQUAL(lstat((tmpDir / testExport).c_str(), &rootattr), 0);
+ BOOST_REQUIRE_EQUAL(lstat((tmpDir / testExport / "inside").c_str(), &insideattr), 0);
+ BOOST_REQUIRE_EQUAL(0, fuse->getattr("/", &attr, nullptr));
BOOST_REQUIRE_EQUAL(attr, rootattr);
- BOOST_REQUIRE_EQUAL(0, fuse->getattr(".", &attr));
+ BOOST_REQUIRE_EQUAL(0, fuse->getattr(".", &attr, nullptr));
BOOST_REQUIRE_EQUAL(attr, rootattr);
- BOOST_REQUIRE_EQUAL(0, fuse->getattr("", &attr));
+ BOOST_REQUIRE_EQUAL(0, fuse->getattr("", &attr, nullptr));
BOOST_REQUIRE_EQUAL(attr, rootattr);
- BOOST_REQUIRE_EQUAL(0, fuse->getattr("/.", &attr));
+ BOOST_REQUIRE_EQUAL(0, fuse->getattr("/.", &attr, nullptr));
BOOST_REQUIRE_EQUAL(attr, rootattr);
- BOOST_REQUIRE_EQUAL(0, fuse->getattr("/inside/..", &attr));
+ BOOST_REQUIRE_EQUAL(0, fuse->getattr("/inside/..", &attr, nullptr));
BOOST_REQUIRE_EQUAL(attr, rootattr);
- BOOST_REQUIRE_EQUAL(0, fuse->getattr("//inside/./..", &attr));
+ BOOST_REQUIRE_EQUAL(0, fuse->getattr("//inside/./..", &attr, nullptr));
BOOST_REQUIRE_EQUAL(attr, rootattr);
- BOOST_REQUIRE_EQUAL(0, fuse->getattr("/inside", &attr));
+ BOOST_REQUIRE_EQUAL(0, fuse->getattr("/inside", &attr, nullptr));
BOOST_REQUIRE_EQUAL(attr, insideattr);
- BOOST_REQUIRE_EQUAL(0, fuse->getattr("/inside/", &attr));
+ BOOST_REQUIRE_EQUAL(0, fuse->getattr("/inside/", &attr, nullptr));
BOOST_REQUIRE_EQUAL(attr, insideattr);
- BOOST_REQUIRE_EQUAL(0, fuse->getattr("/anything/../inside", &attr));
+ BOOST_REQUIRE_EQUAL(0, fuse->getattr("/anything/../inside", &attr, nullptr));
BOOST_REQUIRE_EQUAL(attr, insideattr);
- BOOST_REQUIRE_EQUAL(0, fuse->getattr("/inside/../inside", &attr));
+ BOOST_REQUIRE_EQUAL(0, fuse->getattr("/inside/../inside", &attr, nullptr));
BOOST_REQUIRE_EQUAL(attr, insideattr);
}
-BOOST_AUTO_TEST_CASE( testSandboxing )
+BOOST_AUTO_TEST_CASE(testSandboxing)
{
// A previous (bad) run might create one or more of these:
- std::filesystem::remove(binDir / "outside");
- std::filesystem::remove(binDir / "sub" / "outside");
- std::filesystem::remove(binDir / "sub");
- struct fuse_file_info fi;
- memset(&fi, 0, sizeof(fi));
+ std::filesystem::remove(tmpDir / "outside");
+ std::filesystem::remove(tmpDir / "sub" / "outside");
+ std::filesystem::remove(tmpDir / "sub");
+ struct fuse_file_info fi {};
BOOST_REQUIRE_EQUAL(fuse->create("../outside", 0666, &fi), -EPERM);
- BOOST_REQUIRE(!std::filesystem::exists(binDir / "outside"));
+ BOOST_REQUIRE(!std::filesystem::exists(tmpDir / "outside"));
BOOST_REQUIRE_EQUAL(fuse->create("/../outside", 0666, &fi), -EPERM);
- BOOST_REQUIRE(!std::filesystem::exists(binDir / "outside"));
+ BOOST_REQUIRE(!std::filesystem::exists(tmpDir / "outside"));
BOOST_REQUIRE_EQUAL(fuse->create("../sub/outside", 0666, &fi), -EPERM);
- BOOST_REQUIRE(!std::filesystem::exists(binDir / "sub" / "outside"));
+ BOOST_REQUIRE(!std::filesystem::exists(tmpDir / "sub" / "outside"));
BOOST_REQUIRE_EQUAL(fuse->create("/../sub/outside", 0666, &fi), -EPERM);
- BOOST_REQUIRE(!std::filesystem::exists(binDir / "sub" / "outside"));
+ BOOST_REQUIRE(!std::filesystem::exists(tmpDir / "sub" / "outside"));
BOOST_REQUIRE_EQUAL(fuse->create("../sub/../outside", 0666, &fi), -EPERM);
- BOOST_REQUIRE(!std::filesystem::exists(binDir / "outside"));
+ BOOST_REQUIRE(!std::filesystem::exists(tmpDir / "outside"));
BOOST_REQUIRE_EQUAL(fuse->create("/../sub/../outside", 0666, &fi), -EPERM);
- BOOST_REQUIRE(!std::filesystem::exists(binDir / "outside"));
+ BOOST_REQUIRE(!std::filesystem::exists(tmpDir / "outside"));
BOOST_REQUIRE_EQUAL(fuse->create("/inside", 0666, &fi), 0);
BOOST_REQUIRE_EQUAL(fuse->release("/inside", &fi), 0);
- BOOST_REQUIRE(std::filesystem::exists(binDir / testExport / "inside"));
+ BOOST_REQUIRE(std::filesystem::exists(tmpDir / testExport / "inside"));
BOOST_REQUIRE_EQUAL(fuse->create("inside2", 0666, &fi), 0);
BOOST_REQUIRE_EQUAL(fuse->release("inside2", &fi), 0);
- BOOST_REQUIRE(std::filesystem::exists(binDir / testExport / "inside2"));
+ BOOST_REQUIRE(std::filesystem::exists(tmpDir / testExport / "inside2"));
}
void
enableWriteOnDir(const fuse_operations * fuse, const char * dir)
{
- struct stat st;
- memset(&st, 0, sizeof(st));
- BOOST_REQUIRE_EQUAL(fuse->chmod(dir, 0700), 0);
- BOOST_REQUIRE_EQUAL(fuse->getattr(dir, &st), 0);
+ struct stat st {};
+ BOOST_REQUIRE_EQUAL(fuse->chmod(dir, 0700, nullptr), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr(dir, &st, nullptr), 0);
BOOST_REQUIRE_EQUAL(st.st_mode, 0700 | S_IFDIR);
}
void
disableWriteOnDir(const fuse_operations * fuse, const char * dir)
{
- struct stat st;
- memset(&st, 0, sizeof(st));
- BOOST_REQUIRE_EQUAL(fuse->chmod(dir, 0500), 0);
- BOOST_REQUIRE_EQUAL(fuse->getattr(dir, &st), 0);
+ struct stat st {};
+ BOOST_REQUIRE_EQUAL(fuse->chmod(dir, 0500, nullptr), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr(dir, &st, nullptr), 0);
BOOST_REQUIRE_EQUAL(st.st_mode, 0500 | S_IFDIR);
}
-BOOST_AUTO_TEST_CASE( directories )
+BOOST_AUTO_TEST_CASE(directories)
{
- struct fuse_file_info fi;
- memset(&fi, 0, sizeof(fi));
- struct stat st;
- memset(&st, 0, sizeof(st));
+ struct fuse_file_info fi {};
+ struct stat st {};
BOOST_REQUIRE_EQUAL(fuse->mkdir("/test", 0700), 0);
- BOOST_REQUIRE(std::filesystem::is_directory(binDir / testExport / "test"));
+ BOOST_REQUIRE(std::filesystem::is_directory(tmpDir / testExport / "test"));
BOOST_REQUIRE_EQUAL(fuse->mkdir("/test", 0700), -EEXIST);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/test", &st), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test", &st, nullptr), 0);
BOOST_REQUIRE_EQUAL(fuse->opendir("/test", &fi), 0);
NetFS::NameList nl;
- BOOST_REQUIRE_EQUAL(fuse->readdir("/test", &nl, &nameListAdd, 0, &fi), 0);
+ BOOST_REQUIRE_EQUAL(fuse->readdir("/test", &nl, &nameListAdd, 0, &fi, FUSE_READDIR_PLUS), 0);
BOOST_REQUIRE_EQUAL(nl.size(), 2);
std::sort(nl.begin(), nl.end());
BOOST_REQUIRE_EQUAL(nl[0], ".");
BOOST_REQUIRE_EQUAL(nl[1], "..");
- BOOST_REQUIRE_EQUAL(fuse->getattr("/test/.", &st), 0);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/test/..", &st), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test/.", &st, nullptr), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test/..", &st, nullptr), 0);
BOOST_REQUIRE_EQUAL(fuse->releasedir("/test", &fi), 0);
nl.clear();
BOOST_REQUIRE_EQUAL(fuse->mkdir("/test/sub", 0700), 0);
BOOST_REQUIRE_EQUAL(fuse->opendir("/test", &fi), 0);
- BOOST_REQUIRE_EQUAL(fuse->readdir("/test", &nl, &nameListAdd, 0, &fi), 0);
+ BOOST_REQUIRE_EQUAL(fuse->readdir("/test", &nl, &nameListAdd, 0, &fi, FUSE_READDIR_PLUS), 0);
BOOST_REQUIRE_EQUAL(nl.size(), 3);
std::sort(nl.begin(), nl.end());
BOOST_REQUIRE_EQUAL(nl[0], ".");
@@ -224,25 +237,22 @@ BOOST_AUTO_TEST_CASE( directories )
BOOST_REQUIRE_EQUAL(fuse->releasedir("/test", &fi), 0);
nl.clear();
- BOOST_REQUIRE_EQUAL(fuse->readdir("/test", &nl, &nameListAdd, 0, &fi), -EBADF);
+ BOOST_REQUIRE_EQUAL(fuse->readdir("/test", &nl, &nameListAdd, 0, &fi, FUSE_READDIR_PLUS), -EBADF);
BOOST_REQUIRE_EQUAL(fuse->releasedir("/test", &fi), -EBADF);
BOOST_REQUIRE_EQUAL(fuse->rmdir("/test"), -ENOTEMPTY);
BOOST_REQUIRE_EQUAL(fuse->rmdir("/test/sub"), 0);
BOOST_REQUIRE_EQUAL(fuse->rmdir("/test"), 0);
- BOOST_REQUIRE(!std::filesystem::is_directory(binDir / testExport / "test"));
+ BOOST_REQUIRE(!std::filesystem::is_directory(tmpDir / testExport / "test"));
BOOST_REQUIRE_EQUAL(fuse->rmdir("/test"), -ENOENT);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/test", &st), -ENOENT);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test", &st, nullptr), -ENOENT);
BOOST_REQUIRE_EQUAL(fuse->opendir("/test", &fi), -ENOENT);
}
-BOOST_AUTO_TEST_CASE( files )
+BOOST_AUTO_TEST_CASE(files)
{
- struct fuse_file_info fi;
- memset(&fi, 0, sizeof(fi));
- struct stat st, st2;
- memset(&st, 0, sizeof(st));
- memset(&st2, 0, sizeof(st2));
+ struct fuse_file_info fi {};
+ struct stat st {}, st2 {};
fi.flags = O_RDWR;
BOOST_REQUIRE_EQUAL(fuse->open("/test", &fi), -ENOENT);
BOOST_REQUIRE_EQUAL(fuse->create("/test", 0600, &fi), 0);
@@ -250,43 +260,44 @@ BOOST_AUTO_TEST_CASE( files )
BOOST_REQUIRE_EQUAL(fuse->release("/test", &fi), -EBADF);
BOOST_REQUIRE_EQUAL(fuse->create("/test", 0600, &fi), -EEXIST);
+ std::array<char, 11> buf {};
BOOST_REQUIRE_EQUAL(fuse->open("/test", &fi), 0);
+ BOOST_REQUIRE_EQUAL(fuse->read("/test", buf.data(), 0, 0, &fi), 0);
BOOST_REQUIRE_EQUAL(fuse->write("/test", "some test buffer", 16, 0, &fi), 16);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/test", &st), 0);
+ BOOST_REQUIRE_EQUAL(fuse->flush("/test", &fi), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test", &st, nullptr), 0);
BOOST_REQUIRE_EQUAL(st.st_size, 16);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/test2", &st), -ENOENT);
- BOOST_REQUIRE_EQUAL(fuse->rename("/test", "/test2"), 0);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/test", &st), -ENOENT);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/test2", &st2), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test2", &st, nullptr), -ENOENT);
+ BOOST_REQUIRE_EQUAL(fuse->rename("/test", "/test2", RENAME_EXCHANGE), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test", &st, nullptr), -ENOENT);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test2", &st2, nullptr), 0);
BOOST_REQUIRE_EQUAL(st, st2);
- BOOST_REQUIRE_EQUAL(fuse->fgetattr("/test2", &st2, &fi), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test2", &st2, &fi), 0);
BOOST_REQUIRE_EQUAL(st, st2);
BOOST_REQUIRE_EQUAL(st2.st_size, 16);
BOOST_REQUIRE_EQUAL(fuse->write("/test2", "BUFFER some", 11, 10, &fi), 11);
- BOOST_REQUIRE_EQUAL(fuse->fgetattr("/test2", &st2, &fi), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test2", &st2, &fi), 0);
BOOST_REQUIRE_EQUAL(st2.st_size, 21);
- char buf[11];
- memset(&buf, 0, sizeof(buf));
- BOOST_REQUIRE_EQUAL(fuse->read("/test2", buf, 10, 5, &fi), 10);
+ BOOST_REQUIRE_EQUAL(fuse->read("/test2", buf.data(), 10, 5, &fi), 10);
BOOST_REQUIRE_EQUAL(buf, "test BUFFE");
- BOOST_REQUIRE_EQUAL(fuse->ftruncate("/test2", 11, &fi), 0);
- BOOST_REQUIRE_EQUAL(fuse->fgetattr("/test2", &st2, &fi), 0);
+ BOOST_REQUIRE_EQUAL(fuse->truncate("/test2", 11, &fi), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test2", &st2, &fi), 0);
BOOST_REQUIRE_EQUAL(st2.st_size, 11);
- memset(&buf, 0, sizeof(buf));
- BOOST_REQUIRE_EQUAL(fuse->read("/test2", buf, 10, 5, &fi), 6);
+ buf = {};
+ BOOST_REQUIRE_EQUAL(fuse->read("/test2", buf.data(), 10, 5, &fi), 6);
BOOST_REQUIRE_EQUAL(buf, "test B");
- BOOST_REQUIRE_EQUAL(fuse->truncate("/test2", 7), 0);
- BOOST_REQUIRE_EQUAL(fuse->fgetattr("/test2", &st2, &fi), 0);
+ BOOST_REQUIRE_EQUAL(fuse->truncate("/test2", 7, nullptr), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test2", &st2, &fi), 0);
BOOST_REQUIRE_EQUAL(st2.st_size, 7);
- memset(&buf, 0, sizeof(buf));
- BOOST_REQUIRE_EQUAL(fuse->read("/test2", buf, 10, 5, &fi), 2);
+ buf = {};
+ BOOST_REQUIRE_EQUAL(fuse->read("/test2", buf.data(), 10, 5, &fi), 2);
BOOST_REQUIRE_EQUAL(buf, "te");
BOOST_REQUIRE_EQUAL(fuse->release("/test2", &fi), 0);
BOOST_REQUIRE_EQUAL(fuse->link("/test3", "/test2"), -ENOENT);
BOOST_REQUIRE_EQUAL(fuse->link("/test2", "/test3"), 0);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/test2", &st), 0);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/test3", &st2), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test2", &st, nullptr), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test3", &st2, nullptr), 0);
BOOST_REQUIRE_EQUAL(st, st2);
BOOST_REQUIRE_EQUAL(fuse->unlink("/test2"), 0);
BOOST_REQUIRE_EQUAL(fuse->unlink("/test2"), -ENOENT);
@@ -294,60 +305,72 @@ BOOST_AUTO_TEST_CASE( files )
BOOST_REQUIRE_EQUAL(fuse->unlink("/test3"), -ENOENT);
}
-BOOST_AUTO_TEST_CASE( bgwriteOverlapped, * boost::unit_test::timeout(2) )
+BOOST_AUTO_TEST_CASE(files_copy_range)
+{
+ struct fuse_file_info fiin {}, fiout {};
+ fiin.flags = O_RDWR;
+ fiout.flags = O_RDWR;
+ BOOST_REQUIRE_EQUAL(fuse->create("/src", 0600, &fiin), 0);
+ BOOST_REQUIRE_EQUAL(fuse->create("/dst", 0600, &fiout), 0);
+ BOOST_REQUIRE_EQUAL(fuse->write("/src", "hello world.", 12, 0, &fiin), 12);
+ BOOST_REQUIRE_EQUAL(fuse->copy_file_range("/src", &fiin, 6, "/dst", &fiout, 0, 5, 0), 5);
+ std::array<char, 5> buf {};
+ BOOST_REQUIRE_EQUAL(fuse->read("/dst", buf.data(), 5, 0, &fiout), 5);
+ BOOST_REQUIRE_EQUAL(memcmp("world", buf.data(), buf.size()), 0);
+ BOOST_REQUIRE_EQUAL(fuse->release("/src", &fiin), 0);
+ BOOST_REQUIRE_EQUAL(fuse->release("/dst", &fiout), 0);
+}
+
+BOOST_AUTO_TEST_CASE(bgwriteOverlapped, *boost::unit_test::timeout(2))
{
- struct fuse_file_info fi;
- memset(&fi, 0, sizeof(fi));
- struct stat st;
- memset(&st, 0, sizeof(st));
+ struct fuse_file_info fi {};
+ struct stat st {};
fi.flags = O_RDWR;
- auto s = sizeof(int32_t);
- auto N = 20;
+ constexpr auto s = sizeof(int32_t);
+ constexpr auto N = 20U;
BOOST_REQUIRE_EQUAL(fuse->create("/test", 0600, &fi), 0);
- for (int32_t n = 0; n < N; n += 1) {
- BOOST_REQUIRE_EQUAL(fuse->write("/test", (const char *)(&n), s, n, &fi), s);
+ for (auto n = 0U; n < N; n += 1) {
+ BOOST_REQUIRE_EQUAL(fuse->write("/test", reinterpret_cast<const char *>(&n), s, n, &fi), s);
}
- BOOST_REQUIRE_EQUAL(fuse->fgetattr("/test", &st, &fi), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test", &st, &fi), 0);
BOOST_REQUIRE_EQUAL(st.st_size, N + s - 1);
- char buf[s];
- BOOST_REQUIRE_EQUAL(fuse->read("/test", buf, s, (N - 1), &fi), s);
- BOOST_REQUIRE_EQUAL(*(int*)buf, N - 1);
+ std::array<char, s> buf;
+ BOOST_REQUIRE_EQUAL(fuse->read("/test", buf.data(), s, (N - 1), &fi), s);
+ BOOST_REQUIRE_EQUAL(*reinterpret_cast<unsigned int *>(buf.data()), N - 1);
BOOST_REQUIRE_EQUAL(fuse->release("/test", &fi), 0);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/test", &st), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test", &st, nullptr), 0);
BOOST_REQUIRE_EQUAL(st.st_size, N + s - 1);
}
-BOOST_AUTO_TEST_CASE( bgwriteDistinct, * boost::unit_test::timeout(2) )
+BOOST_AUTO_TEST_CASE(bgwriteDistinct, *boost::unit_test::timeout(2))
{
- struct fuse_file_info fi;
- memset(&fi, 0, sizeof(fi));
- struct stat st;
- memset(&st, 0, sizeof(st));
+ struct fuse_file_info fi {};
+ struct stat st {};
fi.flags = O_RDWR;
- auto s = sizeof(int32_t);
- auto N = 20;
+ constexpr auto s = sizeof(int32_t);
+ constexpr auto N = 20U;
BOOST_REQUIRE_EQUAL(fuse->create("/test", 0600, &fi), 0);
- for (int32_t n = 0; n < N; n += 1) {
- BOOST_REQUIRE_EQUAL(fuse->write("/test", (const char *)(&n), s, n * s, &fi), s);
+ for (auto n = 0U; n < N; n += 1) {
+ BOOST_REQUIRE_EQUAL(fuse->write("/test", reinterpret_cast<const char *>(&n), s, n * s, &fi), s);
}
- BOOST_REQUIRE_EQUAL(fuse->fgetattr("/test", &st, &fi), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test", &st, &fi), 0);
BOOST_REQUIRE_EQUAL(st.st_size, N * s);
- char buf[s];
- BOOST_REQUIRE_EQUAL(fuse->read("/test", buf, s, (N - 1) * s, &fi), s);
- BOOST_REQUIRE_EQUAL(*(int*)buf, N - 1);
+ std::array<char, s> buf;
+ BOOST_REQUIRE_EQUAL(fuse->read("/test", buf.data(), s, (N - 1) * s, &fi), s);
+ BOOST_REQUIRE_EQUAL(*reinterpret_cast<unsigned int *>(buf.data()), N - 1);
BOOST_REQUIRE_EQUAL(fuse->release("/test", &fi), 0);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/test", &st), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test", &st, nullptr), 0);
BOOST_REQUIRE_EQUAL(st.st_size, N * s);
}
-BOOST_AUTO_TEST_CASE( symlinks )
+BOOST_AUTO_TEST_CASE(symlinks)
{
- char buf[BUFSIZ];
+ std::array<char, PATH_MAX> buf;
BOOST_REQUIRE_EQUAL(fuse->mkdir("/dir", 0700), 0);
- BOOST_REQUIRE_EQUAL(fuse->readlink("/dir", buf, BUFSIZ), -EINVAL);
+ BOOST_REQUIRE_EQUAL(fuse->readlink("/dir", buf.data(), buf.size()), -EINVAL);
BOOST_REQUIRE_EQUAL(fuse->symlink("/test2", "/test3"), 0);
BOOST_REQUIRE_EQUAL(fuse->symlink("/test2", "/test3"), -EEXIST);
- BOOST_REQUIRE_EQUAL(fuse->readlink("/test3", buf, BUFSIZ), 0);
+ BOOST_REQUIRE_EQUAL(fuse->readlink("/test3", buf.data(), buf.size()), 0);
BOOST_REQUIRE_EQUAL("/test2", buf);
BOOST_REQUIRE_EQUAL(fuse->unlink("/test3"), 0);
BOOST_REQUIRE_EQUAL(fuse->unlink("/test3"), -ENOENT);
@@ -356,23 +379,22 @@ BOOST_AUTO_TEST_CASE( symlinks )
enableWriteOnDir(fuse, "/");
}
-BOOST_AUTO_TEST_CASE( access )
+BOOST_AUTO_TEST_CASE(access)
{
- struct stat st;
- memset(&st, 0, sizeof(st));
+ struct stat st {};
BOOST_REQUIRE_EQUAL(fuse->access("/", F_OK), 0);
BOOST_REQUIRE_EQUAL(fuse->access("/missing", F_OK), -ENOENT);
// Basic assertions
BOOST_REQUIRE_EQUAL(fuse->mkdir("/dir", 0700), 0);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/dir", &st), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/dir", &st, nullptr), 0);
BOOST_REQUIRE_EQUAL(st.st_mode, 0700 | S_IFDIR);
BOOST_REQUIRE_EQUAL(fuse->access("/dir", F_OK), 0);
BOOST_REQUIRE_EQUAL(fuse->access("/dir", R_OK), 0);
BOOST_REQUIRE_EQUAL(fuse->access("/dir", W_OK), 0);
BOOST_REQUIRE_EQUAL(fuse->access("/dir", X_OK), 0);
- BOOST_REQUIRE_EQUAL(fuse->chmod("/missing", 0000), -ENOENT);
- BOOST_REQUIRE_EQUAL(fuse->chmod("/dir", 0000), 0);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/dir", &st), 0);
+ BOOST_REQUIRE_EQUAL(fuse->chmod("/missing", 0000, nullptr), -ENOENT);
+ BOOST_REQUIRE_EQUAL(fuse->chmod("/dir", 0000, nullptr), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/dir", &st, nullptr), 0);
BOOST_REQUIRE_EQUAL(st.st_mode, 0000 | S_IFDIR);
BOOST_REQUIRE_EQUAL(fuse->access("/dir", F_OK), 0);
BOOST_REQUIRE_EQUAL(fuse->access("/dir", R_OK), -EACCES);
@@ -380,54 +402,50 @@ BOOST_AUTO_TEST_CASE( access )
BOOST_REQUIRE_EQUAL(fuse->access("/dir", X_OK), -EACCES);
enableWriteOnDir(fuse, "/dir");
BOOST_REQUIRE_EQUAL(fuse->rmdir("/dir"), 0);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/dir", &st), -ENOENT);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/dir", &st, nullptr), -ENOENT);
}
-BOOST_AUTO_TEST_CASE( permissionsDirs )
+BOOST_AUTO_TEST_CASE(permissionsDirs)
{
- struct stat st;
- memset(&st, 0, sizeof(st));
+ struct stat st {};
BOOST_REQUIRE_EQUAL(fuse->mkdir("/dir", 0700), 0);
BOOST_REQUIRE_EQUAL(fuse->mkdir("/dir/yes", 0000), 0);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/dir/yes", &st), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/dir/yes", &st, nullptr), 0);
BOOST_REQUIRE_EQUAL(st.st_mode, 0000 | S_IFDIR);
disableWriteOnDir(fuse, "/dir");
BOOST_REQUIRE_EQUAL(fuse->mkdir("/dir/no", 0700), -EACCES);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/dir/no", &st), -ENOENT);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/dir/no", &st, nullptr), -ENOENT);
BOOST_REQUIRE_EQUAL(fuse->rmdir("/dir/yes"), -EACCES);
enableWriteOnDir(fuse, "/dir");
BOOST_REQUIRE_EQUAL(fuse->rmdir("/dir/yes"), 0);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/dir/yes", &st), -ENOENT);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/dir/yes", &st, nullptr), -ENOENT);
}
-BOOST_AUTO_TEST_CASE( permissionsFiles )
+BOOST_AUTO_TEST_CASE(permissionsFiles)
{
- struct stat st;
- memset(&st, 0, sizeof(st));
- struct fuse_file_info fi;
- memset(&fi, 0, sizeof(fi));
+ struct stat st {};
+ struct fuse_file_info fi {};
fi.flags = O_RDWR;
BOOST_REQUIRE_EQUAL(fuse->mkdir("/dir", 0700), 0);
BOOST_REQUIRE_NE(fuse->create("/dir/yes", 0000, &fi), -1);
BOOST_REQUIRE_EQUAL(fuse->release("/dir/yes", &fi), 0);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/dir/yes", &st), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/dir/yes", &st, nullptr), 0);
BOOST_REQUIRE_EQUAL(st.st_mode, 0000 | S_IFREG);
disableWriteOnDir(fuse, "/dir");
BOOST_REQUIRE_EQUAL(fuse->create("/dir/no", 0600, &fi), -EACCES);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/dir/no", &st), -ENOENT);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/dir/no", &st, nullptr), -ENOENT);
BOOST_REQUIRE_EQUAL(fuse->unlink("/dir/yes"), -EACCES);
enableWriteOnDir(fuse, "/dir");
BOOST_REQUIRE_EQUAL(fuse->unlink("/dir/yes"), 0);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/dir/yes", &st), -ENOENT);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/dir/yes", &st, nullptr), -ENOENT);
}
-BOOST_AUTO_TEST_CASE( mknod )
+BOOST_AUTO_TEST_CASE(mknod)
{
BOOST_REQUIRE_EQUAL(fuse->mknod("/nod", 0600 | S_IFIFO, 0), 0);
BOOST_REQUIRE_EQUAL(fuse->mknod("/nod", 0600 | S_IFIFO, 0), -EEXIST);
- struct stat st;
- memset(&st, 0, sizeof(st));
- BOOST_REQUIRE_EQUAL(fuse->getattr("/nod", &st), 0);
+ struct stat st {};
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/nod", &st, nullptr), 0);
BOOST_REQUIRE_EQUAL(st.st_mode, 0600 | S_IFIFO);
BOOST_REQUIRE_EQUAL(fuse->unlink("/nod"), 0);
BOOST_REQUIRE_EQUAL(fuse->unlink("/nod"), -ENOENT);
@@ -436,53 +454,63 @@ BOOST_AUTO_TEST_CASE( mknod )
enableWriteOnDir(fuse, "/");
}
-BOOST_AUTO_TEST_CASE( renameToDir )
+BOOST_AUTO_TEST_CASE(renameToDir)
{
- struct fuse_file_info fi;
- memset(&fi, 0, sizeof(fi));
+ struct fuse_file_info fi {};
BOOST_REQUIRE_EQUAL(fuse->create("/file", 0600, &fi), 0);
BOOST_REQUIRE_EQUAL(fuse->release("/file", &fi), 0);
BOOST_REQUIRE_EQUAL(fuse->mkdir("/dir", 0700), 0);
- BOOST_REQUIRE_EQUAL(fuse->rename("/file", "/dir"), -EISDIR);
+ BOOST_REQUIRE_EQUAL(fuse->rename("/file", "/dir", RENAME_EXCHANGE), -EISDIR);
disableWriteOnDir(fuse, "/dir");
- BOOST_REQUIRE_EQUAL(fuse->rename("/file", "/dir/file"), -EACCES);
+ BOOST_REQUIRE_EQUAL(fuse->rename("/file", "/dir/file", RENAME_EXCHANGE), -EACCES);
enableWriteOnDir(fuse, "/dir");
disableWriteOnDir(fuse, "/");
- BOOST_REQUIRE_EQUAL(fuse->rename("/file", "/dir/file"), -EACCES);
+ BOOST_REQUIRE_EQUAL(fuse->rename("/file", "/dir/file", RENAME_EXCHANGE), -EACCES);
enableWriteOnDir(fuse, "/");
- BOOST_REQUIRE_EQUAL(fuse->rename("/file", "/dir/file"), 0);
+ BOOST_REQUIRE_EQUAL(fuse->rename("/file", "/dir/file", RENAME_EXCHANGE), 0);
BOOST_REQUIRE_EQUAL(fuse->unlink("/file"), -ENOENT);
BOOST_REQUIRE_EQUAL(fuse->unlink("/dir/file"), 0);
BOOST_REQUIRE_EQUAL(fuse->rmdir("/dir"), 0);
}
-BOOST_AUTO_TEST_CASE( chown )
+BOOST_AUTO_TEST_CASE(renameFlags)
+{
+ struct fuse_file_info fi {};
+ BOOST_REQUIRE_EQUAL(fuse->create("/file", 0600, &fi), 0);
+ BOOST_REQUIRE_EQUAL(fuse->release("/file", &fi), 0);
+ BOOST_REQUIRE_EQUAL(fuse->create("/file2", 0600, &fi), 0);
+ BOOST_REQUIRE_EQUAL(fuse->release("/file2", &fi), 0);
+
+ BOOST_REQUIRE_EQUAL(fuse->rename("/file", "/file2", RENAME_NOREPLACE), -EEXIST);
+ BOOST_REQUIRE_EQUAL(fuse->access("/file", F_OK), 0);
+ BOOST_REQUIRE_EQUAL(fuse->access("/file2", F_OK), 0);
+
+ BOOST_REQUIRE_EQUAL(fuse->rename("/file", "/file2", RENAME_EXCHANGE), 0);
+ BOOST_REQUIRE_EQUAL(fuse->access("/file", F_OK), -ENOENT);
+ BOOST_REQUIRE_EQUAL(fuse->access("/file2", F_OK), 0);
+}
+
+BOOST_AUTO_TEST_CASE(chown)
{
BOOST_REQUIRE_EQUAL(fuse->mkdir("/dir", 0777), 0);
- BOOST_REQUIRE_EQUAL(fuse->chown("/dir", getuid(), getgid()), 0);
- struct stat st;
- memset(&st, 0, sizeof(st));
- BOOST_REQUIRE_EQUAL(fuse->getattr("/dir", &st), 0);
+ BOOST_REQUIRE_EQUAL(fuse->chown("/dir", getuid(), getgid(), nullptr), 0);
+ struct stat st {};
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/dir", &st, nullptr), 0);
BOOST_REQUIRE_EQUAL(st.st_uid, getuid());
BOOST_REQUIRE_EQUAL(st.st_gid, getgid());
- BOOST_REQUIRE_EQUAL(fuse->chown("/dir", -2, getgid()), -EPERM);
+ BOOST_REQUIRE_EQUAL(fuse->chown("/dir", 99999999, getgid(), nullptr), -EPERM);
BOOST_REQUIRE_EQUAL(fuse->rmdir("/dir"), 0);
}
-BOOST_AUTO_TEST_CASE( utimens )
+BOOST_AUTO_TEST_CASE(utimens)
{
BOOST_REQUIRE_EQUAL(fuse->mknod("/file", 0600, 0), 0);
- struct timespec times[2];
- times[0].tv_sec = 1;
- times[0].tv_nsec = 100;
- times[1].tv_sec = 2;
- times[1].tv_nsec = 200;
- BOOST_REQUIRE_EQUAL(fuse->utimens("/file", times), 0);
+ std::array<timespec, 2> times {{{1, 100}, {2, 200}}};
+ BOOST_REQUIRE_EQUAL(fuse->utimens("/file", times.data(), nullptr), 0);
times[1].tv_nsec = -200;
- BOOST_REQUIRE_EQUAL(fuse->utimens("/file", times), -EINVAL);
- struct stat st;
- memset(&st, 0, sizeof(st));
- BOOST_REQUIRE_EQUAL(fuse->getattr("/file", &st), 0);
+ BOOST_REQUIRE_EQUAL(fuse->utimens("/file", times.data(), nullptr), -EINVAL);
+ struct stat st {};
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/file", &st, nullptr), 0);
BOOST_REQUIRE_EQUAL(st.st_atime, 1);
BOOST_REQUIRE_EQUAL(st.st_atim.tv_sec, 1);
BOOST_REQUIRE_EQUAL(st.st_mtime, 2);
@@ -492,7 +520,7 @@ BOOST_AUTO_TEST_CASE( utimens )
BOOST_REQUIRE_EQUAL(st.st_mtim.tv_nsec, 0);
BOOST_REQUIRE_EQUAL(st.st_ctim.tv_nsec, 0);
// Real file doesn't
- BOOST_REQUIRE_EQUAL(lstat((binDir / testExport / "file").c_str(), &st), 0);
+ BOOST_REQUIRE_EQUAL(lstat((tmpDir / testExport / "file").c_str(), &st), 0);
BOOST_REQUIRE_EQUAL(st.st_atim.tv_sec, 1);
BOOST_REQUIRE_EQUAL(st.st_atime, 1);
BOOST_REQUIRE_EQUAL(st.st_mtim.tv_sec, 2);
@@ -503,19 +531,18 @@ BOOST_AUTO_TEST_CASE( utimens )
BOOST_AUTO_TEST_SUITE_END();
-BOOST_FIXTURE_TEST_SUITE( NetfsAltCore, AltCore );
+BOOST_FIXTURE_TEST_SUITE(NetfsAltCore, AltCore);
-BOOST_AUTO_TEST_CASE( noListDir )
+BOOST_AUTO_TEST_CASE(noListDir)
{
- struct fuse_file_info fi;
- memset(&fi, 0, sizeof(fi));
+ struct fuse_file_info fi {};
BOOST_REQUIRE_EQUAL(fuse->mkdir("/test", 0700), 0);
- BOOST_REQUIRE(std::filesystem::is_directory(binDir / testExport / "test"));
+ BOOST_REQUIRE(std::filesystem::is_directory(tmpDir / testExport / "test"));
BOOST_REQUIRE_EQUAL(fuse->mkdir("/test", 0700), -EEXIST);
BOOST_REQUIRE_EQUAL(fuse->opendir("/test", &fi), 0);
NetFS::NameList nl;
- BOOST_REQUIRE_EQUAL(fuse->readdir("/test", &nl, &nameListAdd, 0, &fi), 0);
+ BOOST_REQUIRE_EQUAL(fuse->readdir("/test", &nl, &nameListAdd, 0, &fi, fuse_readdir_flags {}), 0);
BOOST_REQUIRE_EQUAL(nl.size(), 2);
std::sort(nl.begin(), nl.end());
BOOST_REQUIRE_EQUAL(nl[0], ".");
@@ -525,7 +552,7 @@ BOOST_AUTO_TEST_CASE( noListDir )
BOOST_REQUIRE_EQUAL(fuse->mkdir("/test/sub", 0700), 0);
BOOST_REQUIRE_EQUAL(fuse->opendir("/test", &fi), 0);
- BOOST_REQUIRE_EQUAL(fuse->readdir("/test", &nl, &nameListAdd, 0, &fi), 0);
+ BOOST_REQUIRE_EQUAL(fuse->readdir("/test", &nl, &nameListAdd, 0, &fi, fuse_readdir_flags {}), 0);
BOOST_REQUIRE_EQUAL(nl.size(), 3);
std::sort(nl.begin(), nl.end());
BOOST_REQUIRE_EQUAL(nl[0], ".");
@@ -534,80 +561,76 @@ BOOST_AUTO_TEST_CASE( noListDir )
BOOST_REQUIRE_EQUAL(fuse->releasedir("/test", &fi), 0);
nl.clear();
- BOOST_REQUIRE_EQUAL(fuse->readdir("/test", &nl, &nameListAdd, 0, &fi), -EBADF);
+ BOOST_REQUIRE_EQUAL(fuse->readdir("/test", &nl, &nameListAdd, 0, &fi, fuse_readdir_flags {}), -EBADF);
BOOST_REQUIRE_EQUAL(fuse->releasedir("/test", &fi), -EBADF);
BOOST_REQUIRE_EQUAL(fuse->rmdir("/test"), -ENOTEMPTY);
BOOST_REQUIRE_EQUAL(fuse->rmdir("/test/sub"), 0);
BOOST_REQUIRE_EQUAL(fuse->rmdir("/test"), 0);
- BOOST_REQUIRE(!std::filesystem::is_directory(binDir / testExport / "test"));
+ BOOST_REQUIRE(!std::filesystem::is_directory(tmpDir / testExport / "test"));
BOOST_REQUIRE_EQUAL(fuse->rmdir("/test"), -ENOENT);
BOOST_REQUIRE_EQUAL(fuse->opendir("/test", &fi), -ENOENT);
}
-BOOST_AUTO_TEST_CASE( testFGWrites )
+BOOST_AUTO_TEST_CASE(testFGWrites)
{
- struct fuse_file_info fi;
- memset(&fi, 0, sizeof(fi));
- struct stat st;
- memset(&st, 0, sizeof(st));
+ struct fuse_file_info fi {};
+ struct stat st {};
fi.flags = O_RDWR;
BOOST_REQUIRE_EQUAL(fuse->create("/test", 0600, &fi), 0);
BOOST_REQUIRE_EQUAL(fuse->write("/test", "some test buffer", 16, 0, &fi), 16);
- BOOST_REQUIRE_EQUAL(fuse->getattr("/test", &st), 0);
+ BOOST_REQUIRE_EQUAL(fuse->getattr("/test", &st, nullptr), 0);
BOOST_REQUIRE_EQUAL(st.st_size, 16);
- char buf[11];
- memset(&buf, 0, sizeof(buf));
- BOOST_REQUIRE_EQUAL(fuse->read("/test", buf, 10, 5, &fi), 10);
+ std::array<char, 11> buf {};
+ BOOST_REQUIRE_EQUAL(fuse->read("/test", buf.data(), 10, 5, &fi), 10);
BOOST_REQUIRE_EQUAL(buf, "test buffe");
BOOST_REQUIRE_EQUAL(fuse->release("/test", &fi), 0);
}
BOOST_AUTO_TEST_SUITE_END();
-BOOST_AUTO_TEST_CASE( testNoAuthNoPass )
+BOOST_AUTO_TEST_CASE(testNoAuthNoPass)
{
Core c("defaultDaemon.xml", "defaultFuse.xml");
struct statvfs s;
- BOOST_REQUIRE_EQUAL(0, c.fuse->statfs("/", &s));
+ BOOST_REQUIRE_EQUAL(0, c.fuse->statfs("/", &s));
}
-BOOST_AUTO_TEST_CASE( testWithAuthNoPass )
+BOOST_AUTO_TEST_CASE(testWithAuthNoPass)
{
Core c("secureDaemon.xml", "defaultFuse.xml");
struct statvfs s;
- BOOST_REQUIRE_EQUAL(-EPERM, c.fuse->statfs("/", &s));
+ BOOST_REQUIRE_EQUAL(-EPERM, c.fuse->statfs("/", &s));
}
-BOOST_AUTO_TEST_CASE( testWithAuthWithPass )
+BOOST_AUTO_TEST_CASE(testWithAuthWithPass)
{
Core c("secureDaemon.xml", "secureFuse.xml");
struct statvfs s;
- BOOST_REQUIRE_EQUAL(0, c.fuse->statfs("/", &s));
+ BOOST_REQUIRE_EQUAL(0, c.fuse->statfs("/", &s));
}
-BOOST_AUTO_TEST_CASE( testNoAuthWithPass )
+BOOST_AUTO_TEST_CASE(testNoAuthWithPass)
{
Core c("defaultDaemon.xml", "secureFuse.xml");
struct statvfs s;
- BOOST_REQUIRE_EQUAL(0, c.fuse->statfs("/", &s));
+ BOOST_REQUIRE_EQUAL(0, c.fuse->statfs("/", &s));
}
-BOOST_AUTO_TEST_CASE( uriConnect )
+BOOST_AUTO_TEST_CASE(uriConnect)
{
- MockDaemonHost daemon(testEndpoint, {
- "--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string()
- });
- FuseMockHost fuse(std::string(), {
- testUri,
- (binDir / "test").string(),
+ MockDaemonHost daemon(testEndpoint, {"--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string()});
+ FuseMockHost fuse(std::string(),
+ {
+ testUri,
+ (tmpDir / "test").string(),
});
struct statvfs s;
- BOOST_REQUIRE_EQUAL(0, fuse.fuse->statfs("/", &s));
+ BOOST_REQUIRE_EQUAL(0, fuse.fuse->statfs("/", &s));
}
-BOOST_AUTO_TEST_CASE( interval_map_works_how_i_think )
+BOOST_AUTO_TEST_CASE(interval_map_works_how_i_think)
{
// Used as proof that setting and selecting ranges given an
// attempt to write to [offset, offset + size) will find
@@ -618,7 +641,7 @@ BOOST_AUTO_TEST_CASE( interval_map_works_how_i_think )
auto check = [&](off_t o, size_t s, int c) {
BOOST_TEST_CONTEXT("offset: " << o << ", size: " << s << ", count: " << c) {
const auto ol = map.equal_range(r(o, s));
- BOOST_TEST_CONTEXT("range: " << ol.first->first.lower() << " to " << ol.first->first.upper()) {
+ BOOST_TEST_CONTEXT("range start: " << ol.first->first.lower()) {
BOOST_REQUIRE_EQUAL(std::distance(ol.first, ol.second), c);
}
}
@@ -626,9 +649,9 @@ BOOST_AUTO_TEST_CASE( interval_map_works_how_i_think )
BOOST_REQUIRE(map.empty());
// Pretend we have 3 writes in progress
- map.insert({r(5, 10), std::make_shared<NetFS::FuseApp::OpenFile::WriteState>() }); // [5, 15)
- map.insert({r(15, 15), std::make_shared<NetFS::FuseApp::OpenFile::WriteState>() }); // [15, 30)
- map.insert({r(35, 10), std::make_shared<NetFS::FuseApp::OpenFile::WriteState>() }); // [35, 45)
+ map.insert({r(5, 10), std::make_shared<NetFS::FuseApp::OpenFile::WriteState>()}); // [5, 15)
+ map.insert({r(15, 15), std::make_shared<NetFS::FuseApp::OpenFile::WriteState>()}); // [15, 30)
+ map.insert({r(35, 10), std::make_shared<NetFS::FuseApp::OpenFile::WriteState>()}); // [35, 45)
// Then the following writes to [O, O+S) should wait on N pending writes
check(0, 1, 0); // Clear before
check(100, 10, 0); // Clear after
@@ -650,4 +673,3 @@ BOOST_AUTO_TEST_CASE( interval_map_works_how_i_think )
map.erase(r(35, 10));
BOOST_REQUIRE(map.empty());
}
-
diff --git a/netfs/unittests/testDaemon.cpp b/netfs/unittests/testDaemon.cpp
new file mode 100644
index 0000000..5c6fbc9
--- /dev/null
+++ b/netfs/unittests/testDaemon.cpp
@@ -0,0 +1,28 @@
+#define BOOST_TEST_MODULE TestNetFSDaemon
+#include <boost/test/data/test_case.hpp>
+#include <boost/test/unit_test.hpp>
+
+#include <entCache.h>
+#include <exceptions.h>
+#include <modeCheck.h>
+
+struct ModeCheckHelper {
+ EntryResolverPtr<User> eru = std::make_shared<UserEntCache>();
+ EntryResolverPtr<Group> erg = std::make_shared<GroupEntCache>(eru);
+};
+
+BOOST_FIXTURE_TEST_SUITE(mc, ModeCheckHelper)
+
+BOOST_AUTO_TEST_CASE(mode_check_valid)
+{
+ ModeCheck mc {{"root", "root"}, "/tmp", *eru, *erg};
+}
+
+BOOST_AUTO_TEST_CASE(mode_check_invalid)
+{
+ BOOST_CHECK_THROW({ ModeCheck({"root", ""}, "/tmp", *eru, *erg); }, NetFS::SystemError);
+ BOOST_CHECK_THROW({ ModeCheck({"", "root"}, "/tmp", *eru, *erg); }, NetFS::SystemError);
+ BOOST_CHECK_THROW({ ModeCheck({"", ""}, "/tmp", *eru, *erg); }, NetFS::SystemError);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/netfs/unittests/testEdgeCases.cpp b/netfs/unittests/testEdgeCases.cpp
index 0cfb888..5eb8d5d 100644
--- a/netfs/unittests/testEdgeCases.cpp
+++ b/netfs/unittests/testEdgeCases.cpp
@@ -1,47 +1,51 @@
#define BOOST_TEST_MODULE TestNetFSEdgeCases
-#include <boost/test/unit_test.hpp>
+
+#include "fuseFiles.h"
#include "mockDaemon.h"
#include "mockFuse.h"
+#include <boost/test/data/test_case.hpp>
+#include <boost/test/unit_test.hpp>
#include <definedDirs.h>
+#include <fcntl.h>
+#include <fileUtils.h>
const std::string testEndpoint("tcp -h localhost -p 12014");
-BOOST_AUTO_TEST_CASE ( createAndDaemonRestart )
+BOOST_AUTO_TEST_CASE(createAndDaemonRestart)
{
- MockDaemonHost daemon(testEndpoint, {
- "--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string()
- });
- FuseMockHost fuse(testEndpoint, {
- (rootDir / "defaultFuse.xml:testvol").string(),
- (rootDir / "test").string()
- });
+ MockDaemonHost daemon(testEndpoint, {"--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string()});
+ FuseMockHost fuse(testEndpoint, {(rootDir / "defaultFuse.xml:testvol").string(), (rootDir / "test").string()});
- struct statvfs s;
- BOOST_REQUIRE_EQUAL(0, fuse.fuse->statfs("/", &s));
+ struct statvfs s {};
+ BOOST_REQUIRE_EQUAL(0, fuse.fuse->statfs("/", &s));
const char * fileName = "/createMe";
BOOST_TEST_CHECKPOINT("Create a new file");
- struct fuse_file_info fh;
+ struct fuse_file_info fh {};
memset(&fh, 0, sizeof(fh));
fh.flags = O_WRONLY | O_CREAT | O_APPEND;
BOOST_REQUIRE_EQUAL(0, fuse.fuse->create(fileName, 0100644, &fh));
BOOST_REQUIRE(fh.fh);
BOOST_TEST_CHECKPOINT("Fetch file attributes");
- struct stat st;
- BOOST_REQUIRE_EQUAL(0, fuse.fuse->fgetattr(fileName, &st, &fh));
+ struct stat st {};
+ BOOST_REQUIRE_EQUAL(0, fuse.fuse->getattr(fileName, &st, &fh));
BOOST_REQUIRE_EQUAL(st.st_size, 0);
BOOST_REQUIRE_EQUAL(st.st_uid, getuid());
BOOST_REQUIRE_EQUAL(st.st_gid, getgid());
BOOST_TEST_CHECKPOINT("Write some data");
- char someData[890];
- for (auto & d : someData) {
- d = rand();
- }
- BOOST_REQUIRE_EQUAL(sizeof(someData), fuse.fuse->write(fileName, someData, sizeof(someData), 0, &fh));
+ std::array<char, 890> someData {};
+ std::generate(begin(someData), end(someData),
+ [dist = std::uniform_int_distribution<char>(
+ std::numeric_limits<char>::min(), std::numeric_limits<char>::max()),
+ mersenne_engine = std::mt19937(std::random_device()())]() mutable {
+ return dist(mersenne_engine);
+ });
- BOOST_REQUIRE_EQUAL(0, fuse.fuse->fgetattr(fileName, &st, &fh));
+ BOOST_REQUIRE_EQUAL(sizeof(someData), fuse.fuse->write(fileName, someData.data(), someData.size(), 0, &fh));
+
+ BOOST_REQUIRE_EQUAL(0, fuse.fuse->getattr(fileName, &st, &fh));
BOOST_REQUIRE_EQUAL(st.st_size, sizeof(someData));
BOOST_REQUIRE_EQUAL(0, fuse.fuse->flush(fileName, &fh));
@@ -49,75 +53,64 @@ BOOST_AUTO_TEST_CASE ( createAndDaemonRestart )
daemon.restart();
BOOST_TEST_CHECKPOINT("Fetch file attributes again");
- BOOST_REQUIRE_EQUAL(0, fuse.fuse->fgetattr(fileName, &st, &fh));
+ BOOST_REQUIRE_EQUAL(0, fuse.fuse->getattr(fileName, &st, &fh));
BOOST_TEST_CHECKPOINT("Close file");
BOOST_REQUIRE_EQUAL(0, fuse.fuse->release(fileName, &fh));
}
-BOOST_AUTO_TEST_CASE( noDaemonAtStartUp )
+BOOST_AUTO_TEST_CASE(noDaemonAtStartUp)
{
- FuseMockHost fuse(testEndpoint, {
- (rootDir / "defaultFuse.xml:testvol").string(),
- (rootDir / "test").string()
- });
+ FuseMockHost fuse(testEndpoint, {(rootDir / "defaultFuse.xml:testvol").string(), (rootDir / "test").string()});
- struct statvfs s;
- BOOST_REQUIRE_EQUAL(-EIO, fuse.fuse->statfs("/", &s));
- MockDaemonHost daemon(testEndpoint, {
- "--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string()
- });
+ struct statvfs s {};
+ BOOST_REQUIRE_EQUAL(-EIO, fuse.fuse->statfs("/", &s));
+ MockDaemonHost daemon(testEndpoint, {"--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string()});
- BOOST_REQUIRE_EQUAL(0, fuse.fuse->statfs("/", &s));
+ BOOST_REQUIRE_EQUAL(0, fuse.fuse->statfs("/", &s));
}
-BOOST_AUTO_TEST_CASE ( daemonUnavailableAfterUse )
+BOOST_AUTO_TEST_CASE(daemonUnavailableAfterUse)
{
- FuseMockHost fuse(testEndpoint, {
- (rootDir / "defaultFuse.xml:testvol").string(),
- (rootDir / "test").string()
- });
- struct statvfs s;
+ FuseMockHost fuse(testEndpoint, {(rootDir / "defaultFuse.xml:testvol").string(), (rootDir / "test").string()});
+
+ struct statvfs s {};
{
- MockDaemonHost daemon(testEndpoint, {
- "--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string()
- });
+ MockDaemonHost daemon(testEndpoint, {"--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string()});
- BOOST_REQUIRE_EQUAL(0, fuse.fuse->statfs("/", &s));
+ BOOST_REQUIRE_EQUAL(0, fuse.fuse->statfs("/", &s));
}
- BOOST_REQUIRE_EQUAL(-EIO, fuse.fuse->statfs("/", &s));
+
+ BOOST_REQUIRE_EQUAL(-EIO, fuse.fuse->statfs("/", &s));
{
- MockDaemonHost daemon(testEndpoint, {
- "--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string()
- });
+ MockDaemonHost daemon(testEndpoint, {"--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string()});
- BOOST_REQUIRE_EQUAL(0, fuse.fuse->statfs("/", &s));
+ BOOST_REQUIRE_EQUAL(0, fuse.fuse->statfs("/", &s));
}
- BOOST_REQUIRE_EQUAL(-EIO, fuse.fuse->statfs("/", &s));
+ BOOST_REQUIRE_EQUAL(-EIO, fuse.fuse->statfs("/", &s));
}
-BOOST_AUTO_TEST_CASE( manyThreads )
+BOOST_AUTO_TEST_CASE(manyThreads)
{
- MockDaemonHost daemon(testEndpoint, {
- "--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string()
- });
- FuseMockHost fuse(testEndpoint, {
- (rootDir / "defaultFuse.xml:testvol").string(),
- (rootDir / "test").string()
- });
+ MockDaemonHost daemon(testEndpoint, {"--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string()});
+ FuseMockHost fuse(testEndpoint, {(rootDir / "defaultFuse.xml:testvol").string(), (rootDir / "test").string()});
- bool running = true;
+ std::atomic<bool> running {true};
std::vector<std::thread> ths;
- std::atomic_uint success = 0;
+ ths.reserve(20);
+ std::atomic<unsigned int> success {0}, failure {0};
for (int x = 0; x < 20; x++) {
ths.emplace_back([&] {
- struct statvfs s;
- while(running) {
+ struct statvfs s {};
+ while (running) {
if (fuse.fuse->statfs("/", &s) == 0) {
success++;
}
+ else {
+ failure++;
+ }
+ usleep(10000);
}
- usleep(10000);
});
}
@@ -125,11 +118,54 @@ BOOST_AUTO_TEST_CASE( manyThreads )
daemon.restart();
sleep(1);
running = false;
-
+
for (auto & th : ths) {
th.join();
}
BOOST_CHECK_GT(success, 800);
+ BOOST_CHECK_EQUAL(failure, 0);
}
+BOOST_DATA_TEST_CASE(bigWritesAndReads,
+ boost::unit_test::data::make({"defaultFuseNoAsync.xml", "defaultFuse.xml"})
+ * boost::unit_test::data::xrange<size_t>(1024UL * 100UL, 4096UL * 1024UL, 512UL * 1024UL)
+ * boost::unit_test::data::make<off_t>(0, 317, 4096UL * 1024UL),
+ config, size, offset)
+{
+ MockDaemonHost daemon(testEndpoint, {"--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string()});
+ FuseMockHost fuse(testEndpoint, {(rootDir / config).string() + ":testvol", (rootDir / "test").string()});
+
+ {
+ const auto buf = FuseMock::genRandomData(size);
+ {
+ fuse_file_info fi {};
+ fi.flags = O_RDWR;
+ auto fd = fuse.fuse->create("/big", 0600, &fi);
+ BOOST_REQUIRE_GE(fd, 0);
+ BOOST_REQUIRE_NE(fi.fh, 0);
+ BOOST_REQUIRE_EQUAL(buf.size(), fuse.fuse->write("/big", buf.data(), buf.size(), offset, &fi));
+ BOOST_REQUIRE_EQUAL(0, fuse.fuse->release("/big", &fi));
+ }
+
+ AdHoc::FileUtils::MemMap mm(daemon.TestExportRoot / "big");
+ const auto file {mm.sv()};
+ BOOST_CHECK_EQUAL_COLLECTIONS(buf.begin(), buf.end(), file.begin() + offset, file.end());
+ }
+ {
+ auto buf = FuseMock::genRandomData(size);
+ {
+ fuse_file_info fi {};
+ fi.flags = O_RDONLY;
+ auto fd = fuse.fuse->open("/big", &fi);
+ BOOST_REQUIRE_GE(fd, 0);
+ BOOST_REQUIRE_NE(fi.fh, 0);
+ BOOST_REQUIRE_EQUAL(buf.size(), fuse.fuse->read("/big", buf.data(), buf.size(), offset, &fi));
+ BOOST_REQUIRE_EQUAL(0, fuse.fuse->release("/big", &fi));
+ }
+
+ AdHoc::FileUtils::MemMap mm(daemon.TestExportRoot / "big");
+ const auto file {mm.sv()};
+ BOOST_CHECK_EQUAL_COLLECTIONS(buf.begin(), buf.end(), file.begin() + offset, file.end());
+ }
+}
diff --git a/netfs/unittests/testFuse.cpp b/netfs/unittests/testFuse.cpp
new file mode 100644
index 0000000..c04c52c
--- /dev/null
+++ b/netfs/unittests/testFuse.cpp
@@ -0,0 +1,143 @@
+#define BOOST_TEST_MODULE TestNetFSFuse
+#include "mockDaemon.h"
+#include "mockMount.h"
+#include <boost/test/unit_test.hpp>
+#include <c++11Helpers.h>
+#include <cache.impl.h>
+#include <definedDirs.h>
+#include <filesystem>
+#include <fuse.h>
+#include <fuseApp.h>
+#include <fuseMappersImpl.h>
+#include <lockHelpers.h>
+#include <ostream>
+#include <thread>
+
+#define BOOST_CHECK_EQUAL_FIELD(left, right, field) BOOST_CHECK_EQUAL((left).field, (right).field);
+
+#define BOOST_CHECK_EQUAL_STAT(left, right) \
+ BOOST_CHECK_NE(&(left), &(right)); \
+ BOOST_CHECK_EQUAL_FIELD(left, right, st_atim.tv_sec); \
+ BOOST_CHECK_EQUAL_FIELD(left, right, st_mtim.tv_sec); \
+ BOOST_CHECK_EQUAL_FIELD(left, right, st_ctim.tv_sec); \
+ BOOST_CHECK_EQUAL_FIELD(left, right, st_mode); \
+ BOOST_CHECK_EQUAL_FIELD(left, right, st_nlink); \
+ BOOST_CHECK_EQUAL_FIELD(left, right, st_uid); \
+ BOOST_CHECK_EQUAL_FIELD(left, right, st_gid); \
+ BOOST_CHECK_EQUAL_FIELD(left, right, st_rdev); \
+ BOOST_CHECK_EQUAL_FIELD(left, right, st_size); \
+ BOOST_CHECK_EQUAL_FIELD(left, right, st_blksize); \
+ BOOST_CHECK_EQUAL_FIELD(left, right, st_blocks);
+
+static auto
+get_lstat(const std::filesystem::path & p)
+{
+ struct stat st { };
+ BOOST_TEST_INFO(p);
+ BOOST_REQUIRE_EQUAL(::lstat(p.c_str(), &st), 0);
+ return st;
+}
+
+BOOST_FIXTURE_TEST_SUITE(fmp, FuseMountPoint);
+
+BOOST_AUTO_TEST_CASE(fuse, *boost::unit_test::timeout(5))
+{
+ BOOST_REQUIRE(std::filesystem::is_directory(mntpnt));
+}
+
+BOOST_AUTO_TEST_CASE(fuse_ls, *boost::unit_test::timeout(5))
+{
+ static const auto ME_HASH {std::filesystem::hash_value("/me")};
+
+ BOOST_REQUIRE(std::filesystem::is_directory(mntpnt));
+ BOOST_REQUIRE(std::filesystem::is_empty(mntpnt));
+ const auto rpath = mntpnt / "me";
+ const auto lpath = MockDaemonHost::TestExportRoot / "me";
+ BOOST_REQUIRE_NE(lpath, rpath);
+
+ std::filesystem::create_symlink(selfExe, rpath);
+
+ const auto st_local = get_lstat(lpath);
+
+ BOOST_CHECK(!fuseApp.getStatCache().get(ME_HASH));
+
+ std::set<std::filesystem::path> paths(std::filesystem::directory_iterator(mntpnt), {});
+ BOOST_REQUIRE_EQUAL(paths.size(), 1);
+ BOOST_CHECK_EQUAL(paths.begin()->filename(), "me");
+
+ auto cached = fuseApp.getStatCache().get(ME_HASH);
+ BOOST_REQUIRE(cached);
+ const auto & st_cache = (*cached);
+ BOOST_CHECK_EQUAL_STAT(st_cache, st_local);
+
+ const auto st_remote = get_lstat(rpath);
+ BOOST_CHECK_EQUAL_STAT(st_remote, st_local);
+}
+
+BOOST_AUTO_TEST_SUITE_END();
+
+BOOST_AUTO_TEST_CASE(url_params_non)
+{
+ auto fcs = NetFS::FuseApp::configureFromUri("tcp://localhost/foo");
+ BOOST_CHECK_EQUAL(fcs->Async, false);
+}
+
+BOOST_AUTO_TEST_CASE(url_params_1)
+{
+ auto fcs = NetFS::FuseApp::configureFromUri("tcp://localhost/foo?async=1");
+ BOOST_CHECK_EQUAL(fcs->Async, true);
+}
+
+BOOST_AUTO_TEST_CASE(url_params_2)
+{
+ auto fcs = NetFS::FuseApp::configureFromUri("tcp://localhost/foo?0&async=0");
+ BOOST_CHECK_EQUAL(fcs->Async, false);
+}
+
+BOOST_AUTO_TEST_CASE(url_params_hide)
+{
+ auto fcs = NetFS::FuseApp::configureFromUri("tcp://localhost/foo?mapper=hide");
+ BOOST_REQUIRE(fcs->Mapper);
+ BOOST_REQUIRE(std::dynamic_pointer_cast<NetFS::Client::HideUnknownMapperImpl>(fcs->Mapper));
+}
+
+BOOST_AUTO_TEST_CASE(url_params_mask_default)
+{
+ auto fcs = NetFS::FuseApp::configureFromUri("tcp://localhost/foo?mapper=mask");
+ BOOST_REQUIRE(fcs->Mapper);
+ auto mapper = std::dynamic_pointer_cast<NetFS::Client::MaskUnknownMapperImpl>(fcs->Mapper);
+ BOOST_REQUIRE(mapper);
+}
+
+BOOST_AUTO_TEST_CASE(url_params_mask_nondefault)
+{
+ auto fcs = NetFS::FuseApp::configureFromUri(
+ "tcp://localhost/"
+ "foo?mapper=mask&mapper.unknownuser=uu&mapper.unknowngroup=ug&mapper.usermask=0100&mapper.groupmask=0010");
+ BOOST_REQUIRE(fcs->Mapper);
+ auto mapper = std::dynamic_pointer_cast<NetFS::Client::MaskUnknownMapperImpl>(fcs->Mapper);
+ BOOST_REQUIRE(mapper);
+ BOOST_CHECK_EQUAL(mapper->UnknownUser, "uu");
+ BOOST_CHECK_EQUAL(mapper->UnknownGroup, "ug");
+ BOOST_CHECK_EQUAL(mapper->UserMask, 0100);
+ BOOST_CHECK_EQUAL(mapper->GroupMask, 0010);
+}
+
+BOOST_AUTO_TEST_CASE(config_file_hide)
+{
+ auto fcs = NetFS::FuseApp::configureFromFile(rootDir / "defaultFuseHide.xml", "testvol");
+ BOOST_REQUIRE(fcs->Mapper);
+ BOOST_REQUIRE(std::dynamic_pointer_cast<NetFS::Client::HideUnknownMapperImpl>(fcs->Mapper));
+}
+
+BOOST_AUTO_TEST_CASE(config_file_mask)
+{
+ auto fcs = NetFS::FuseApp::configureFromFile(rootDir / "defaultFuseMask.xml", "testvol");
+ BOOST_REQUIRE(fcs->Mapper);
+ auto mapper = std::dynamic_pointer_cast<NetFS::Client::MaskUnknownMapperImpl>(fcs->Mapper);
+ BOOST_REQUIRE(mapper);
+ BOOST_CHECK_EQUAL(mapper->UnknownUser, "uu");
+ BOOST_CHECK_EQUAL(mapper->UnknownGroup, "ug");
+ BOOST_CHECK_EQUAL(mapper->UserMask, 0100);
+ BOOST_CHECK_EQUAL(mapper->GroupMask, 0010);
+}
diff --git a/netfs/unittests/testGlacier.cpp b/netfs/unittests/testGlacier.cpp
index 900a531..afe3475 100644
--- a/netfs/unittests/testGlacier.cpp
+++ b/netfs/unittests/testGlacier.cpp
@@ -1,34 +1,22 @@
#define BOOST_TEST_MODULE TestNetFSGlacier
#include <boost/test/unit_test.hpp>
+
#include "mockDaemon.h"
#include "mockFuse.h"
-#include <boost/scope_exit.hpp>
+#include "mockGlacier.h"
#include <definedDirs.h>
const std::string testEndpoint("tcp -h localhost -p 12013");
-BOOST_AUTO_TEST_CASE ( withRouter )
-{
- BOOST_REQUIRE_EQUAL(0,
- system("/usr/bin/glacier2router --Glacier2.Client.Endpoints='tcp -p 14063' --Glacier2.PermissionsVerifier=Glacier2/NullPermissionsVerifier --daemon --pidfile /tmp/glacier.pid"));
- sleep(1);
-
- BOOST_SCOPE_EXIT(void) {
- BOOST_REQUIRE_EQUAL(0, system("kill $(cat /tmp/glacier.pid)"));
- } BOOST_SCOPE_EXIT_END;
+BOOST_TEST_GLOBAL_FIXTURE(Glacier);
- {
- MockDaemonHost daemon(testEndpoint, {
- "--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string()
- });
- FuseMockHost fuse(testEndpoint, {
- (rootDir / "defaultFuse.xml:testvol").string(),
- (rootDir / "test").string(),
- "--Ice.Default.Router=Glacier2/router:tcp -h localhost -p 14063"
- });
+BOOST_AUTO_TEST_CASE(withRouter)
+{
+ MockDaemonHost daemon(testEndpoint, {"--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string()});
+ FuseMockHost fuse(testEndpoint,
+ {(rootDir / "defaultFuse.xml:testvol").string(), (rootDir / "test").string(),
+ "--Ice.Default.Router=Glacier2/router:tcp -h localhost -p 14063"});
- struct statvfs s;
- BOOST_REQUIRE_EQUAL(0, fuse.fuse->statfs("/", &s));
- }
+ struct statvfs s { };
+ BOOST_CHECK_EQUAL(0, fuse.fuse->statfs("/", &s));
}
-
diff --git a/netfs/unittests/testLib.cpp b/netfs/unittests/testLib.cpp
new file mode 100644
index 0000000..b6d41f2
--- /dev/null
+++ b/netfs/unittests/testLib.cpp
@@ -0,0 +1,206 @@
+#define BOOST_TEST_MODULE TestNetFSLib
+#include <boost/test/data/test_case.hpp>
+#include <boost/test/unit_test.hpp>
+#include <defaultMapper.h>
+#include <entCache.impl.h>
+#include <fuseMappersImpl.h>
+#include <lockHelpers.h>
+
+struct TestEntry {
+ TestEntry(int i, std::string n) : id(i), name(std::move(n)) { }
+
+ int id;
+ std::string name;
+};
+
+struct TestEntCache : public EntCache<TestEntry> {
+ void
+ fillCache() const noexcept override
+ {
+ idcache->insert(std::make_shared<TestEntry>(1, "user1"));
+ idcache->insert(std::make_shared<TestEntry>(2, "user2"));
+ idcache->insert(std::make_shared<TestEntry>(3, "user3"));
+ }
+};
+
+const auto GoodIds = boost::unit_test::data::make({1, 2, 3});
+const auto BadIds = boost::unit_test::data::make({0, -1, 10});
+const auto GoodNames = boost::unit_test::data::make({"user1", "user2", "user3"});
+const auto BadNames = boost::unit_test::data::make({"", "bad", "user4"});
+
+BOOST_FIXTURE_TEST_SUITE(tec, TestEntCache);
+
+BOOST_DATA_TEST_CASE(notfoundid, BadIds, id)
+{
+ BOOST_CHECK(!getEntry(id));
+}
+
+BOOST_DATA_TEST_CASE(notfoundname, BadNames, name)
+{
+ BOOST_CHECK(!getEntry(name));
+}
+
+BOOST_DATA_TEST_CASE(foundid, GoodNames ^ GoodIds, name, id)
+{
+ BOOST_CHECK_EQUAL(getEntry(id)->name, name);
+}
+
+BOOST_DATA_TEST_CASE(foundname, GoodNames ^ GoodIds, name, id)
+{
+ BOOST_CHECK_EQUAL(getEntry(name)->id, id);
+}
+
+BOOST_AUTO_TEST_SUITE_END();
+
+BOOST_AUTO_TEST_CASE(group_membership)
+{
+ Group g(0, "root");
+ g.members.insert(0);
+ g.members.insert(1);
+ g.members.insert(5);
+
+ BOOST_REQUIRE_EQUAL(3, g.members.size());
+ BOOST_CHECK(g.hasMember(0));
+ BOOST_CHECK(g.hasMember(1));
+ BOOST_CHECK(g.hasMember(5));
+ BOOST_CHECK(!g.hasMember(3));
+ BOOST_CHECK(!g.hasMember(6));
+}
+
+// These tests make some assumptions about local system users
+BOOST_FIXTURE_TEST_CASE(usercache, UserEntCache)
+{
+ auto root = getEntry(0);
+ BOOST_REQUIRE(root);
+ BOOST_CHECK_EQUAL(root->id, 0);
+ BOOST_CHECK_EQUAL(root->name, "root");
+ BOOST_CHECK_EQUAL(root->group, 0);
+
+ auto rootByName = getEntry(root->name);
+ BOOST_REQUIRE_EQUAL(root, rootByName);
+}
+
+struct TestGroupEntCache : public GroupEntCache {
+ TestGroupEntCache() : GroupEntCache(std::make_shared<UserEntCache>()) { }
+};
+
+BOOST_FIXTURE_TEST_CASE(groupcache, TestGroupEntCache)
+{
+ auto root = getEntry(0);
+ BOOST_REQUIRE(root);
+ BOOST_CHECK_EQUAL(root->id, 0);
+ BOOST_CHECK_EQUAL(root->name, "root");
+ BOOST_CHECK_EQUAL(root->members.size(), 1);
+ BOOST_CHECK(root->hasMember(0));
+
+ auto rootByName = getEntry(root->name);
+ BOOST_REQUIRE_EQUAL(root, rootByName);
+}
+
+BOOST_FIXTURE_TEST_SUITE(dm, NetFS::Mapping::DefaultMapper);
+
+BOOST_AUTO_TEST_CASE(good_maptransport)
+{
+ auto fs = mapTransport("root", "root");
+ BOOST_CHECK_EQUAL(0, fs.uid);
+ BOOST_CHECK_EQUAL(0, fs.gid);
+ BOOST_CHECK_EQUAL(0, fs.mask);
+}
+
+BOOST_AUTO_TEST_CASE(good_mapfs)
+{
+ auto tn = mapFileSystem(0, 0);
+ BOOST_CHECK_EQUAL("root", tn.username);
+ BOOST_CHECK_EQUAL("root", tn.groupname);
+}
+
+BOOST_AUTO_TEST_CASE(bad_maptransport)
+{
+ BOOST_REQUIRE_THROW(mapTransport("root", ""), NetFS::SystemError);
+ BOOST_REQUIRE_THROW(mapTransport("", "root"), NetFS::SystemError);
+ BOOST_REQUIRE_THROW(mapTransport("", ""), NetFS::SystemError);
+}
+
+BOOST_AUTO_TEST_CASE(bad_mapfilesystem)
+{
+ BOOST_REQUIRE_THROW(mapFileSystem(0, 99999999), NetFS::SystemError);
+ BOOST_REQUIRE_THROW(mapFileSystem(99999999, 0), NetFS::SystemError);
+ BOOST_REQUIRE_THROW(mapFileSystem(99999999, 99999999), NetFS::SystemError);
+}
+
+BOOST_AUTO_TEST_SUITE_END();
+
+BOOST_FIXTURE_TEST_SUITE(hm, NetFS::Client::HideUnknownMapperImpl);
+
+BOOST_AUTO_TEST_CASE(good_maptransport)
+{
+ auto fs = mapTransport("root", "root");
+ BOOST_CHECK_EQUAL(0, fs.uid);
+ BOOST_CHECK_EQUAL(0, fs.gid);
+ BOOST_CHECK_EQUAL(0, fs.mask);
+}
+
+BOOST_AUTO_TEST_CASE(good_mapfs)
+{
+ auto tn = mapFileSystem(0, 0);
+ BOOST_CHECK_EQUAL("root", tn.username);
+ BOOST_CHECK_EQUAL("root", tn.groupname);
+}
+
+BOOST_AUTO_TEST_CASE(bad_maptransport)
+{
+ BOOST_CHECK_EQUAL(mapTransport("root", "").mask, ~0);
+ BOOST_CHECK_EQUAL(mapTransport("", "root").mask, ~0);
+ BOOST_CHECK_EQUAL(mapTransport("", "").mask, ~0);
+}
+
+BOOST_AUTO_TEST_CASE(bad_mapfilesystem)
+{
+ BOOST_REQUIRE_THROW(mapFileSystem(0, 99999999), NetFS::SystemError);
+ BOOST_REQUIRE_THROW(mapFileSystem(99999999, 0), NetFS::SystemError);
+ BOOST_REQUIRE_THROW(mapFileSystem(99999999, 99999999), NetFS::SystemError);
+}
+
+BOOST_AUTO_TEST_SUITE_END();
+
+BOOST_FIXTURE_TEST_SUITE(mm, NetFS::Client::MaskUnknownMapperImpl);
+
+BOOST_AUTO_TEST_CASE(good_maptransport)
+{
+ auto fs = mapTransport("root", "root");
+ BOOST_CHECK_EQUAL(0, fs.uid);
+ BOOST_CHECK_EQUAL(0, fs.gid);
+ BOOST_CHECK_EQUAL(0, fs.mask);
+}
+
+BOOST_AUTO_TEST_CASE(good_mapfs)
+{
+ auto tn = mapFileSystem(0, 0);
+ BOOST_CHECK_EQUAL("root", tn.username);
+ BOOST_CHECK_EQUAL("root", tn.groupname);
+}
+
+BOOST_AUTO_TEST_CASE(bad_maptransport)
+{
+ BOOST_CHECK_EQUAL(mapTransport("root", "").mask, 0070);
+ BOOST_CHECK_EQUAL(mapTransport("", "root").mask, 0700);
+ BOOST_CHECK_EQUAL(mapTransport("", "").mask, 0770);
+}
+
+BOOST_AUTO_TEST_CASE(bad_maptransport_badfallback)
+{
+ UnknownUser = "not existing";
+ UnknownGroup = "not existing";
+ BOOST_REQUIRE_THROW(mapTransport("root", ""), NetFS::SystemError);
+ BOOST_REQUIRE_THROW(mapTransport("", "root"), NetFS::SystemError);
+ BOOST_REQUIRE_THROW(mapTransport("", ""), NetFS::SystemError);
+}
+
+BOOST_AUTO_TEST_CASE(bad_mapfilesystem)
+{
+ BOOST_REQUIRE_THROW(mapFileSystem(0, 99999999), NetFS::SystemError);
+ BOOST_REQUIRE_THROW(mapFileSystem(99999999, 0), NetFS::SystemError);
+ BOOST_REQUIRE_THROW(mapFileSystem(99999999, 99999999), NetFS::SystemError);
+}
+
+BOOST_AUTO_TEST_SUITE_END();
diff --git a/netfs/unittests/testPerf.cpp b/netfs/unittests/testPerf.cpp
new file mode 100644
index 0000000..eff3ae4
--- /dev/null
+++ b/netfs/unittests/testPerf.cpp
@@ -0,0 +1,133 @@
+#include "mockDaemon.h"
+#include "mockFuse.h"
+#include <algorithm>
+#include <benchmark/benchmark.h>
+#include <climits>
+#include <compileTimeFormatter.h>
+#include <definedDirs.h>
+#include <filesystem>
+#include <fuseFiles.h>
+
+const std::filesystem::path tmpDir {getenv("XDG_RUNTIME_DIR") / selfExe.filename()};
+const std::string testEndpoint("tcp -h localhost -p 12016");
+const std::string testUri("tcp://localhost:12016/testvol");
+
+class Core {
+public:
+ Core() :
+ daemonHost(testEndpoint,
+ {"--Ice.ThreadPool.Client.Size=4", "--Ice.ThreadPool.Client.SizeMax=20",
+ "--Ice.ThreadPool.Server.Size=5", "--Ice.ThreadPool.Server.SizeMax=20",
+ "--NetFSD.ConfigPath=" + (rootDir / "defaultDaemon.xml").string()})
+ {
+ }
+
+protected:
+ MockDaemonHost daemonHost;
+
+public:
+};
+
+Core globalCore;
+
+class Fuse : public FuseMockHost, public benchmark::Fixture {
+public:
+ Fuse() :
+ FuseMockHost(testEndpoint,
+ {"--Ice.ThreadPool.Client.Size=6", "--Ice.ThreadPool.Client.SizeMax=20",
+ (rootDir / "defaultFuse.xml").string() + ":testvol", (tmpDir / "test").string()})
+ {
+ }
+
+ static int
+ nameListDiscard(void *, const char *, const struct stat *, off_t, fuse_fill_dir_flags)
+ {
+ return 0;
+ }
+};
+
+BENCHMARK_F(Fuse, statfs_root)(benchmark::State & state)
+{
+ struct statvfs st { };
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(fuse->statfs("/", &st));
+ }
+}
+
+BENCHMARK_F(Fuse, getattr_root)(benchmark::State & state)
+{
+ struct stat st { };
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(fuse->getattr("/", &st, nullptr));
+ }
+}
+
+BENCHMARK_F(Fuse, openclose_dir_root)(benchmark::State & state)
+{
+ for (auto _ : state) {
+ fuse_file_info fi {};
+ benchmark::DoNotOptimize(fuse->opendir("/", &fi));
+ benchmark::DoNotOptimize(fuse->releasedir("/", &fi));
+ }
+}
+
+AdHocFormatter(numbered_filename, "/files/test-%?.txt");
+AdHocFormatter(numbered_dir, "/dirs/test-%?");
+
+BENCHMARK_F(Fuse, create_files)(benchmark::State & state)
+{
+ fuse->mkdir("/files", 0700);
+ unsigned int n {};
+ for (auto _ : state) {
+ fuse_file_info fi {};
+ const std::string filename {numbered_filename::get(n++)};
+ benchmark::DoNotOptimize(fuse->create(filename.c_str(), 0600, &fi));
+ benchmark::DoNotOptimize(fuse->release(filename.c_str(), &fi));
+ }
+}
+
+BENCHMARK_F(Fuse, create_dirs)(benchmark::State & state)
+{
+ fuse->mkdir("/dirs", 0700);
+ unsigned int n {};
+ for (auto _ : state) {
+ const std::string dirname {numbered_dir::get(n++)};
+ benchmark::DoNotOptimize(fuse->mkdir(dirname.c_str(), 0600));
+ }
+}
+
+BENCHMARK_F(Fuse, ls_files)(benchmark::State & state)
+{
+ fuse_file_info fi {};
+ fuse->opendir("/files", &fi);
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(fuse->readdir("/files", nullptr, nameListDiscard, 0, &fi, FUSE_READDIR_PLUS));
+ }
+ fuse->releasedir("/", &fi);
+}
+
+BENCHMARK_F(Fuse, write_file)(benchmark::State & state)
+{
+ fuse_file_info fi {};
+ fuse->open("/files/test-0.txt", &fi);
+ std::array<char, 1024> data {};
+ off_t off {};
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(fuse->write("/files/test-0.txt", data.data(), data.size(), off, &fi));
+ off += data.size();
+ }
+ fuse->release("/", &fi);
+}
+
+BENCHMARK_F(Fuse, read_file)(benchmark::State & state)
+{
+ fuse_file_info fi {};
+ fuse->open("/files/test-0.txt", &fi);
+ std::array<char, 1024> data {};
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(fuse->read("/files/test-0.txt", data.data(), data.size(), 0, &fi));
+ }
+ fuse->release("/", &fi);
+}
+
+BENCHMARK_MAIN();
diff --git a/netfs/unittests/thread-suppressions.txt b/netfs/unittests/thread-suppressions.txt
new file mode 100644
index 0000000..95c4982
--- /dev/null
+++ b/netfs/unittests/thread-suppressions.txt
@@ -0,0 +1,5 @@
+# Unmount Fuse filesystem while daemon is reading socket (expected)
+race:fmp::Run::~Run
+
+# Pfft... no idea
+race:IceInternal::PromiseOutgoing