summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2019-07-20 10:53:14 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2019-07-20 10:53:14 +0100
commit4c25da8a7ba7437657c3e2da592145113d3532f4 (patch)
tree40f0468135089ff4f9e967bf8ee74b020bee67f7
downloadnetfs-gitfs-4c25da8a7ba7437657c3e2da592145113d3532f4.tar.bz2
netfs-gitfs-4c25da8a7ba7437657c3e2da592145113d3532f4.tar.xz
netfs-gitfs-4c25da8a7ba7437657c3e2da592145113d3532f4.zip
Initial commit
Not a lot of stuff, hard coded paths, tests against /usr/portage
-rw-r--r--.gitignore1
-rw-r--r--Jamroot.jam31
-rw-r--r--src/Jamfile.jam12
-rw-r--r--src/blob.cpp49
-rw-r--r--src/blob.h27
-rw-r--r--src/dir.cpp30
-rw-r--r--src/dir.h24
-rw-r--r--src/git.cpp30
-rw-r--r--src/git.h91
-rw-r--r--src/main.cpp28
-rw-r--r--src/repo.cpp174
-rw-r--r--src/repo.h42
-rw-r--r--src/repoList.cpp16
-rw-r--r--src/repoList.h17
-rw-r--r--unittests/Jamfile.jam29
-rw-r--r--unittests/core.cpp69
-rw-r--r--unittests/mockDefs.cpp26
-rw-r--r--unittests/mockDefs.h31
18 files changed, 727 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ba077a4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+bin
diff --git a/Jamroot.jam b/Jamroot.jam
new file mode 100644
index 0000000..a2d56a8
--- /dev/null
+++ b/Jamroot.jam
@@ -0,0 +1,31 @@
+import os ;
+using gcc : : [ os.environ CXX ] ;
+
+variant coverage : debug ;
+
+project
+ : requirements
+ <define>ICE_CPP11_MAPPING
+ <cxxflags>"-std=c++17"
+ <cxxflags>"-fvisibility=hidden -fvisibility-inlines-hidden"
+ <linkflags>"-Wl,-z,defs,--warn-once,--gc-sections"
+ <variant>release:<cxxflags>"-flto=2"
+ <variant>release:<linkflags>"-flto=2"
+ <variant>debug:<cxxflags>"-W -Wall -Wextra -Werror -Wwrite-strings"
+ <variant>coverage:<cxxflags>"--coverage"
+ <variant>coverage:<linkflags>"--coverage"
+ ;
+
+lib adhocutil : : : : <include>/usr/include/adhocutil ;
+lib dbppcore : : : : <include>/usr/include/dbpp ;
+lib git2 ;
+lib Ice ;
+lib Ice++11 ;
+lib IceBox++11 ;
+lib icetray : : : : <include>/usr/include/icetray <library>dbppcore ;
+lib netfs-api : : : : <include>/usr/include/netfs <library>Ice++11 <library>IceBox++11 <library>pthread ;
+lib pthread ;
+
+build-project src ;
+build-project unittests ;
+
diff --git a/src/Jamfile.jam b/src/Jamfile.jam
new file mode 100644
index 0000000..db6267c
--- /dev/null
+++ b/src/Jamfile.jam
@@ -0,0 +1,12 @@
+
+lib gitfs :
+ [ glob *.cpp ]
+ :
+ <library>..//adhocutil
+ <library>..//icetray
+ <library>..//git2
+ <library>..//netfs-api
+ : :
+ <library>..//netfs-api
+ ;
+
diff --git a/src/blob.cpp b/src/blob.cpp
new file mode 100644
index 0000000..8fb0509
--- /dev/null
+++ b/src/blob.cpp
@@ -0,0 +1,49 @@
+#include <Ice/ObjectAdapter.h>
+#include "blob.h"
+
+GitFS::Blob::Blob(const GitFS::Git::RepositoryPtr & r, const GitFS::Git::TreeEntryPtr & te) :
+ blob(Git::BlobLookup(r, *git_tree_entry_id(te.get()))),
+ blobSize(git_blob_rawsize(blob.get())),
+ blobContent(static_cast<const char *>(git_blob_rawcontent(blob.get())))
+{
+}
+
+void
+GitFS::Blob::close(const ::Ice::Current& current)
+{
+ current.adapter->remove(current.id);
+}
+
+
+NetFS::Attr
+GitFS::Blob::fgetattr(ReqEnv, const ::Ice::Current&)
+{
+ return {};
+}
+
+
+NetFS::Buffer
+GitFS::Blob::read(long long int offset, long long int size, const ::Ice::Current&)
+{
+ if (offset > blobSize) {
+ return {};
+ }
+ auto len = std::min(blobSize - offset, size);
+ return NetFS::Buffer(blobContent + offset, blobContent + offset + len);
+}
+
+
+void
+GitFS::Blob::ftruncate(ReqEnv, long long int, const ::Ice::Current&)
+{
+ throw NetFS::SystemError(EROFS);
+}
+
+
+void
+GitFS::Blob::write(long long int, long long int, Buffer, const ::Ice::Current&)
+{
+ throw NetFS::SystemError(EROFS);
+}
+
+
diff --git a/src/blob.h b/src/blob.h
new file mode 100644
index 0000000..0a2a77d
--- /dev/null
+++ b/src/blob.h
@@ -0,0 +1,27 @@
+#ifndef GITFS_BLOB_H
+#define GITFS_BLOB_H
+
+#include <file.h>
+#include "git.h"
+
+namespace GitFS {
+ using namespace NetFS;
+ class Blob : public File {
+ public:
+ Blob(const GitFS::Git::RepositoryPtr & r, const Git::TreeEntryPtr &);
+
+ void close(const ::Ice::Current& current) override;
+ Attr fgetattr(ReqEnv env, const ::Ice::Current& current) override;
+ Buffer read(long long int offset, long long int size, const ::Ice::Current& current) override;
+ void ftruncate(ReqEnv env, long long int size, const ::Ice::Current& current) override;
+ void write(long long int offset, long long int size, Buffer data, const ::Ice::Current& current) override;
+
+ private:
+ Git::BlobPtr blob;
+ const decltype(git_blob_rawsize({})) blobSize;
+ const char * const blobContent;
+ };
+}
+
+#endif
+
diff --git a/src/dir.cpp b/src/dir.cpp
new file mode 100644
index 0000000..8a9fd86
--- /dev/null
+++ b/src/dir.cpp
@@ -0,0 +1,30 @@
+#include <Ice/ObjectAdapter.h>
+#include "dir.h"
+
+GitFS::Directory::Directory(const GitFS::Git::TreePtr & t, const std::string & p) :
+ tree(t),
+ path(p)
+{
+}
+
+void
+GitFS::Directory::close(const ::Ice::Current& current)
+{
+ current.adapter->remove(current.id);
+}
+
+
+NetFS::NameList
+GitFS::Directory::readdir(const ::Ice::Current&)
+{
+ return {};
+}
+
+
+NetFS::DirectoryContents
+GitFS::Directory::listdir(const ::Ice::Current&)
+{
+ return {};
+}
+
+
diff --git a/src/dir.h b/src/dir.h
new file mode 100644
index 0000000..74afde3
--- /dev/null
+++ b/src/dir.h
@@ -0,0 +1,24 @@
+#ifndef GITFS_DIRECTORY_H
+#define GITFS_DIRECTORY_H
+
+#include <directory.h>
+#include "git.h"
+
+namespace GitFS {
+ using namespace NetFS;
+ class Directory : public DirectoryV2 {
+ public:
+ Directory(const GitFS::Git::TreePtr & r, const std::string &);
+
+ void close(const ::Ice::Current& current) override;
+ NameList readdir(const ::Ice::Current& current) override;
+ DirectoryContents listdir(const ::Ice::Current& current) override;
+
+ private:
+ Git::TreePtr tree;
+ const std::string path;
+ };
+}
+
+#endif
+
diff --git a/src/git.cpp b/src/git.cpp
new file mode 100644
index 0000000..084e6ca
--- /dev/null
+++ b/src/git.cpp
@@ -0,0 +1,30 @@
+#include "git.h"
+#include <execinfo.h>
+#include <exceptions.h>
+
+namespace GitFS {
+ namespace Git {
+ void
+ throwError(int err)
+ {
+#if LIBGIT2_VER_MINOR >= 28
+ const git_error * e = git_error_last();
+#else
+ const git_error * e = giterr_last();
+#endif
+ throw Error { err, e->klass, e->message };
+ }
+ }
+}
+
+namespace std {
+ std::ostream &
+ operator<<(std::ostream & s, const git_oid & oid)
+ {
+ char str[GIT_OID_HEXSZ + 1];
+ git_oid_tostr(str, sizeof(str), &oid);
+ s.write(str, GIT_OID_HEXSZ);
+ return s;
+ }
+}
+
diff --git a/src/git.h b/src/git.h
new file mode 100644
index 0000000..c0ae4e3
--- /dev/null
+++ b/src/git.h
@@ -0,0 +1,91 @@
+#ifndef GITFS_GIT_H
+#define GITFS_GIT_H
+
+#include <memory>
+#include <git2.h>
+#include <ostream>
+
+namespace GitFS {
+ namespace Git {
+ struct Error {
+ int err;
+ int klass;
+ const char * message;
+ };
+ void throwError(int err);
+
+ template<typename ... P, typename ... A>
+ void
+ gitSafe(int (*func)(P...), A ... p)
+ {
+ if (int _giterror = func(p...); _giterror != 0) {
+ throwError(_giterror);
+ }
+ }
+
+ template<typename T>
+ using TPtr = std::shared_ptr<T>;
+
+ template<typename R, typename ... P, typename ... A>
+ auto
+ gitSafeGet(int(*get)(R**, P...), void(*release)(R*), A ... p)
+ {
+ R * r = nullptr;
+ gitSafe(get, &r, p...);
+ return TPtr<R>(r, release);
+ }
+
+ template<typename R, typename ... P, typename ... A>
+ auto
+ gitSafeGet(int(*get)(R**, P...), A ... p)
+ {
+ R * r = nullptr;
+ gitSafe(get, &r, p...);
+ return r;
+ }
+
+ inline auto OidParse(const std::string_view & str)
+ {
+ git_oid oid;
+ gitSafe(git_oid_fromstrn, &oid, str.data(), str.length());
+ return oid;
+ }
+
+ inline auto RepositoryOpenBare(const std::string & path)
+ {
+ return gitSafeGet(git_repository_open_bare, git_repository_free, path.c_str());
+ }
+ using RepositoryPtr = decltype(RepositoryOpenBare(""));
+
+ inline auto BlobLookup(const RepositoryPtr & repo, const git_oid & blob)
+ {
+ return gitSafeGet(git_blob_lookup, git_blob_free, repo.get(), &blob);
+ }
+ using BlobPtr = decltype(BlobLookup({}, {}));
+
+ inline auto CommitLookup(const RepositoryPtr & repo, const git_oid & commitId)
+ {
+ return gitSafeGet(git_commit_lookup, git_commit_free, repo.get(), &commitId);
+ }
+ using CommitPtr = decltype(CommitLookup({}, {}));
+
+ inline auto TreeLookup(const RepositoryPtr & repo, const git_oid & treeId)
+ {
+ return gitSafeGet(git_tree_lookup, git_tree_free, repo.get(), &treeId);
+ }
+ using TreePtr = decltype(TreeLookup({}, {}));
+
+ inline auto TreeEntryByPath(const TreePtr & tree, const std::string & path)
+ {
+ return gitSafeGet(git_tree_entry_bypath, git_tree_entry_free, tree.get(), path.c_str() + 1);
+ }
+ using TreeEntryPtr = decltype(TreeEntryByPath({}, {}));
+ }
+}
+
+namespace std {
+ std::ostream & operator<<(std::ostream &, const git_oid &);
+}
+
+#endif
+
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..0666a3c
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,28 @@
+#include <icetrayService.h>
+#include <git2.h>
+#include <factory.h>
+#include <icecube.h>
+#include "repoList.h"
+
+namespace GitFS {
+ class Main : public IceTray::Service {
+ public:
+ Main()
+ {
+ git_libgit2_init();
+ }
+
+ ~Main()
+ {
+ git_libgit2_shutdown();
+ }
+
+ void addObjects(const std::string &, const Ice::CommunicatorPtr & ic, const Ice::StringSeq &, const Ice::ObjectAdapterPtr & adp) override
+ {
+ IceTray::Cube::addObject<NetFS::Service, RepoList>(adp, "Service", ic->getProperties());
+ }
+ };
+
+ NAMEDFACTORY("default", Main, IceTray::ServiceFactory);
+}
+
diff --git a/src/repo.cpp b/src/repo.cpp
new file mode 100644
index 0000000..299f2ce
--- /dev/null
+++ b/src/repo.cpp
@@ -0,0 +1,174 @@
+#include <Ice/ObjectAdapter.h>
+#include <sys/stat.h>
+#include "repo.h"
+#include "blob.h"
+#include "dir.h"
+
+GitFS::Repo::Repo() :
+ repo(Git::RepositoryOpenBare("/usr/portage/.git")),
+ commit(Git::CommitLookup(repo, Git::OidParse("97a435f30472eb941f96234d274b3aeb492e2793"))),
+ tree(Git::TreeLookup(repo, *git_commit_tree_id(commit.get())))
+{
+}
+
+void
+GitFS::Repo::disconnect(const ::Ice::Current& current)
+{
+ current.adapter->remove(current.id);
+}
+
+
+NetFS::DirectoryPrxPtr
+GitFS::Repo::opendir(ReqEnv, ::std::string path, const ::Ice::Current& ice)
+{
+ return Ice::uncheckedCast<NetFS::DirectoryV2Prx>(ice.adapter->addWithUUID(
+ std::make_shared<Directory>(tree, path)));
+}
+
+
+NetFS::VFS
+GitFS::Repo::statfs(ReqEnv, ::std::string, const ::Ice::Current&)
+{
+ return {};
+}
+
+
+int
+GitFS::Repo::access(ReqEnv, ::std::string path, int mode, const ::Ice::Current&)
+{
+ if (mode & W_OK) return EACCES;
+ if (path.empty()) return EINVAL;
+ if (path == "/") return 0;
+
+ try {
+ auto e = Git::TreeEntryByPath(tree, path);
+ const auto emode = git_tree_entry_filemode(e.get());
+
+ if (S_ISDIR(emode)) return 0;
+ if (mode & R_OK && !(S_IRUSR & emode)) return EACCES;
+ if (mode & X_OK && !(S_IXUSR & emode)) return EACCES;
+
+ return 0;
+ }
+ catch (const Git::Error & e) {
+ if (e.err == GIT_ENOTFOUND) {
+ return ENOENT;
+ }
+ throw NetFS::SystemError(EIO);
+ }
+}
+
+
+NetFS::Attr
+GitFS::Repo::getattr(ReqEnv, ::std::string path, const ::Ice::Current&)
+{
+ NetFS::Attr a {};
+ (void)path;
+ a.ctime = a.atime = a.mtime = git_commit_time(commit.get());
+ return a;
+}
+
+
+::std::string
+GitFS::Repo::readlink(ReqEnv, ::std::string path, const ::Ice::Current&)
+{
+ auto e = Git::TreeEntryByPath(tree, path);
+ if (!e) throw NetFS::SystemError(ENOENT);
+ const auto emode = git_tree_entry_filemode(e.get());
+ if (!S_ISLNK(emode)) throw NetFS::SystemError(EINVAL);
+ return {};
+}
+
+
+NetFS::FilePrxPtr
+GitFS::Repo::open(ReqEnv, ::std::string path, int, const ::Ice::Current& ice)
+{
+ return Ice::uncheckedCast<NetFS::FilePrx>(ice.adapter->addWithUUID(
+ std::make_shared<Blob>(repo, Git::TreeEntryByPath(tree, path))));
+}
+
+
+NetFS::FilePrxPtr
+GitFS::Repo::create(ReqEnv, ::std::string, int, int, const ::Ice::Current&)
+{
+ throw NetFS::SystemError(EROFS);
+}
+
+
+void
+GitFS::Repo::truncate(ReqEnv, ::std::string, long long int, const ::Ice::Current&)
+{
+ throw NetFS::SystemError(EROFS);
+}
+
+
+void
+GitFS::Repo::unlink(ReqEnv, ::std::string, const ::Ice::Current&)
+{
+ throw NetFS::SystemError(EROFS);
+}
+
+
+void
+GitFS::Repo::mkdir(ReqEnv, ::std::string, int, const ::Ice::Current&)
+{
+ throw NetFS::SystemError(EROFS);
+}
+
+
+void
+GitFS::Repo::rmdir(ReqEnv, ::std::string, const ::Ice::Current&)
+{
+ throw NetFS::SystemError(EROFS);
+}
+
+
+void
+GitFS::Repo::mknod(ReqEnv, ::std::string, int, int, const ::Ice::Current&)
+{
+ throw NetFS::SystemError(EROFS);
+}
+
+
+void
+GitFS::Repo::symlink(ReqEnv, ::std::string, ::std::string, const ::Ice::Current&)
+{
+ throw NetFS::SystemError(EROFS);
+}
+
+
+void
+GitFS::Repo::link(ReqEnv, ::std::string, ::std::string, const ::Ice::Current&)
+{
+ throw NetFS::SystemError(EROFS);
+}
+
+
+void
+GitFS::Repo::rename(ReqEnv, ::std::string, ::std::string, const ::Ice::Current&)
+{
+ throw NetFS::SystemError(EROFS);
+}
+
+
+void
+GitFS::Repo::chmod(ReqEnv, ::std::string, int, const ::Ice::Current&)
+{
+ throw NetFS::SystemError(EROFS);
+}
+
+
+void
+GitFS::Repo::chown(ReqEnv, ::std::string, int, int, const ::Ice::Current&)
+{
+ throw NetFS::SystemError(EROFS);
+}
+
+
+void
+GitFS::Repo::utimens(ReqEnv, ::std::string, long long int, long long int, long long int, long long int, const ::Ice::Current&)
+{
+ throw NetFS::SystemError(EROFS);
+}
+
+
diff --git a/src/repo.h b/src/repo.h
new file mode 100644
index 0000000..e6f7d94
--- /dev/null
+++ b/src/repo.h
@@ -0,0 +1,42 @@
+#ifndef GITFS_REPO_H
+#define GITFS_REPO_H
+
+#include <volume.h>
+#include "git.h"
+
+namespace GitFS {
+ using namespace NetFS;
+ class Repo : public Volume {
+ public:
+ Repo();
+
+ void disconnect(const ::Ice::Current& current) override;
+ DirectoryPrxPtr opendir(ReqEnv env, ::std::string path, const ::Ice::Current& current) override;
+ VFS statfs(ReqEnv env, ::std::string path, const ::Ice::Current& current) override;
+ int access(ReqEnv env, ::std::string path, int mode, const ::Ice::Current& current) override;
+ Attr getattr(ReqEnv env, ::std::string path, const ::Ice::Current& current) override;
+ ::std::string readlink(ReqEnv env, ::std::string path, const ::Ice::Current& current) override;
+ FilePrxPtr open(ReqEnv env, ::std::string path, int flags, const ::Ice::Current& current) override;
+ FilePrxPtr create(ReqEnv env, ::std::string path, int flags, int mode, const ::Ice::Current& current) override;
+ void truncate(ReqEnv env, ::std::string path, long long int size, const ::Ice::Current& current) override;
+ void unlink(ReqEnv env, ::std::string path, const ::Ice::Current& current) override;
+ void mkdir(ReqEnv env, ::std::string path, int mode, const ::Ice::Current& current) override;
+ void rmdir(ReqEnv env, ::std::string path, const ::Ice::Current& current) override;
+ void mknod(ReqEnv env, ::std::string path, int mode, int dev, const ::Ice::Current& current) override;
+ void symlink(ReqEnv env, ::std::string path1, ::std::string path2, const ::Ice::Current& current) override;
+ void link(ReqEnv env, ::std::string path1, ::std::string path2, const ::Ice::Current& current) override;
+ void rename(ReqEnv env, ::std::string from, ::std::string to, const ::Ice::Current& current) override;
+ void chmod(ReqEnv env, ::std::string path, int mode, const ::Ice::Current& current) override;
+ void chown(ReqEnv env, ::std::string path, int uid, int gid, const ::Ice::Current& current) override;
+ void utimens(ReqEnv env, ::std::string path, long long int atime, long long int atimens, long long int mtime, long long int mtimens, const ::Ice::Current& current) override;
+
+ private:
+ Git::RepositoryPtr repo;
+ Git::CommitPtr commit;
+ Git::TreePtr tree;
+ };
+}
+
+#endif
+
+
diff --git a/src/repoList.cpp b/src/repoList.cpp
new file mode 100644
index 0000000..0ee92c5
--- /dev/null
+++ b/src/repoList.cpp
@@ -0,0 +1,16 @@
+#include <Ice/ObjectAdapter.h>
+#include "repoList.h"
+#include "repo.h"
+
+GitFS::RepoList::RepoList(const Ice::PropertiesPtr &)
+{
+
+}
+
+NetFS::VolumePrxPtr
+GitFS::RepoList::connect(const ::std::string, const ::std::string,
+ const ::Ice::Current & ice)
+{
+ return Ice::uncheckedCast<NetFS::VolumePrx>(ice.adapter->addWithUUID(std::make_shared<Repo>()));
+}
+
diff --git a/src/repoList.h b/src/repoList.h
new file mode 100644
index 0000000..ac8b885
--- /dev/null
+++ b/src/repoList.h
@@ -0,0 +1,17 @@
+#ifndef GITFS_REPOLIST_H
+#define GITFS_REPOLIST_H
+
+#include <service.h>
+#include <Ice/Properties.h>
+
+namespace GitFS {
+ class RepoList : public NetFS::Service {
+ public:
+ RepoList(const Ice::PropertiesPtr & ptr);
+
+ virtual NetFS::VolumePrxPtr connect(const ::std::string volume, const ::std::string auth, const ::Ice::Current& current) override;
+ };
+}
+
+#endif
+
diff --git a/unittests/Jamfile.jam b/unittests/Jamfile.jam
new file mode 100644
index 0000000..9de6ea6
--- /dev/null
+++ b/unittests/Jamfile.jam
@@ -0,0 +1,29 @@
+import testing ;
+
+lib boost_utf : : <name>boost_unit_test_framework ;
+lib dryice : : : : <library>..//icetray ;
+
+path-constant me : . ;
+
+lib common :
+ mockDefs.cpp
+ :
+ <library>dryice
+ <library>../src//gitfs
+ <library>..//adhocutil
+ <library>boost_utf
+ <define>ROOT=\"$(me)\"
+ : :
+ <library>dryice
+ <library>../src//gitfs
+ <library>boost_utf
+ <define>ROOT=\"$(me)\"
+ <define>BOOST_TEST_DYN_LINK
+ ;
+
+
+run core.cpp : : :
+ <library>../src//gitfs
+ <library>common
+ ;
+
diff --git a/unittests/core.cpp b/unittests/core.cpp
new file mode 100644
index 0000000..9b38dfe
--- /dev/null
+++ b/unittests/core.cpp
@@ -0,0 +1,69 @@
+#define BOOST_TEST_MODULE GitFS_Core
+#include <boost/test/unit_test.hpp>
+
+#include "mockDefs.h"
+#include "sys/stat.h"
+
+using namespace GitFS;
+using namespace GitFS::Test;
+
+BOOST_TEST_GLOBAL_FIXTURE(Service);
+
+BOOST_FIXTURE_TEST_SUITE(client, Client)
+
+ // TODO
+
+BOOST_AUTO_TEST_SUITE_END();
+
+BOOST_FIXTURE_TEST_SUITE(volume, VolumeClient)
+
+BOOST_AUTO_TEST_CASE( access )
+{
+ // Noting is writable
+ // Directories are all readable and executable
+
+ BOOST_CHECK_EQUAL(EINVAL, v->access(env, "", R_OK));
+
+ BOOST_CHECK_EQUAL(0, v->access(env, "/", R_OK));
+ BOOST_CHECK_EQUAL(EACCES, v->access(env, "/", W_OK));
+ BOOST_CHECK_EQUAL(0, v->access(env, "/", X_OK));
+
+ BOOST_CHECK_EQUAL(0, v->access(env, "/metadata", R_OK));
+ BOOST_CHECK_EQUAL(EACCES, v->access(env, "/metadata", W_OK));
+ BOOST_CHECK_EQUAL(0, v->access(env, "/metadata", X_OK));
+
+ BOOST_CHECK_EQUAL(0, v->access(env, "/metadata/timestamp.chk", R_OK));
+ BOOST_CHECK_EQUAL(EACCES, v->access(env, "/metadata/timestamp.chk", W_OK));
+ BOOST_CHECK_EQUAL(EACCES, v->access(env, "/metadata/timestamp.chk", X_OK));
+
+ BOOST_CHECK_EQUAL(0, v->access(env, "/app-antivirus/skyldav/files/skyldav.initd", R_OK));
+ BOOST_CHECK_EQUAL(EACCES, v->access(env, "/app-antivirus/skyldav/files/skyldav.initd", W_OK));
+ BOOST_CHECK_EQUAL(0, v->access(env, "/app-antivirus/skyldav/files/skyldav.initd", X_OK));
+
+ BOOST_CHECK_EQUAL(ENOENT, v->access(env, "/missing", R_OK));
+ BOOST_CHECK_EQUAL(EACCES, v->access(env, "/missing", W_OK));
+ BOOST_CHECK_EQUAL(ENOENT, v->access(env, "/missing", X_OK));
+
+ BOOST_CHECK_EQUAL(ENOENT, v->access(env, "/.", R_OK));
+ BOOST_CHECK_EQUAL(ENOENT, v->access(env, "/../", R_OK));
+ BOOST_CHECK_EQUAL(ENOENT, v->access(env, ".", R_OK));
+ BOOST_CHECK_EQUAL(ENOENT, v->access(env, "..", R_OK));
+ BOOST_CHECK_EQUAL(ENOENT, v->access(env, "../", R_OK));
+}
+
+BOOST_AUTO_TEST_CASE( statRoot )
+{
+ //auto a = v->getattr(env, "/");
+ //BOOST_CHECK_EQUAL(0170555, a.mode);
+ //BOOST_CHECK_EQUAL(1563566842, a.mtime);
+ //BOOST_CHECK_EQUAL(1563566842, a.ctime);
+ //BOOST_CHECK_EQUAL(1563566842, a.atime);
+}
+
+BOOST_AUTO_TEST_CASE(stat_root)
+{
+ auto attr = v->getattr(env, "");
+}
+
+BOOST_AUTO_TEST_SUITE_END();
+
diff --git a/unittests/mockDefs.cpp b/unittests/mockDefs.cpp
new file mode 100644
index 0000000..78ad270
--- /dev/null
+++ b/unittests/mockDefs.cpp
@@ -0,0 +1,26 @@
+#include <boost/test/test_tools.hpp>
+#include "mockDefs.h"
+
+GitFS::Test::Service::Service()
+{
+}
+
+GitFS::Test::Client::Client() :
+ s(getProxy<NetFS::ServicePrx>("Service"))
+{
+ BOOST_TEST_REQUIRE(s);
+ s->ice_ping();
+}
+
+GitFS::Test::VolumeClient::VolumeClient() :
+ v(s->connect("testrepo", "testauth"))
+{
+ BOOST_TEST_REQUIRE(v);
+ v->ice_ping();
+}
+
+GitFS::Test::VolumeClient::~VolumeClient()
+{
+ v->disconnect();
+}
+
diff --git a/unittests/mockDefs.h b/unittests/mockDefs.h
new file mode 100644
index 0000000..f6a6c99
--- /dev/null
+++ b/unittests/mockDefs.h
@@ -0,0 +1,31 @@
+#ifndef GITFS_TEST_MOCKDEFS_H
+#define GITFS_TEST_MOCKDEFS_H
+
+#include <visibility.h>
+#include <dryice.h>
+#include <service.h>
+
+namespace GitFS::Test {
+ class DLL_PUBLIC Service : public IceTray::DryIce {
+ public:
+ Service();
+ };
+
+ class DLL_PUBLIC Client : public IceTray::DryIceClient {
+ public:
+ Client();
+
+ const NetFS::ServicePrxPtr s;
+ };
+ class DLL_PUBLIC VolumeClient : public Client {
+ public:
+ VolumeClient();
+ ~VolumeClient();
+
+ const NetFS::ReqEnv env;
+ const NetFS::VolumePrxPtr v;
+ };
+}
+
+#endif
+