Merge fspp repository into here

This commit is contained in:
Sebastian Messmer 2016-02-11 12:14:44 +01:00
commit 0fb710ac69
154 changed files with 6985 additions and 2 deletions

View File

@ -15,6 +15,7 @@ addons:
apt:
packages:
- libcrypto++-dev
- libfuse-dev
install:
- wget -O boost.tar.bz2 https://sourceforge.net/projects/boost/files/boost/1.56.0/boost_1_56_0.tar.bz2/download
- tar -xf boost.tar.bz2
@ -24,9 +25,14 @@ install:
- sudo ./b2 -d0 install
- cd ..
- sudo rm -rf boost.tar.bz2 boost_1_56_0
- wget https://raw.githubusercontent.com/smessmer/travis-utils/master/run_with_fuse.sh
- chmod +x run_with_fuse.sh
script:
- mkdir cmake
- cd cmake
- cmake ..
- make -j2
- ./test/cpp-utils-test
- ./run_with_fuse.sh ./test/fspp-test
after_script:
- rm run_with_fuse.sh

112
CMakeLists.fspp.txt Normal file
View File

@ -0,0 +1,112 @@
=======
INCLUDE(messmer/cmake/tools)
# Actually create targets: EXEcutables and libraries.
ADD_BII_TARGETS()
ADD_BOOST(filesystem system thread chrono)
ACTIVATE_CPP14()
REQUIRE_GCC_VERSION(4.8)
ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64)
ENABLE_STYLE_WARNINGS()
IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
IF(EXISTS "/usr/local/include/osxfuse")
TARGET_INCLUDE_DIRECTORIES(${BII_LIB_TARGET} PUBLIC /usr/local/include/osxfuse)
TARGET_LINK_LIBRARIES(${BII_BLOCK_TARGET} INTERFACE osxfuse)
ELSE()
MESSAGE(FATAL_ERROR "Osxfuse not found. Please install osxfuse.")
ENDIF(EXISTS "/usr/local/include/osxfuse")
ELSE(CMAKE_SYSTEM_NAME)
TARGET_LINK_LIBRARIES(${BII_BLOCK_TARGET} INTERFACE fuse)
ENDIF(CMAKE_SYSTEM_NAME)
# You can safely delete lines from here...
###############################################################################
# REFERENCE #
###############################################################################
#
# This CMakeLists.txt file helps defining your block building and compiling
# To learn more about the CMake use with biicode, visit http://docs.biicode.com/c++.html
#
# ----------------------------------------------------
# NEW FEATURE! Include cmake files from remote blocks:
# -----------------------------------------------------
# Now you can handle cmake dependencies alike you do with c/c++:
#
# INCLUDE(user/block/myrecipe) # include myrecipe.cmake from remote user/block
#
# > EXAMPLE: Include our recipes and activate C++11 in your block (http://www.biicode.com/biicode/cmake)
#
# INCLUDE(biicode/cmake/tools) # Include tools.cmake file from "cmake" block from the "biicode" user
# ACTIVATE_CPP11(INTERFACE ${BII_BLOCK_TARGET})
#
# Remember to run "bii find" to download out cmake tools file
#
# ---------------------
# INIT_BIICODE_BLOCK()
# ---------------------
# This function creates several helper variables as ${BII_BLOCK_NAME} and ${BII_BLOCK_USER}
# Also it loads variables from the cmake/bii_user_block_vars.cmake
# ${BII_LIB_SRC} File list to create the library
# ${BII_LIB_TYPE} Empty (default, STATIC most casess) STATIC or SHARED
# ${BII_LIB_DEPS} Dependencies to other libraries (user2_block2, user3_blockX)
# ${BII_LIB_SYSTEM_HEADERS} System linking requirements as windows.h, pthread.h, etc
#
# You can use or modify them here, for example, to add or remove files from targets based on OS
# Or use typical cmake configurations done BEFORE defining targets. Examples:
# ADD_DEFINITIONS(-DFOO)
# FIND_PACKAGE(OpenGL QUIET)
# You can add INCLUDE_DIRECTORIES here too
#
# ---------------------
# ADD_BIICODE_TARGETS()
# ---------------------
#
# This function creates the following variables:
# ${BII_BLOCK_TARGET} Interface (no files) target for convenient configuration of all
# targets in this block, as the rest of targets always depend on it
# has name in the form "user_block_interface"
# ${BII_LIB_TARGET} Target library name, usually in the form "user_block". May not exist
# if BII_LIB_SRC is empty
# ${BII_BLOCK_TARGETS} List of all targets defined in this block
# ${BII_BLOCK_EXES} List of executables targets defined in this block
# ${BII_exe_name_TARGET}: Executable target (e.g. ${BII_main_TARGET}. You can also use
# directly the name of the executable target (e.g. user_block_main)
#
# > EXAMPLE: Add include directories to all targets of this block
#
# TARGET_INCLUDE_DIRECTORIES(${BII_BLOCK_TARGET} INTERFACE myincludedir)
#
# You can add private include directories to the Lib (if existing)
#
# > EXAMPLE: Link with pthread:
#
# TARGET_LINK_LIBRARIES(${BII_BLOCK_TARGET} INTERFACE pthread)
# or link against library:
# TARGET_LINK_LIBRARIES(${BII_LIB_TARGET} PUBLIC pthread)
# or directly use the library target name:
# TARGET_LINK_LIBRARIES(user_block PUBLIC pthread)
#
# NOTE: This can be also done adding pthread to ${BII_LIB_DEPS}
# BEFORE calling ADD_BIICODE_TARGETS()
#
# > EXAMPLE: how to activate C++11
#
# IF(APPLE)
# TARGET_COMPILE_OPTIONS(${BII_BLOCK_TARGET} INTERFACE "-std=c++11 -stdlib=libc++")
# ELSEIF (WIN32 OR UNIX)
# TARGET_COMPILE_OPTIONS(${BII_BLOCK_TARGET} INTERFACE "-std=c++11")
# ENDIF(APPLE)
#
# > EXAMPLE: Set properties to target
#
# SET_TARGET_PROPERTIES(${BII_BLOCK_TARGET} PROPERTIES COMPILE_DEFINITIONS "IOV_MAX=255")
#
>>>>>>> fspp_develop

View File

@ -1,2 +0,0 @@
# cpp-utils [![Build Status](https://travis-ci.org/smessmer/cpp-utils.svg?branch=master)](https://travis-ci.org/smessmer/cpp-utils)
Some C++ helper classes

44
biicode.conf Normal file
View File

@ -0,0 +1,44 @@
# Biicode configuration file
[requirements]
google/gmock: 4
google/gtest: 11
messmer/cmake: 3
messmer/cpp-utils: 9
[parent]
messmer/fspp: 8
[paths]
# Local directories to look for headers (within block)
# /
# include
[dependencies]
# Manual adjust file implicit dependencies, add (+), remove (-), or overwrite (=)
# hello.h + hello_imp.cpp hello_imp2.cpp
# *.h + *.cpp
test/main.cpp + test/*.cpp
[mains]
# Manual adjust of files that define an executable
# !main.cpp # Do not build executable from this file
# main2.cpp # Build it (it doesnt have a main() function, but maybe it includes it)
[hooks]
# These are defined equal to [dependencies],files names matching bii*stage*hook.py
# will be launched as python scripts at stage = {post_process, clean}
# CMakeLists.txt + bii/my_post_process1_hook.py bii_clean_hook.py
[includes]
# Mapping of include patterns to external blocks
# hello*.h: user3/depblock # includes will be processed as user3/depblock/hello*.h
[data]
# Manually define data files dependencies, that will be copied to bin for execution
# By default they are copied to bin/user/block/... which should be taken into account
# when loading from disk such data
# image.cpp + image.jpg # code should write open("user/block/image.jpg")
[tests]
test/*

22
fs_interface/Device.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#ifndef MESSMER_FSPP_FSINTERFACE_DEVICE_H_
#define MESSMER_FSPP_FSINTERFACE_DEVICE_H_
#include <boost/filesystem.hpp>
#include <messmer/cpp-utils/pointer/unique_ref.h>
#include <sys/statvfs.h>
namespace fspp {
class Node;
class Device {
public:
virtual ~Device() {}
virtual void statfs(const boost::filesystem::path &path, struct ::statvfs *fsstat) = 0;
virtual boost::optional<cpputils::unique_ref<Node>> Load(const boost::filesystem::path &path) = 0;
};
}
#endif

40
fs_interface/Dir.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#ifndef MESSMER_FSPP_FSINTERFACE_DIR_H_
#define MESSMER_FSPP_FSINTERFACE_DIR_H_
#include "Node.h"
#include <messmer/cpp-utils/pointer/unique_ref.h>
#include <string>
namespace fspp {
class Device;
class OpenFile;
class Dir: public virtual Node {
public:
virtual ~Dir() {}
enum class EntryType: uint8_t {
DIR = 0x00,
FILE = 0x01,
SYMLINK = 0x02
};
struct Entry {
Entry(EntryType type_, const std::string &name_): type(type_), name(name_) {}
EntryType type;
std::string name;
};
virtual cpputils::unique_ref<OpenFile> createAndOpenFile(const std::string &name, mode_t mode, uid_t uid, gid_t gid) = 0;
virtual void createDir(const std::string &name, mode_t mode, uid_t uid, gid_t gid) = 0;
virtual void createSymlink(const std::string &name, const boost::filesystem::path &target, uid_t uid, gid_t gid) = 0;
//TODO Allow alternative implementation returning only children names without more information
//virtual std::unique_ptr<std::vector<std::string>> children() const = 0;
virtual cpputils::unique_ref<std::vector<Entry>> children() const = 0;
};
}
#endif

22
fs_interface/File.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#ifndef MESSMER_FSPP_FSINTERFACE_FILE_H_
#define MESSMER_FSPP_FSINTERFACE_FILE_H_
#include "Node.h"
#include <messmer/cpp-utils/pointer/unique_ref.h>
namespace fspp {
class Device;
class OpenFile;
class File: public virtual Node {
public:
virtual ~File() {}
virtual cpputils::unique_ref<OpenFile> open(int flags) const = 0;
virtual void truncate(off_t size) const = 0;
};
}
#endif

26
fs_interface/Node.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#ifndef MESSMER_FSPP_FSINTERFACE_NODE_H_
#define MESSMER_FSPP_FSINTERFACE_NODE_H_
#include <boost/filesystem.hpp>
#include <sys/stat.h>
namespace fspp {
class Node {
public:
virtual ~Node() {}
virtual void stat(struct ::stat *result) const = 0;
virtual void chmod(mode_t mode) = 0;
virtual void chown(uid_t uid, gid_t gid) = 0;
virtual void access(int mask) const = 0;
virtual void rename(const boost::filesystem::path &to) = 0;
virtual void utimens(const timespec lastAccessTime, const timespec lastModificationTime) = 0;
virtual void remove() = 0;
};
}
#endif

26
fs_interface/OpenFile.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#ifndef MESSMER_FSPP_FSINTERFACE_OPENFILE_H_
#define MESSMER_FSPP_FSINTERFACE_OPENFILE_H_
#include <boost/filesystem.hpp>
#include <sys/stat.h>
namespace fspp {
class Device;
class OpenFile {
public:
virtual ~OpenFile() {}
virtual void stat(struct ::stat *result) const = 0;
virtual void truncate(off_t size) const = 0;
virtual size_t read(void *buf, size_t count, off_t offset) const = 0;
virtual void write(const void *buf, size_t count, off_t offset) = 0;
virtual void flush() = 0;
virtual void fsync() = 0;
virtual void fdatasync() = 0;
};
}
#endif

21
fs_interface/Symlink.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#ifndef MESSMER_FSPP_FSINTERFACE_SYMLINK_H_
#define MESSMER_FSPP_FSINTERFACE_SYMLINK_H_
#include "Node.h"
#include <messmer/cpp-utils/pointer/unique_ref.h>
#include <string>
namespace fspp {
class Device;
class Symlink: public virtual Node {
public:
virtual ~Symlink() {}
virtual boost::filesystem::path target() const = 0;
};
}
#endif

19
fstest/FsTest.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#ifndef MESSMER_FSPP_FSTEST_FSTEST_H_
#define MESSMER_FSPP_FSTEST_FSTEST_H_
#include "testutils/FileSystemTest.h"
#include "FsppDeviceTest.h"
#include "FsppDirTest.h"
#include "FsppFileTest.h"
#include "FsppSymlinkTest.h"
#include "FsppOpenFileTest.h"
#define FSPP_ADD_FILESYTEM_TESTS(FS_NAME, FIXTURE) \
INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppDeviceTest, FIXTURE); \
INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppDirTest, FIXTURE); \
INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppFileTest, FIXTURE); \
INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppSymlinkTest, FIXTURE); \
INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppOpenFileTest, FIXTURE); \
#endif

105
fstest/FsppDeviceTest.h Normal file
View File

@ -0,0 +1,105 @@
#pragma once
#ifndef MESSMER_FSPP_FSTEST_FSPPDEVICETEST_H_
#define MESSMER_FSPP_FSTEST_FSPPDEVICETEST_H_
template<class ConcreteFileSystemTestFixture>
class FsppDeviceTest: public FileSystemTest<ConcreteFileSystemTestFixture> {
public:
void InitDirStructure() {
this->LoadDir("/")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/")->createDir("myemptydir", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/mydir")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/mydir")->createAndOpenFile("myfile2", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/mydir")->createDir("mysubdir", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/mydir/mysubdir")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/mydir/mysubdir")->createDir("mysubsubdir", this->MODE_PUBLIC, 0, 0);
}
};
TYPED_TEST_CASE_P(FsppDeviceTest);
TYPED_TEST_P(FsppDeviceTest, InitFilesystem) {
//fixture->createDevice() is called in the FileSystemTest constructor
}
TYPED_TEST_P(FsppDeviceTest, LoadRootDir) {
this->LoadDir("/");
}
TYPED_TEST_P(FsppDeviceTest, LoadFileFromRootDir) {
this->InitDirStructure();
this->LoadFile("/myfile");
}
TYPED_TEST_P(FsppDeviceTest, LoadDirFromRootDir) {
this->InitDirStructure();
this->LoadDir("/mydir");
}
TYPED_TEST_P(FsppDeviceTest, LoadNonexistingFromEmptyRootDir) {
EXPECT_EQ(boost::none, this->device->Load("/nonexisting"));
}
TYPED_TEST_P(FsppDeviceTest, LoadNonexistingFromRootDir) {
this->InitDirStructure();
EXPECT_EQ(boost::none, this->device->Load("/nonexisting"));
}
TYPED_TEST_P(FsppDeviceTest, LoadNonexistingFromNonexistingDir) {
this->InitDirStructure();
//TODO Change as soon as we have a concept of how to handle filesystem errors in the interface
EXPECT_ANY_THROW(
this->device->Load("/nonexisting/nonexisting2")
);
}
TYPED_TEST_P(FsppDeviceTest, LoadNonexistingFromExistingDir) {
this->InitDirStructure();
EXPECT_EQ(boost::none, this->device->Load("/mydir/nonexisting"));
}
TYPED_TEST_P(FsppDeviceTest, LoadNonexistingFromExistingEmptyDir) {
this->InitDirStructure();
EXPECT_EQ(boost::none, this->device->Load("/myemptydir/nonexisting"));
}
TYPED_TEST_P(FsppDeviceTest, LoadFileFromDir_Nesting1) {
this->InitDirStructure();
this->LoadFile("/mydir/myfile");
}
TYPED_TEST_P(FsppDeviceTest, LoadDirFromDir_Nesting1) {
this->InitDirStructure();
this->LoadDir("/mydir/mysubdir");
}
TYPED_TEST_P(FsppDeviceTest, LoadFileFromDir_Nesting2) {
this->InitDirStructure();
this->LoadFile("/mydir/mysubdir/myfile");
}
TYPED_TEST_P(FsppDeviceTest, LoadDirFromDir_Nesting2) {
this->InitDirStructure();
this->LoadDir("/mydir/mysubdir/mysubsubdir");
}
//TODO Test statfs
REGISTER_TYPED_TEST_CASE_P(FsppDeviceTest,
InitFilesystem,
LoadRootDir,
LoadFileFromRootDir,
LoadDirFromRootDir,
LoadNonexistingFromEmptyRootDir,
LoadNonexistingFromRootDir,
LoadNonexistingFromNonexistingDir,
LoadNonexistingFromExistingDir,
LoadNonexistingFromExistingEmptyDir,
LoadFileFromDir_Nesting1,
LoadDirFromDir_Nesting1,
LoadFileFromDir_Nesting2,
LoadDirFromDir_Nesting2
);
#endif

289
fstest/FsppDirTest.h Normal file
View File

@ -0,0 +1,289 @@
#pragma once
#ifndef MESSMER_FSPP_FSTEST_FSPPDIRTEST_H_
#define MESSMER_FSPP_FSTEST_FSPPDIRTEST_H_
template<class ConcreteFileSystemTestFixture>
class FsppDirTest: public FileSystemTest<ConcreteFileSystemTestFixture> {
public:
void InitDirStructure() {
this->LoadDir("/")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/")->createDir("myemptydir", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/mydir")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/mydir")->createAndOpenFile("myfile2", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/mydir")->createDir("mysubdir", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/mydir/mysubdir")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/mydir/mysubdir")->createDir("mysubsubdir", this->MODE_PUBLIC, 0, 0);
}
void EXPECT_CHILDREN_ARE(const boost::filesystem::path &path, const std::initializer_list<fspp::Dir::Entry> expected) {
EXPECT_CHILDREN_ARE(*this->LoadDir(path), expected);
}
void EXPECT_CHILDREN_ARE(const fspp::Dir &dir, const std::initializer_list<fspp::Dir::Entry> expected) {
std::vector<fspp::Dir::Entry> expectedChildren = expected;
expectedChildren.push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, "."));
expectedChildren.push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, ".."));
EXPECT_UNORDERED_EQ(expectedChildren, *dir.children());
}
template<class Entry>
void EXPECT_UNORDERED_EQ(const std::vector<Entry> &expected, std::vector<Entry> actual) {
EXPECT_EQ(expected.size(), actual.size());
for (const Entry &expectedEntry : expected) {
removeOne(&actual, expectedEntry);
}
}
template<class Entry>
void removeOne(std::vector<Entry> *entries, const Entry &toRemove) {
for (auto iter = entries->begin(); iter != entries->end(); ++iter) {
if (iter->type == toRemove.type && iter->name == toRemove.name) {
entries->erase(iter);
return;
}
}
EXPECT_TRUE(false);
}
};
TYPED_TEST_CASE_P(FsppDirTest);
fspp::Dir::Entry DirEntry(const std::string &name) {
return fspp::Dir::Entry(fspp::Dir::EntryType::DIR, name);
}
fspp::Dir::Entry FileEntry(const std::string &name) {
return fspp::Dir::Entry(fspp::Dir::EntryType::FILE, name);
}
TYPED_TEST_P(FsppDirTest, Children_RootDir_Empty) {
this->EXPECT_CHILDREN_ARE("/", {});
}
TYPED_TEST_P(FsppDirTest, Children_RootDir_OneFile_Directly) {
auto rootdir = this->LoadDir("/");
rootdir->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE(*rootdir, {
FileEntry("myfile")
});
}
TYPED_TEST_P(FsppDirTest, Children_RootDir_OneFile_AfterReloadingDir) {
this->LoadDir("/")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE("/", {
FileEntry("myfile")
});
}
TYPED_TEST_P(FsppDirTest, Children_RootDir_OneDir_Directly) {
auto rootdir = this->LoadDir("/");
rootdir->createDir("mydir", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE(*rootdir, {
DirEntry("mydir")
});
}
TYPED_TEST_P(FsppDirTest, Children_RootDir_OneDir_AfterReloadingDir) {
this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE("/", {
DirEntry("mydir")
});
}
TYPED_TEST_P(FsppDirTest, Children_RootDir_LargerStructure) {
this->InitDirStructure();
this->EXPECT_CHILDREN_ARE("/", {
FileEntry("myfile"),
DirEntry("mydir"),
DirEntry("myemptydir")
});
}
TYPED_TEST_P(FsppDirTest, Children_Nested_Empty) {
this->LoadDir("/")->createDir("myemptydir", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE("/myemptydir", {});
}
TYPED_TEST_P(FsppDirTest, Children_Nested_OneFile_Directly) {
this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0);
auto dir = this->LoadDir("/mydir");
dir->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE(*dir, {
FileEntry("myfile")
});
}
TYPED_TEST_P(FsppDirTest, Children_Nested_OneFile_AfterReloadingDir) {
this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/mydir")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE("/mydir", {
FileEntry("myfile")
});
}
TYPED_TEST_P(FsppDirTest, Children_Nested_OneDir_Directly) {
this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0);
auto dir = this->LoadDir("/mydir");
dir->createDir("mysubdir", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE(*dir, {
DirEntry("mysubdir")
});
}
TYPED_TEST_P(FsppDirTest, Children_Nested_OneDir_AfterReloadingDir) {
this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/mydir")->createDir("mysubdir", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE("/mydir", {
DirEntry("mysubdir")
});
}
TYPED_TEST_P(FsppDirTest, Children_Nested_LargerStructure_Empty) {
this->InitDirStructure();
this->EXPECT_CHILDREN_ARE("/myemptydir", {});
}
TYPED_TEST_P(FsppDirTest, Children_Nested_LargerStructure) {
this->InitDirStructure();
this->EXPECT_CHILDREN_ARE("/mydir", {
FileEntry("myfile"),
FileEntry("myfile2"),
DirEntry("mysubdir")
});
}
TYPED_TEST_P(FsppDirTest, Children_Nested2_LargerStructure) {
this->InitDirStructure();
this->EXPECT_CHILDREN_ARE("/mydir/mysubdir", {
FileEntry("myfile"),
DirEntry("mysubsubdir")
});
}
TYPED_TEST_P(FsppDirTest, CreateAndOpenFile_InEmptyRoot) {
this->LoadDir("/")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0);
this->LoadFile("/myfile");
}
TYPED_TEST_P(FsppDirTest, CreateAndOpenFile_InNonemptyRoot) {
this->InitDirStructure();
this->LoadDir("/")->createAndOpenFile("mynewfile", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE("/", {
FileEntry("myfile"),
DirEntry("mydir"),
DirEntry("myemptydir"),
FileEntry("mynewfile")
});
}
TYPED_TEST_P(FsppDirTest, CreateAndOpenFile_InEmptyNestedDir) {
this->InitDirStructure();
this->LoadDir("/myemptydir")->createAndOpenFile("mynewfile", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE("/myemptydir", {
FileEntry("mynewfile")
});
}
TYPED_TEST_P(FsppDirTest, CreateAndOpenFile_InNonemptyNestedDir) {
this->InitDirStructure();
this->LoadDir("/mydir")->createAndOpenFile("mynewfile", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE("/mydir", {
FileEntry("myfile"),
FileEntry("myfile2"),
DirEntry("mysubdir"),
FileEntry("mynewfile")
});
}
TYPED_TEST_P(FsppDirTest, CreateAndOpenFile_AlreadyExisting) {
this->LoadDir("/")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0);
//TODO Change, once we know which way of error reporting we want for such errors
EXPECT_ANY_THROW(
this->LoadDir("/")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0);
);
}
TYPED_TEST_P(FsppDirTest, CreateDir_InEmptyRoot) {
this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/mydir");
}
TYPED_TEST_P(FsppDirTest, CreateDir_InNonemptyRoot) {
this->InitDirStructure();
this->LoadDir("/")->createDir("mynewdir", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE("/", {
FileEntry("myfile"),
DirEntry("mydir"),
DirEntry("myemptydir"),
DirEntry("mynewdir")
});
}
TYPED_TEST_P(FsppDirTest, CreateDir_InEmptyNestedDir) {
this->InitDirStructure();
this->LoadDir("/myemptydir")->createDir("mynewdir", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE("/myemptydir", {
DirEntry("mynewdir")
});
}
TYPED_TEST_P(FsppDirTest, CreateDir_InNonemptyNestedDir) {
this->InitDirStructure();
this->LoadDir("/mydir")->createDir("mynewdir", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE("/mydir", {
FileEntry("myfile"),
FileEntry("myfile2"),
DirEntry("mysubdir"),
DirEntry("mynewdir")
});
}
TYPED_TEST_P(FsppDirTest, CreateDir_AlreadyExisting) {
this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0);
//TODO Change, once we know which way of error reporting we want for such errors
EXPECT_ANY_THROW(
this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0);
);
}
REGISTER_TYPED_TEST_CASE_P(FsppDirTest,
Children_RootDir_Empty,
Children_RootDir_OneFile_Directly,
Children_RootDir_OneFile_AfterReloadingDir,
Children_RootDir_OneDir_Directly,
Children_RootDir_OneDir_AfterReloadingDir,
Children_RootDir_LargerStructure,
Children_Nested_Empty,
Children_Nested_OneFile_Directly,
Children_Nested_OneFile_AfterReloadingDir,
Children_Nested_OneDir_Directly,
Children_Nested_OneDir_AfterReloadingDir,
Children_Nested_LargerStructure,
Children_Nested_LargerStructure_Empty,
Children_Nested2_LargerStructure,
CreateAndOpenFile_InEmptyRoot,
CreateAndOpenFile_InNonemptyRoot,
CreateAndOpenFile_InEmptyNestedDir,
CreateAndOpenFile_InNonemptyNestedDir,
CreateAndOpenFile_AlreadyExisting,
CreateDir_InEmptyRoot,
CreateDir_InNonemptyRoot,
CreateDir_InEmptyNestedDir,
CreateDir_InNonemptyNestedDir,
CreateDir_AlreadyExisting
);
//TODO stat
//TODO access
//TODO rename
//TODO utimens
//TODO rmdir
//TODO chmod
//TODO chown
//TODO mkdir with uid/gid
//TODO Test permission flags
#endif

179
fstest/FsppFileTest.h Normal file
View File

@ -0,0 +1,179 @@
#pragma once
#ifndef MESSMER_FSPP_FSTEST_FSPPFILETEST_H_
#define MESSMER_FSPP_FSTEST_FSPPFILETEST_H_
#include <sys/fcntl.h>
#include <sys/stat.h>
#include "testutils/FileTest.h"
template<class ConcreteFileSystemTestFixture>
class FsppFileTest: public FileTest<ConcreteFileSystemTestFixture> {
public:
void Test_Open_RDONLY(fspp::File *file) {
file->open(O_RDONLY);
}
void Test_Open_WRONLY(fspp::File *file) {
file->open(O_WRONLY);
}
void Test_Open_RDWR(fspp::File *file) {
file->open(O_RDONLY);
}
void Test_Truncate_DontChange1(fspp::File *file) {
file->truncate(0);
this->EXPECT_SIZE(0, *file);
}
void Test_Truncate_GrowTo1(fspp::File *file) {
file->truncate(1);
this->EXPECT_SIZE(1, *file);
}
void Test_Truncate_Grow(fspp::File *file) {
file->truncate(10*1024*1024);
this->EXPECT_SIZE(10*1024*1024, *file);
}
void Test_Truncate_DontChange2(fspp::File *file) {
file->truncate(10*1024*1024);
file->truncate(10*1024*1024);
this->EXPECT_SIZE(10*1024*1024, *file);
}
void Test_Truncate_Shrink(fspp::File *file) {
file->truncate(10*1024*1024);
file->truncate(5*1024*1024);
this->EXPECT_SIZE(5*1024*1024, *file);
}
void Test_Truncate_ShrinkTo0(fspp::File *file) {
file->truncate(10*1024*1024);
file->truncate(0);
this->EXPECT_SIZE(0, *file);
}
void Test_Stat_CreatedFileIsEmpty(fspp::File *file) {
this->EXPECT_SIZE(0, *file);
}
};
TYPED_TEST_CASE_P(FsppFileTest);
TYPED_TEST_P(FsppFileTest, Open_RDONLY) {
this->Test_Open_RDONLY(this->file_root.get());
}
TYPED_TEST_P(FsppFileTest, Open_RDONLY_Nested) {
this->Test_Open_RDONLY(this->file_nested.get());
}
TYPED_TEST_P(FsppFileTest, Open_WRONLY) {
this->Test_Open_WRONLY(this->file_root.get());
}
TYPED_TEST_P(FsppFileTest, Open_WRONLY_Nested) {
this->Test_Open_WRONLY(this->file_nested.get());
}
TYPED_TEST_P(FsppFileTest, Open_RDWR) {
this->Test_Open_RDWR(this->file_root.get());
}
TYPED_TEST_P(FsppFileTest, Open_RDWR_Nested) {
this->Test_Open_RDWR(this->file_nested.get());
}
TYPED_TEST_P(FsppFileTest, Truncate_DontChange1) {
this->Test_Truncate_DontChange1(this->file_root.get());
}
TYPED_TEST_P(FsppFileTest, Truncate_DontChange1_Nested) {
this->Test_Truncate_DontChange1(this->file_nested.get());
}
TYPED_TEST_P(FsppFileTest, Truncate_GrowTo1) {
this->Test_Truncate_GrowTo1(this->file_root.get());
}
TYPED_TEST_P(FsppFileTest, Truncate_GrowTo1_Nested) {
this->Test_Truncate_GrowTo1(this->file_nested.get());
}
TYPED_TEST_P(FsppFileTest, Truncate_Grow) {
this->Test_Truncate_Grow(this->file_root.get());
}
TYPED_TEST_P(FsppFileTest, Truncate_Grow_Nested) {
this->Test_Truncate_Grow(this->file_nested.get());
}
TYPED_TEST_P(FsppFileTest, Truncate_DontChange2) {
this->Test_Truncate_DontChange2(this->file_root.get());
}
TYPED_TEST_P(FsppFileTest, Truncate_DontChange2_Nested) {
this->Test_Truncate_DontChange2(this->file_nested.get());
}
TYPED_TEST_P(FsppFileTest, Truncate_Shrink) {
this->Test_Truncate_Shrink(this->file_root.get());
}
TYPED_TEST_P(FsppFileTest, Truncate_Shrink_Nested) {
this->Test_Truncate_Shrink(this->file_nested.get());
}
TYPED_TEST_P(FsppFileTest, Truncate_ShrinkTo0) {
this->Test_Truncate_ShrinkTo0(this->file_root.get());
}
TYPED_TEST_P(FsppFileTest, Truncate_ShrinkTo0_Nested) {
this->Test_Truncate_ShrinkTo0(this->file_nested.get());
}
TYPED_TEST_P(FsppFileTest, Stat_CreatedFileIsEmpty) {
this->Test_Stat_CreatedFileIsEmpty(this->file_root.get());
}
TYPED_TEST_P(FsppFileTest, Stat_CreatedFileIsEmpty_Nested) {
this->Test_Stat_CreatedFileIsEmpty(this->file_nested.get());
}
REGISTER_TYPED_TEST_CASE_P(FsppFileTest,
Open_RDONLY,
Open_RDONLY_Nested,
Open_WRONLY,
Open_WRONLY_Nested,
Open_RDWR,
Open_RDWR_Nested,
Truncate_DontChange1,
Truncate_DontChange1_Nested,
Truncate_GrowTo1,
Truncate_GrowTo1_Nested,
Truncate_Grow,
Truncate_Grow_Nested,
Truncate_DontChange2,
Truncate_DontChange2_Nested,
Truncate_Shrink,
Truncate_Shrink_Nested,
Truncate_ShrinkTo0,
Truncate_ShrinkTo0_Nested,
Stat_CreatedFileIsEmpty,
Stat_CreatedFileIsEmpty_Nested
);
//TODO stat
//TODO access
//TODO rename
//TODO utimens
//TODO unlink
//TODO chmod
//TODO chown
//TODO createAndOpenFile with uid/gid
//TODO Test permission flags
#endif

30
fstest/FsppOpenFileTest.h Normal file
View File

@ -0,0 +1,30 @@
#pragma once
#ifndef MESSMER_FSPP_FSTEST_FSPPOPENFILETEST_H_
#define MESSMER_FSPP_FSTEST_FSPPOPENFILETEST_H_
#include "testutils/FileTest.h"
template<class ConcreteFileSystemTestFixture>
class FsppOpenFileTest: public FileTest<ConcreteFileSystemTestFixture> {
public:
};
TYPED_TEST_CASE_P(FsppOpenFileTest);
TYPED_TEST_P(FsppOpenFileTest, Bla) {
//TODO
}
REGISTER_TYPED_TEST_CASE_P(FsppOpenFileTest,
Bla
);
//TODO Test stat
//TODO Test truncate
//TODO Test read
//TODO Test write
//TODO Test flush
//TODO Test fsync
//TODO Test fdatasync
#endif

78
fstest/FsppSymlinkTest.h Normal file
View File

@ -0,0 +1,78 @@
#pragma once
#ifndef MESSMER_FSPP_FSTEST_FSPPSYMLINKTEST_H_
#define MESSMER_FSPP_FSTEST_FSPPSYMLINKTEST_H_
#include <sys/fcntl.h>
#include <sys/stat.h>
#include "testutils/FileSystemTest.h"
template<class ConcreteFileSystemTestFixture>
class FsppSymlinkTest: public FileSystemTest<ConcreteFileSystemTestFixture> {
public:
void CreateSymlink(const std::string &source, const boost::filesystem::path &target) {
this->LoadDir("/")->createSymlink(source, target, 0, 0);
}
void Test_Create_AbsolutePath() {
CreateSymlink("mysymlink", "/my/symlink/target");
}
void Test_Create_RelativePath() {
CreateSymlink("mysymlink", "../target");
}
void Test_Read_AbsolutePath() {
CreateSymlink("mysymlink", "/my/symlink/target");
EXPECT_EQ("/my/symlink/target", this->LoadSymlink("/mysymlink")->target());
}
void Test_Read_RelativePath() {
CreateSymlink("mysymlink", "../target");
EXPECT_EQ("../target", this->LoadSymlink("/mysymlink")->target());
}
void Test_Delete() {
CreateSymlink("mysymlink", "/my/symlink/target");
std::cerr << "1" << std::endl;
EXPECT_NE(boost::none, this->device->Load("/mysymlink"));
std::cerr << "2" << std::endl;
this->LoadSymlink("/mysymlink")->remove();
std::cerr << "3" << std::endl;
EXPECT_EQ(boost::none, this->device->Load("/mysymlink"));
}
};
TYPED_TEST_CASE_P(FsppSymlinkTest);
TYPED_TEST_P(FsppSymlinkTest, Create_AbsolutePath) {
this->Test_Create_AbsolutePath();
}
TYPED_TEST_P(FsppSymlinkTest, Create_RelativePath) {
this->Test_Create_RelativePath();
}
TYPED_TEST_P(FsppSymlinkTest, Read_AbsolutePath) {
this->Test_Read_AbsolutePath();
}
TYPED_TEST_P(FsppSymlinkTest, Read_RelativePath) {
this->Test_Read_RelativePath();
}
TYPED_TEST_P(FsppSymlinkTest, Delete) {
this->Test_Delete();
}
REGISTER_TYPED_TEST_CASE_P(FsppSymlinkTest,
Create_AbsolutePath,
Create_RelativePath,
Read_AbsolutePath,
Read_RelativePath,
Delete
);
//TODO Other tests?
#endif

View File

@ -0,0 +1,64 @@
#pragma once
#ifndef MESSMER_FSPP_FSTEST_TESTUTILS_FILESYSTEMTEST_H_
#define MESSMER_FSPP_FSTEST_TESTUTILS_FILESYSTEMTEST_H_
#include <google/gtest/gtest.h>
#include <type_traits>
#include <boost/static_assert.hpp>
#include <messmer/cpp-utils/pointer/unique_ref.h>
#include <messmer/cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
#include "../../fs_interface/Device.h"
#include "../../fs_interface/Dir.h"
#include "../../fs_interface/File.h"
#include "../../fs_interface/Symlink.h"
#include "../../fs_interface/OpenFile.h"
class FileSystemTestFixture {
public:
virtual ~FileSystemTestFixture() {}
virtual cpputils::unique_ref<fspp::Device> createDevice() = 0;
};
template<class ConcreteFileSystemTestFixture>
class FileSystemTest: public ::testing::Test {
public:
BOOST_STATIC_ASSERT_MSG(
(std::is_base_of<FileSystemTestFixture, ConcreteFileSystemTestFixture>::value),
"Given test fixture for instantiating the (type parameterized) FileSystemTest must inherit from FileSystemTestFixture"
);
FileSystemTest(): fixture(), device(fixture.createDevice()) {}
ConcreteFileSystemTestFixture fixture;
cpputils::unique_ref<fspp::Device> device;
static constexpr mode_t MODE_PUBLIC = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH;
cpputils::unique_ref<fspp::Dir> LoadDir(const boost::filesystem::path &path) {
auto loaded = device->Load(path);
EXPECT_NE(boost::none, loaded);
auto dir = cpputils::dynamic_pointer_move<fspp::Dir>(*loaded);
EXPECT_NE(boost::none, dir);
return std::move(*dir);
}
cpputils::unique_ref<fspp::File> LoadFile(const boost::filesystem::path &path) {
auto loaded = device->Load(path);
EXPECT_NE(boost::none, loaded);
auto file = cpputils::dynamic_pointer_move<fspp::File>(*loaded);
EXPECT_NE(boost::none, file);
return std::move(*file);
}
cpputils::unique_ref<fspp::Symlink> LoadSymlink(const boost::filesystem::path &path) {
auto loaded = device->Load(path);
EXPECT_NE(boost::none, loaded);
auto symlink = cpputils::dynamic_pointer_move<fspp::Symlink>(*loaded);
EXPECT_NE(boost::none, symlink);
return std::move(*symlink);
}
};
#endif

View File

@ -0,0 +1,51 @@
#pragma once
#ifndef MESSMER_FSPP_FSTEST_TESTUTILS_FILETEST_H_
#define MESSMER_FSPP_FSTEST_TESTUTILS_FILETEST_H_
#include "FileSystemTest.h"
#include <messmer/cpp-utils/data/Data.h>
#include <messmer/cpp-utils/pointer/unique_ref.h>
template<class ConcreteFileSystemTestFixture>
class FileTest: public FileSystemTest<ConcreteFileSystemTestFixture> {
public:
FileTest(): file_root(), file_nested() {
this->LoadDir("/")->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0);
file_root = cpputils::to_unique_ptr(this->LoadFile("/myfile"));
this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0);
this->LoadDir("/mydir")->createAndOpenFile("mynestedfile", this->MODE_PUBLIC, 0, 0);
file_nested = cpputils::to_unique_ptr(this->LoadFile("/mydir/mynestedfile"));
}
std::unique_ptr<fspp::File> file_root;
std::unique_ptr<fspp::File> file_nested;
void EXPECT_SIZE(uint64_t expectedSize, const fspp::File &file) {
EXPECT_SIZE_IN_FILE(expectedSize, file);
auto openFile = file.open(O_RDONLY);
EXPECT_SIZE_IN_OPEN_FILE(expectedSize, *openFile);
EXPECT_NUMBYTES_READABLE(expectedSize, *openFile);
}
void EXPECT_SIZE_IN_FILE(uint64_t expectedSize, const fspp::File &file) {
struct stat st;
file.stat(&st);
EXPECT_EQ(expectedSize, st.st_size);
}
void EXPECT_SIZE_IN_OPEN_FILE(uint64_t expectedSize, const fspp::OpenFile &file) {
struct stat st;
file.stat(&st);
EXPECT_EQ(expectedSize, st.st_size);
}
void EXPECT_NUMBYTES_READABLE(uint64_t expectedSize, const fspp::OpenFile &file) {
cpputils::Data data(expectedSize);
//Try to read one byte more than the expected size
ssize_t readBytes = file.read(data.data(), expectedSize+1, 0);
//and check that it only read the expected size (but also not less)
EXPECT_EQ(expectedSize, readBytes);
}
};
#endif

53
fuse/Filesystem.h Normal file
View File

@ -0,0 +1,53 @@
#pragma once
#ifndef MESSMER_FSPP_FUSE_FILESYSTEM_H_
#define MESSMER_FSPP_FUSE_FILESYSTEM_H_
#include <boost/filesystem.hpp>
#include <messmer/cpp-utils/pointer/unique_ref.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include "../fs_interface/Dir.h"
namespace fspp {
namespace fuse {
class Filesystem {
public:
virtual ~Filesystem() {}
//TODO Test uid/gid parameters of createAndOpenFile
virtual int createAndOpenFile(const boost::filesystem::path &path, mode_t mode, uid_t uid, gid_t gid) = 0;
virtual int openFile(const boost::filesystem::path &path, int flags) = 0;
virtual void flush(int descriptor) = 0;
virtual void closeFile(int descriptor) = 0;
virtual void lstat(const boost::filesystem::path &path, struct ::stat *stbuf) = 0;
virtual void fstat(int descriptor, struct ::stat *stbuf) = 0;
//TODO Test chmod
virtual void chmod(const boost::filesystem::path &path, mode_t mode) = 0;
//TODO Test chown
virtual void chown(const boost::filesystem::path &path, uid_t uid, gid_t gid) = 0;
virtual void truncate(const boost::filesystem::path &path, off_t size) = 0;
virtual void ftruncate(int descriptor, off_t size) = 0;
virtual size_t read(int descriptor, void *buf, size_t count, off_t offset) = 0;
virtual void write(int descriptor, const void *buf, size_t count, off_t offset) = 0;
virtual void fsync(int descriptor) = 0;
virtual void fdatasync(int descriptor) = 0;
virtual void access(const boost::filesystem::path &path, int mask) = 0;
//TODO Test uid/gid parameters of mkdir
virtual void mkdir(const boost::filesystem::path &path, mode_t mode, uid_t uid, gid_t gid) = 0;
virtual void rmdir(const boost::filesystem::path &path) = 0;
virtual void unlink(const boost::filesystem::path &path) = 0;
virtual void rename(const boost::filesystem::path &from, const boost::filesystem::path &to) = 0;
virtual void utimens(const boost::filesystem::path &path, timespec lastAccessTime, timespec lastModificationTime) = 0;
virtual void statfs(const boost::filesystem::path &path, struct statvfs *fsstat) = 0;
//TODO We shouldn't use Dir::Entry here, that's in another layer
virtual cpputils::unique_ref<std::vector<Dir::Entry>> readDir(const boost::filesystem::path &path) = 0;
//TODO Test createSymlink
virtual void createSymlink(const boost::filesystem::path &to, const boost::filesystem::path &from, uid_t uid, gid_t gid) = 0;
//TODO Test readSymlink
virtual void readSymlink(const boost::filesystem::path &path, char *buf, size_t size) = 0;
};
}
}
#endif

831
fuse/Fuse.cpp Normal file
View File

@ -0,0 +1,831 @@
#include "Fuse.h"
#include <memory>
#include <cassert>
#include "FuseErrnoException.h"
#include "Filesystem.h"
#include <iostream>
#include <messmer/cpp-utils/assert/assert.h>
#include <messmer/cpp-utils/logging/logging.h>
#include <csignal>
using std::vector;
using std::string;
namespace bf = boost::filesystem;
using namespace cpputils::logging;
using namespace fspp::fuse;
#define FUSE_OBJ ((Fuse *) fuse_get_context()->private_data)
// Remove the following line, if you don't want to output each fuse operation on the console
//#define FSPP_LOG 1
namespace {
int fusepp_getattr(const char *path, struct stat *stbuf) {
int rs = FUSE_OBJ->getattr(bf::path(path), stbuf);
return rs;
}
int fusepp_fgetattr(const char *path, struct stat *stbuf, fuse_file_info *fileinfo) {
return FUSE_OBJ->fgetattr(bf::path(path), stbuf, fileinfo);
}
int fusepp_readlink(const char *path, char *buf, size_t size) {
return FUSE_OBJ->readlink(bf::path(path), buf, size);
}
int fusepp_mknod(const char *path, mode_t mode, dev_t rdev) {
return FUSE_OBJ->mknod(bf::path(path), mode, rdev);
}
int fusepp_mkdir(const char *path, mode_t mode) {
return FUSE_OBJ->mkdir(bf::path(path), mode);
}
int fusepp_unlink(const char *path) {
return FUSE_OBJ->unlink(bf::path(path));
}
int fusepp_rmdir(const char *path) {
return FUSE_OBJ->rmdir(bf::path(path));
}
int fusepp_symlink(const char *to, const char *from) {
return FUSE_OBJ->symlink(bf::path(to), bf::path(from));
}
int fusepp_rename(const char *from, const char *to) {
return FUSE_OBJ->rename(bf::path(from), bf::path(to));
}
int fusepp_link(const char *from, const char *to) {
return FUSE_OBJ->link(bf::path(from), bf::path(to));
}
int fusepp_chmod(const char *path, mode_t mode) {
return FUSE_OBJ->chmod(bf::path(path), mode);
}
int fusepp_chown(const char *path, uid_t uid, gid_t gid) {
return FUSE_OBJ->chown(bf::path(path), uid, gid);
}
int fusepp_truncate(const char *path, off_t size) {
return FUSE_OBJ->truncate(bf::path(path), size);
}
int fusepp_ftruncate(const char *path, off_t size, fuse_file_info *fileinfo) {
return FUSE_OBJ->ftruncate(bf::path(path), size, fileinfo);
}
int fusepp_utimens(const char *path, const timespec times[2]) {
return FUSE_OBJ->utimens(bf::path(path), times);
}
int fusepp_open(const char *path, fuse_file_info *fileinfo) {
return FUSE_OBJ->open(bf::path(path), fileinfo);
}
int fusepp_release(const char *path, fuse_file_info *fileinfo) {
return FUSE_OBJ->release(bf::path(path), fileinfo);
}
int fusepp_read(const char *path, char *buf, size_t size, off_t offset, fuse_file_info *fileinfo) {
return FUSE_OBJ->read(bf::path(path), buf, size, offset, fileinfo);
}
int fusepp_write(const char *path, const char *buf, size_t size, off_t offset, fuse_file_info *fileinfo) {
return FUSE_OBJ->write(bf::path(path), buf, size, offset, fileinfo);
}
int fusepp_statfs(const char *path, struct statvfs *fsstat) {
return FUSE_OBJ->statfs(bf::path(path), fsstat);
}
int fusepp_flush(const char *path, fuse_file_info *fileinfo) {
return FUSE_OBJ->flush(bf::path(path), fileinfo);
}
int fusepp_fsync(const char *path, int datasync, fuse_file_info *fileinfo) {
return FUSE_OBJ->fsync(bf::path(path), datasync, fileinfo);
}
//int fusepp_setxattr(const char*, const char*, const char*, size_t, int)
//int fusepp_getxattr(const char*, const char*, char*, size_t)
//int fusepp_listxattr(const char*, char*, size_t)
//int fusepp_removexattr(const char*, const char*)
int fusepp_opendir(const char *path, fuse_file_info *fileinfo) {
return FUSE_OBJ->opendir(bf::path(path), fileinfo);
}
int fusepp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, fuse_file_info *fileinfo) {
return FUSE_OBJ->readdir(bf::path(path), buf, filler, offset, fileinfo);
}
int fusepp_releasedir(const char *path, fuse_file_info *fileinfo) {
return FUSE_OBJ->releasedir(bf::path(path), fileinfo);
}
int fusepp_fsyncdir(const char *path, int datasync, fuse_file_info *fileinfo) {
return FUSE_OBJ->fsyncdir(bf::path(path), datasync, fileinfo);
}
void* fusepp_init(fuse_conn_info *conn) {
auto f = FUSE_OBJ;
f->init(conn);
return f;
}
void fusepp_destroy(void *userdata) {
auto f = FUSE_OBJ;
ASSERT(userdata == f, "Wrong userdata set");
UNUSED(userdata); //In case the assert is disabled
f->destroy();
}
int fusepp_access(const char *path, int mask) {
return FUSE_OBJ->access(bf::path(path), mask);
}
int fusepp_create(const char *path, mode_t mode, fuse_file_info *fileinfo) {
return FUSE_OBJ->create(bf::path(path), mode, fileinfo);
}
/*int fusepp_lock(const char*, fuse_file_info*, int cmd, flock*)
int fusepp_bmap(const char*, size_t blocksize, uint64_t *idx)
int fusepp_ioctl(const char*, int cmd, void *arg, fuse_file_info*, unsigned int flags, void *data)
int fusepp_poll(const char*, fuse_file_info*, fuse_pollhandle *ph, unsigned *reventsp)
int fusepp_write_buf(const char*, fuse_bufvec *buf, off_t off, fuse_file_info*)
int fusepp_read_buf(const chas*, struct fuse_bufvec **bufp, size_t size, off_T off, fuse_file_info*)
int fusepp_flock(const char*, fuse_file_info*, int op)
int fusepp_fallocate(const char*, int, off_t, off_t, fuse_file_info*)*/
fuse_operations *operations() {
static std::unique_ptr<fuse_operations> singleton(nullptr);
if (!singleton) {
singleton = std::make_unique<fuse_operations>();
singleton->getattr = &fusepp_getattr;
singleton->fgetattr = &fusepp_fgetattr;
singleton->readlink = &fusepp_readlink;
singleton->mknod = &fusepp_mknod;
singleton->mkdir = &fusepp_mkdir;
singleton->unlink = &fusepp_unlink;
singleton->rmdir = &fusepp_rmdir;
singleton->symlink = &fusepp_symlink;
singleton->rename = &fusepp_rename;
singleton->link = &fusepp_link;
singleton->chmod = &fusepp_chmod;
singleton->chown = &fusepp_chown;
singleton->truncate = &fusepp_truncate;
singleton->utimens = &fusepp_utimens;
singleton->open = &fusepp_open;
singleton->read = &fusepp_read;
singleton->write = &fusepp_write;
singleton->statfs = &fusepp_statfs;
singleton->flush = &fusepp_flush;
singleton->release = &fusepp_release;
singleton->fsync = &fusepp_fsync;
/*#ifdef HAVE_SYS_XATTR_H
singleton->setxattr = &fusepp_setxattr;
singleton->getxattr = &fusepp_getxattr;
singleton->listxattr = &fusepp_listxattr;
singleton->removexattr = &fusepp_removexattr;
#endif*/
singleton->opendir = &fusepp_opendir;
singleton->readdir = &fusepp_readdir;
singleton->releasedir = &fusepp_releasedir;
singleton->fsyncdir = &fusepp_fsyncdir;
singleton->init = &fusepp_init;
singleton->destroy = &fusepp_destroy;
singleton->access = &fusepp_access;
singleton->create = &fusepp_create;
singleton->ftruncate = &fusepp_ftruncate;
}
return singleton.get();
}
}
Fuse::~Fuse() {
}
Fuse::Fuse(Filesystem *fs)
:_fs(fs), _mountdir(), _running(false) {
}
void Fuse::_logException(const std::exception &e) {
LOG(ERROR) << "Exception thrown: " << e.what();
}
void Fuse::_logUnknownException() {
LOG(ERROR) << "Unknown exception thrown";
}
void Fuse::run(int argc, char **argv) {
vector<char*> _argv(argv, argv + argc);
_mountdir = argv[1];
fuse_main(_argv.size(), _argv.data(), operations(), (void*)this);
}
bool Fuse::running() const {
return _running;
}
void Fuse::stop() {
//TODO Find better way to unmount (i.e. don't use external fusermount). Unmounting by kill(getpid(), SIGINT) worked, but left the mount directory transport endpoint as not connected.
int ret = system(("fusermount -z -u " + _mountdir.native()).c_str()); // "-z" takes care that if the filesystem can't be unmounted right now because something is opened, it will be unmounted as soon as it can be.
if (ret != 0) {
LOG(ERROR) << "Could not unmount filesystem";
}
}
int Fuse::getattr(const bf::path &path, struct stat *stbuf) {
#ifdef FSPP_LOG
LOG(DEBUG) << "getattr(" << path << ", _, _)";
#endif
try {
_fs->lstat(path, stbuf);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::getattr: " << e.what();
return -EIO;
} catch(fspp::fuse::FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::fgetattr(const bf::path &path, struct stat *stbuf, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG
LOG(DEBUG) << "fgetattr(" << path << ", _, _)\n";
#endif
// On FreeBSD, trying to do anything with the mountpoint ends up
// opening it, and then using the FD for an fgetattr. So in the
// special case of a path of "/", I need to do a getattr on the
// underlying root directory instead of doing the fgetattr().
// TODO Check if necessary
if (path.native() == "/") {
return getattr(path, stbuf);
}
try {
_fs->fstat(fileinfo->fh, stbuf);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::fgetattr: " << e.what();
return -EIO;
} catch(fspp::fuse::FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::readlink(const bf::path &path, char *buf, size_t size) {
#ifdef FSPP_LOG
LOG(DEBUG) << "readlink(" << path << ", _, " << size << ")";
#endif
try {
_fs->readSymlink(path, buf, size);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::readlink: " << e.what();
return -EIO;
} catch (fspp::fuse::FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::mknod(const bf::path &path, mode_t mode, dev_t rdev) {
UNUSED(rdev);
UNUSED(mode);
UNUSED(path);
LOG(WARN) << "Called non-implemented mknod(" << path << ", " << mode << ", _)";
return ENOSYS;
}
int Fuse::mkdir(const bf::path &path, mode_t mode) {
#ifdef FSPP_LOG
LOG(DEBUG) << "mkdir(" << path << ", " << mode << ")";
#endif
try {
auto context = fuse_get_context();
_fs->mkdir(path, mode, context->uid, context->gid);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::mkdir: " << e.what();
return -EIO;
} catch(fspp::fuse::FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::unlink(const bf::path &path) {
#ifdef FSPP_LOG
LOG(DEBUG) << "unlink(" << path << ")";
#endif
try {
_fs->unlink(path);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::unlink: " << e.what();
return -EIO;
} catch(fspp::fuse::FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::rmdir(const bf::path &path) {
#ifdef FSPP_LOG
LOG(DEBUG) << "rmdir(" << path << ")";
#endif
try {
_fs->rmdir(path);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::rmdir: " << e.what();
return -EIO;
} catch(fspp::fuse::FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::symlink(const bf::path &from, const bf::path &to) {
#ifdef FSPP_LOG
LOG(DEBUG) << "symlink(" << from << ", " << to << ")";
#endif
try {
auto context = fuse_get_context();
_fs->createSymlink(from, to, context->uid, context->gid);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::symlink: " << e.what();
return -EIO;
} catch(fspp::fuse::FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::rename(const bf::path &from, const bf::path &to) {
#ifdef FSPP_LOG
LOG(DEBUG) << "rename(" << from << ", " << to << ")";
#endif
try {
_fs->rename(from, to);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::rename: " << e.what();
return -EIO;
} catch(fspp::fuse::FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
//TODO
int Fuse::link(const bf::path &from, const bf::path &to) {
LOG(WARN) << "NOT IMPLEMENTED: link(" << from << ", " << to << ")";
//auto real_from = _impl->RootDir() / from;
//auto real_to = _impl->RootDir() / to;
//int retstat = ::link(real_from.c_str(), real_to.c_str());
//return errcode_map(retstat);
return ENOSYS;
}
int Fuse::chmod(const bf::path &path, mode_t mode) {
#ifdef FSPP_LOG
LOG(DEBUG) << "chmod(" << path << ", " << mode << ")";
#endif
try {
_fs->chmod(path, mode);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::chmod: " << e.what();
return -EIO;
} catch (fspp::fuse::FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::chown(const bf::path &path, uid_t uid, gid_t gid) {
#ifdef FSPP_LOG
LOG(DEBUG) << "chown(" << path << ", " << uid << ", " << gid << ")";
#endif
try {
_fs->chown(path, uid, gid);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::chown: " << e.what();
return -EIO;
} catch (fspp::fuse::FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::truncate(const bf::path &path, off_t size) {
#ifdef FSPP_LOG
LOG(DEBUG) << "truncate(" << path << ", " << size << ")";
#endif
try {
_fs->truncate(path, size);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::truncate: " << e.what();
return -EIO;
} catch (FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::ftruncate(const bf::path &path, off_t size, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG
LOG(DEBUG) << "ftruncate(" << path << ", " << size << ")";
#endif
UNUSED(path);
try {
_fs->ftruncate(fileinfo->fh, size);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::ftruncate: " << e.what();
return -EIO;
} catch (FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::utimens(const bf::path &path, const timespec times[2]) {
#ifdef FSPP_LOG
LOG(DEBUG) << "utimens(" << path << ", _)";
#endif
try {
_fs->utimens(path, times[0], times[1]);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::utimens: " << e.what();
return -EIO;
} catch (FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::open(const bf::path &path, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG
LOG(DEBUG) << "open(" << path << ", _)";
#endif
try {
fileinfo->fh = _fs->openFile(path, fileinfo->flags);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::open: " << e.what();
return -EIO;
} catch (FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::release(const bf::path &path, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG
LOG(DEBUG) << "release(" << path << ", _)";
#endif
UNUSED(path);
try {
_fs->closeFile(fileinfo->fh);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::release: " << e.what();
return -EIO;
} catch (FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::read(const bf::path &path, char *buf, size_t size, off_t offset, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG
LOG(DEBUG) << "read(" << path << ", _, " << size << ", " << offset << ", _ )";
#endif
UNUSED(path);
try {
return _fs->read(fileinfo->fh, buf, size, offset);
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::read: " << e.what();
return -EIO;
} catch (FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::write(const bf::path &path, const char *buf, size_t size, off_t offset, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG
LOG(DEBUG) << "write(" << path << ", _, " << size << ", " << offset << ", _)";
#endif
UNUSED(path);
try {
_fs->write(fileinfo->fh, buf, size, offset);
return size;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::write: " << e.what();
return -EIO;
} catch (FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
//TODO
int Fuse::statfs(const bf::path &path, struct statvfs *fsstat) {
#ifdef FSPP_LOG
LOG(DEBUG) << "statfs(" << path << ", _)";
#endif
try {
_fs->statfs(path, fsstat);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::statfs: " << e.what();
return -EIO;
} catch (FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
//TODO
int Fuse::flush(const bf::path &path, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG
//TODO Implement it
LOG(WARN) << "Called non-implemented flush(" << path << ", _)";
#endif
UNUSED(path);
try {
_fs->flush(fileinfo->fh);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::flush: " << e.what();
return -EIO;
} catch (FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::fsync(const bf::path &path, int datasync, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG
LOG(DEBUG) << "fsync(" << path << ", " << datasync << ", _)";
#endif
UNUSED(path);
try {
if (datasync) {
_fs->fdatasync(fileinfo->fh);
} else {
_fs->fsync(fileinfo->fh);
}
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::fsync: " << e.what();
return -EIO;
} catch (FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::opendir(const bf::path &path, fuse_file_info *fileinfo) {
UNUSED(path);
UNUSED(fileinfo);
//LOG(DEBUG) << "opendir(" << path << ", _)";
//We don't need opendir, because readdir works directly on the path
return 0;
}
int Fuse::readdir(const bf::path &path, void *buf, fuse_fill_dir_t filler, off_t offset, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG
LOG(DEBUG) << "readdir(" << path << ", _, _, " << offset << ", _)";
#endif
UNUSED(fileinfo);
UNUSED(offset);
try {
auto entries = _fs->readDir(path);
struct stat stbuf;
for (const auto &entry : *entries) {
//We could pass more file metadata to filler() in its third parameter,
//but it doesn't help performance since fuse ignores everything in stbuf
//except for file-type bits in st_mode and (if used) st_ino.
//It does getattr() calls on all entries nevertheless.
if (entry.type == Dir::EntryType::DIR) {
stbuf.st_mode = S_IFDIR;
} else if (entry.type == Dir::EntryType::FILE) {
stbuf.st_mode = S_IFREG;
} else if (entry.type == Dir::EntryType::SYMLINK) {
stbuf.st_mode = S_IFLNK;
} else {
ASSERT(false, "Unknown entry type");
}
if (filler(buf, entry.name.c_str(), &stbuf, 0) != 0) {
return -ENOMEM;
}
}
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::readdir: " << e.what();
return -EIO;
} catch (FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::releasedir(const bf::path &path, fuse_file_info *fileinfo) {
UNUSED(path);
UNUSED(fileinfo);
//LOG(DEBUG) << "releasedir(" << path << ", _)";
//We don't need releasedir, because readdir works directly on the path
return 0;
}
//TODO
int Fuse::fsyncdir(const bf::path &path, int datasync, fuse_file_info *fileinfo) {
UNUSED(fileinfo);
UNUSED(datasync);
UNUSED(path);
//LOG(WARN) << "Called non-implemented fsyncdir(" << path << ", " << datasync << ", _)";
return 0;
}
void Fuse::init(fuse_conn_info *conn) {
UNUSED(conn);
_running = true;
#ifdef FSPP_LOG
cpputils::logging::setLevel(DEBUG);
#endif
LOG(INFO) << "Filesystem started.";
}
void Fuse::destroy() {
_running = false;
LOG(INFO) << "Filesystem stopped.";
}
int Fuse::access(const bf::path &path, int mask) {
#ifdef FSPP_LOG
LOG(DEBUG) << "access(" << path << ", " << mask << ")";
#endif
try {
_fs->access(path, mask);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::access: " << e.what();
return -EIO;
} catch (FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}
int Fuse::create(const bf::path &path, mode_t mode, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG
LOG(DEBUG) << "create(" << path << ", " << mode << ", _)";
#endif
try {
auto context = fuse_get_context();
fileinfo->fh = _fs->createAndOpenFile(path, mode, context->uid, context->gid);
return 0;
} catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::create: " << e.what();
return -EIO;
} catch (FuseErrnoException &e) {
return -e.getErrno();
} catch(const std::exception &e) {
_logException(e);
return -EIO;
} catch(...) {
_logUnknownException();
return -EIO;
}
}

72
fuse/Fuse.h Normal file
View File

@ -0,0 +1,72 @@
#pragma once
#ifndef MESSMER_FSPP_FUSE_FUSE_H_
#define MESSMER_FSPP_FUSE_FUSE_H_
#include "params.h"
#include <cstdio>
#include <string>
#include <vector>
#include <sys/stat.h>
#include <boost/filesystem.hpp>
#include "messmer/cpp-utils/macros.h"
namespace fspp {
class Device;
namespace fuse {
class Filesystem;
class Fuse final {
public:
explicit Fuse(Filesystem *fs);
~Fuse();
void run(int argc, char **argv);
bool running() const;
void stop();
int getattr(const boost::filesystem::path &path, struct stat *stbuf);
int fgetattr(const boost::filesystem::path &path, struct stat *stbuf, fuse_file_info *fileinfo);
int readlink(const boost::filesystem::path &path, char *buf, size_t size);
int mknod(const boost::filesystem::path &path, mode_t mode, dev_t rdev);
int mkdir(const boost::filesystem::path &path, mode_t mode);
int unlink(const boost::filesystem::path &path);
int rmdir(const boost::filesystem::path &path);
int symlink(const boost::filesystem::path &from, const boost::filesystem::path &to);
int rename(const boost::filesystem::path &from, const boost::filesystem::path &to);
int link(const boost::filesystem::path &from, const boost::filesystem::path &to);
int chmod(const boost::filesystem::path &path, mode_t mode);
int chown(const boost::filesystem::path &path, uid_t uid, gid_t gid);
int truncate(const boost::filesystem::path &path, off_t size);
int ftruncate(const boost::filesystem::path &path, off_t size, fuse_file_info *fileinfo);
int utimens(const boost::filesystem::path &path, const timespec times[2]);
int open(const boost::filesystem::path &path, fuse_file_info *fileinfo);
int release(const boost::filesystem::path &path, fuse_file_info *fileinfo);
int read(const boost::filesystem::path &path, char *buf, size_t size, off_t offset, fuse_file_info *fileinfo);
int write(const boost::filesystem::path &path, const char *buf, size_t size, off_t offset, fuse_file_info *fileinfo);
int statfs(const boost::filesystem::path &path, struct statvfs *fsstat);
int flush(const boost::filesystem::path &path, fuse_file_info *fileinfo);
int fsync(const boost::filesystem::path &path, int flags, fuse_file_info *fileinfo);
int opendir(const boost::filesystem::path &path, fuse_file_info *fileinfo);
int readdir(const boost::filesystem::path &path, void *buf, fuse_fill_dir_t filler, off_t offset, fuse_file_info *fileinfo);
int releasedir(const boost::filesystem::path &path, fuse_file_info *fileinfo);
int fsyncdir(const boost::filesystem::path &path, int datasync, fuse_file_info *fileinfo);
void init(fuse_conn_info *conn);
void destroy();
int access(const boost::filesystem::path &path, int mask);
int create(const boost::filesystem::path &path, mode_t mode, fuse_file_info *fileinfo);
private:
static void _logException(const std::exception &e);
static void _logUnknownException();
Filesystem *_fs;
boost::filesystem::path _mountdir;
bool _running;
DISALLOW_COPY_AND_ASSIGN(Fuse);
};
}
}
#endif

39
fuse/FuseErrnoException.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#ifndef MESSMER_FSPP_FUSE_FUSEERRNOEXCEPTION_H_
#define MESSMER_FSPP_FUSE_FUSEERRNOEXCEPTION_H_
#include <stdexcept>
#include <errno.h>
#include <messmer/cpp-utils/assert/assert.h>
namespace fspp {
namespace fuse{
class FuseErrnoException final: public std::runtime_error {
public:
explicit FuseErrnoException(int errno_);
int getErrno() const;
private:
int _errno;
};
inline void CHECK_RETVAL(int retval) {
if (retval < 0) {
throw FuseErrnoException(errno);
}
}
inline FuseErrnoException::FuseErrnoException(int errno_)
:runtime_error(strerror(errno_)), _errno(errno_) {
ASSERT(_errno != 0, "Errno shouldn't be zero");
}
inline int FuseErrnoException::getErrno() const {
return _errno;
}
}
}
#endif

8
fuse/params.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#ifndef MESSMER_FSPP_FUSE_PARAMS_H_
#define MESSMER_FSPP_FUSE_PARAMS_H_
#define FUSE_USE_VERSION 26
#include <fuse.h>
#endif

321
impl/FilesystemImpl.cpp Normal file
View File

@ -0,0 +1,321 @@
#include "FilesystemImpl.h"
#include <fcntl.h>
#include "../fs_interface/Device.h"
#include "../fs_interface/Dir.h"
#include "../fs_interface/Symlink.h"
#include "../fuse/FuseErrnoException.h"
#include "../fs_interface/File.h"
#include <messmer/cpp-utils/logging/logging.h>
#include <messmer/cpp-utils/pointer/unique_ref.h>
#include <sstream>
using namespace fspp;
using cpputils::dynamic_pointer_move;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
using std::vector;
using std::string;
using boost::none;
namespace bf = boost::filesystem;
using namespace cpputils::logging;
#ifdef FSPP_PROFILE
#include "Profiler.h"
#include <iomanip>
#include <ios>
#define PROFILE(name) Profiler profiler_##name(&name);
#else
#define PROFILE(name)
#endif
FilesystemImpl::FilesystemImpl(Device *device)
:
#ifdef FSPP_PROFILE
_loadFileNanosec(0), _loadDirNanosec(0), _loadSymlinkNanosec(0), _openFileNanosec(0), _flushNanosec(0),
_closeFileNanosec(0), _lstatNanosec(0), _fstatNanosec(0), _chmodNanosec(0), _chownNanosec(0), _truncateNanosec(0),
_ftruncateNanosec(0), _readNanosec(0), _writeNanosec(0), _fsyncNanosec(0), _fdatasyncNanosec(0), _accessNanosec(0),
_createAndOpenFileNanosec(0), _createAndOpenFileNanosec_withoutLoading(0), _mkdirNanosec(0),
_mkdirNanosec_withoutLoading(0), _rmdirNanosec(0), _rmdirNanosec_withoutLoading(0), _unlinkNanosec(0),
_unlinkNanosec_withoutLoading(0), _renameNanosec(0), _readDirNanosec(0), _readDirNanosec_withoutLoading(0),
_utimensNanosec(0), _statfsNanosec(0), _createSymlinkNanosec(0), _createSymlinkNanosec_withoutLoading(0),
_readSymlinkNanosec(0), _readSymlinkNanosec_withoutLoading(0),
#endif
_device(device), _open_files()
{
}
FilesystemImpl::~FilesystemImpl() {
#ifdef FSPP_PROFILE
std::ostringstream profilerInformation;
profilerInformation << "Profiler Information\n"
<< std::fixed << std::setprecision(6)
<< std::setw(40) << "LoadFile: " << static_cast<double>(_loadFileNanosec)/1000000000 << "\n"
<< std::setw(40) << "LoadDir: " << static_cast<double>(_loadDirNanosec)/1000000000 << "\n"
<< std::setw(40) << "LoadSymlink: " << static_cast<double>(_loadSymlinkNanosec)/1000000000 << "\n"
<< std::setw(40) << "OpenFile: " << static_cast<double>(_openFileNanosec)/1000000000 << "\n"
<< std::setw(40) << "Flush: " << static_cast<double>(_flushNanosec)/1000000000 << "\n"
<< std::setw(40) << "CloseFile: " << static_cast<double>(_closeFileNanosec)/1000000000 << "\n"
<< std::setw(40) << "Lstat: " << static_cast<double>(_lstatNanosec)/1000000000 << "\n"
<< std::setw(40) << "Fstat: " << static_cast<double>(_fstatNanosec)/1000000000 << "\n"
<< std::setw(40) << "Chmod: " << static_cast<double>(_chmodNanosec)/1000000000 << "\n"
<< std::setw(40) << "Chown: " << static_cast<double>(_chownNanosec)/1000000000 << "\n"
<< std::setw(40) << "Truncate: " << static_cast<double>(_truncateNanosec)/1000000000 << "\n"
<< std::setw(40) << "Ftruncate: " << static_cast<double>(_ftruncateNanosec)/1000000000 << "\n"
<< std::setw(40) << "Read: " << static_cast<double>(_readNanosec)/1000000000 << "\n"
<< std::setw(40) << "Write: " << static_cast<double>(_writeNanosec)/1000000000 << "\n"
<< std::setw(40) << "Fsync: " << static_cast<double>(_fsyncNanosec)/1000000000 << "\n"
<< std::setw(40) << "Fdatasync: " << static_cast<double>(_fdatasyncNanosec)/1000000000 << "\n"
<< std::setw(40) << "Access: " << static_cast<double>(_accessNanosec)/1000000000 << "\n"
<< std::setw(40) << "CreateAndOpenFile: " << static_cast<double>(_createAndOpenFileNanosec)/1000000000 << "\n"
<< std::setw(40) << "CreateAndOpenFile (without loading): " << static_cast<double>(_createAndOpenFileNanosec_withoutLoading)/1000000000 << "\n"
<< std::setw(40) << "Mkdir: " << static_cast<double>(_mkdirNanosec)/1000000000 << "\n"
<< std::setw(40) << "Mkdir (without loading): " << static_cast<double>(_mkdirNanosec_withoutLoading)/1000000000 << "\n"
<< std::setw(40) << "Rmdir: " << static_cast<double>(_rmdirNanosec)/1000000000 << "\n"
<< std::setw(40) << "Rmdir (without loading): " << static_cast<double>(_rmdirNanosec_withoutLoading)/1000000000 << "\n"
<< std::setw(40) << "Unlink: " << static_cast<double>(_unlinkNanosec)/1000000000 << "\n"
<< std::setw(40) << "Unlink (without loading): " << static_cast<double>(_unlinkNanosec_withoutLoading)/1000000000 << "\n"
<< std::setw(40) << "Rename: " << static_cast<double>(_renameNanosec)/1000000000 << "\n"
<< std::setw(40) << "ReadDir: " << static_cast<double>(_readDirNanosec)/1000000000 << "\n"
<< std::setw(40) << "ReadDir (without loading): " << static_cast<double>(_readDirNanosec_withoutLoading)/1000000000 << "\n"
<< std::setw(40) << "Utimens: " << static_cast<double>(_utimensNanosec)/1000000000 << "\n"
<< std::setw(40) << "Statfs: " << static_cast<double>(_statfsNanosec)/1000000000 << "\n"
<< std::setw(40) << "CreateSymlink: " << static_cast<double>(_createSymlinkNanosec)/1000000000 << "\n"
<< std::setw(40) << "CreateSymlink (without loading): " << static_cast<double>(_createSymlinkNanosec_withoutLoading)/1000000000 << "\n"
<< std::setw(40) << "ReadSymlink: " << static_cast<double>(_readSymlinkNanosec)/1000000000 << "\n"
<< std::setw(40) << "ReadSymlink (without loading): " << static_cast<double>(_readSymlinkNanosec_withoutLoading)/1000000000 << "\n";
LOG(INFO) << profilerInformation.str();
#endif
}
unique_ref<File> FilesystemImpl::LoadFile(const bf::path &path) {
PROFILE(_loadFileNanosec);
auto node = _device->Load(path);
if (node == none) {
throw fuse::FuseErrnoException(EIO);
}
auto file = dynamic_pointer_move<File>(*node);
if (file == none) {
throw fuse::FuseErrnoException(EISDIR);
}
return std::move(*file);
}
unique_ref<Dir> FilesystemImpl::LoadDir(const bf::path &path) {
PROFILE(_loadDirNanosec);
auto node = _device->Load(path);
if (node == none) {
throw fuse::FuseErrnoException(EIO);
}
auto dir = dynamic_pointer_move<Dir>(*node);
if (dir == none) {
throw fuse::FuseErrnoException(ENOTDIR);
}
return std::move(*dir);
}
unique_ref<Symlink> FilesystemImpl::LoadSymlink(const bf::path &path) {
PROFILE(_loadSymlinkNanosec);
auto node = _device->Load(path);
if (node == none) {
throw fuse::FuseErrnoException(EIO);
}
auto lnk = dynamic_pointer_move<Symlink>(*node);
if (lnk == none) {
throw fuse::FuseErrnoException(ENOTDIR);
}
return std::move(*lnk);
}
unique_ref<Node> FilesystemImpl::LoadFileOrSymlink(const bf::path &path) {
PROFILE(_loadFileOrSymlinkNanosec);
auto node = _device->Load(path);
if (node == none) {
throw fuse::FuseErrnoException(EIO);
}
auto file = dynamic_pointer_move<File>(*node);
if (file != none) {
return std::move(*file);
}
auto symlink = dynamic_pointer_move<Symlink>(*node);
if (symlink != none) {
return std::move(*symlink);
}
throw fuse::FuseErrnoException(EISDIR);
}
int FilesystemImpl::openFile(const bf::path &path, int flags) {
auto file = LoadFile(path);
return openFile(*file, flags);
}
int FilesystemImpl::openFile(const File &file, int flags) {
PROFILE(_openFileNanosec);
return _open_files.open(file.open(flags));
}
void FilesystemImpl::flush(int descriptor) {
PROFILE(_flushNanosec);
_open_files.get(descriptor)->flush();
}
void FilesystemImpl::closeFile(int descriptor) {
PROFILE(_closeFileNanosec);
_open_files.close(descriptor);
}
void FilesystemImpl::lstat(const bf::path &path, struct ::stat *stbuf) {
PROFILE(_lstatNanosec);
auto node = _device->Load(path);
if(node == none) {
throw fuse::FuseErrnoException(ENOENT);
} else {
(*node)->stat(stbuf);
}
}
void FilesystemImpl::fstat(int descriptor, struct ::stat *stbuf) {
PROFILE(_fstatNanosec);
_open_files.get(descriptor)->stat(stbuf);
}
void FilesystemImpl::chmod(const boost::filesystem::path &path, mode_t mode) {
PROFILE(_chmodNanosec);
auto node = _device->Load(path);
if(node == none) {
throw fuse::FuseErrnoException(ENOENT);
} else {
(*node)->chmod(mode);
}
}
void FilesystemImpl::chown(const boost::filesystem::path &path, uid_t uid, gid_t gid) {
PROFILE(_chownNanosec);
auto node = _device->Load(path);
if(node == none) {
throw fuse::FuseErrnoException(ENOENT);
} else {
(*node)->chown(uid, gid);
}
}
void FilesystemImpl::truncate(const bf::path &path, off_t size) {
PROFILE(_truncateNanosec);
LoadFile(path)->truncate(size);
}
void FilesystemImpl::ftruncate(int descriptor, off_t size) {
PROFILE(_ftruncateNanosec);
_open_files.get(descriptor)->truncate(size);
}
size_t FilesystemImpl::read(int descriptor, void *buf, size_t count, off_t offset) {
PROFILE(_readNanosec);
return _open_files.get(descriptor)->read(buf, count, offset);
}
void FilesystemImpl::write(int descriptor, const void *buf, size_t count, off_t offset) {
PROFILE(_writeNanosec);
_open_files.get(descriptor)->write(buf, count, offset);
}
void FilesystemImpl::fsync(int descriptor) {
PROFILE(_fsyncNanosec);
_open_files.get(descriptor)->fsync();
}
void FilesystemImpl::fdatasync(int descriptor) {
PROFILE(_fdatasyncNanosec);
_open_files.get(descriptor)->fdatasync();
}
void FilesystemImpl::access(const bf::path &path, int mask) {
PROFILE(_accessNanosec);
auto node = _device->Load(path);
if(node == none) {
throw fuse::FuseErrnoException(ENOENT);
} else {
(*node)->access(mask);
}
}
int FilesystemImpl::createAndOpenFile(const bf::path &path, mode_t mode, uid_t uid, gid_t gid) {
PROFILE(_createAndOpenFileNanosec);
auto dir = LoadDir(path.parent_path());
PROFILE(_createAndOpenFileNanosec_withoutLoading);
auto file = dir->createAndOpenFile(path.filename().native(), mode, uid, gid);
return _open_files.open(std::move(file));
}
void FilesystemImpl::mkdir(const bf::path &path, mode_t mode, uid_t uid, gid_t gid) {
PROFILE(_mkdirNanosec);
auto dir = LoadDir(path.parent_path());
PROFILE(_mkdirNanosec_withoutLoading);
dir->createDir(path.filename().native(), mode, uid, gid);
}
void FilesystemImpl::rmdir(const bf::path &path) {
PROFILE(_rmdirNanosec);
auto dir = LoadDir(path);
PROFILE(_rmdirNanosec_withoutLoading);
dir->remove();
}
void FilesystemImpl::unlink(const bf::path &path) {
PROFILE(_unlinkNanosec);
auto node = LoadFileOrSymlink(path);
PROFILE(_unlinkNanosec_withoutLoading);
node->remove();
}
void FilesystemImpl::rename(const bf::path &from, const bf::path &to) {
PROFILE(_renameNanosec);
auto node = _device->Load(from);
if(node == none) {
throw fuse::FuseErrnoException(ENOENT);
} else {
(*node)->rename(to);
}
}
unique_ref<vector<Dir::Entry>> FilesystemImpl::readDir(const bf::path &path) {
PROFILE(_readDirNanosec);
auto dir = LoadDir(path);
PROFILE(_readDirNanosec_withoutLoading);
return dir->children();
}
void FilesystemImpl::utimens(const bf::path &path, timespec lastAccessTime, timespec lastModificationTime) {
PROFILE(_utimensNanosec);
auto node = _device->Load(path);
if(node == none) {
throw fuse::FuseErrnoException(ENOENT);
} else {
(*node)->utimens(lastAccessTime, lastModificationTime);
}
}
void FilesystemImpl::statfs(const bf::path &path, struct statvfs *fsstat) {
PROFILE(_statfsNanosec);
_device->statfs(path, fsstat);
}
void FilesystemImpl::createSymlink(const bf::path &to, const bf::path &from, uid_t uid, gid_t gid) {
PROFILE(_createSymlinkNanosec);
auto parent = LoadDir(from.parent_path());
PROFILE(_createSymlinkNanosec_withoutLoading);
parent->createSymlink(from.filename().native(), to, uid, gid);
}
void FilesystemImpl::readSymlink(const bf::path &path, char *buf, size_t size) {
PROFILE(_readSymlinkNanosec);
string target = LoadSymlink(path)->target().native();
PROFILE(_readSymlinkNanosec_withoutLoading);
std::memcpy(buf, target.c_str(), std::min(target.size()+1, size));
buf[size-1] = '\0';
}

105
impl/FilesystemImpl.h Normal file
View File

@ -0,0 +1,105 @@
#pragma once
#ifndef MESSMER_FSPP_IMPL_FILESYSTEMIMPL_H_
#define MESSMER_FSPP_IMPL_FILESYSTEMIMPL_H_
#include "FuseOpenFileList.h"
#include "../fuse/Filesystem.h"
#include <messmer/cpp-utils/pointer/unique_ref.h>
#include <atomic>
//Remove this line if you don't want profiling
//#define FSPP_PROFILE 1
//TODO Test
namespace fspp {
class Node;
class File;
class Symlink;
class OpenFile;
class FilesystemImpl final: public fuse::Filesystem {
public:
explicit FilesystemImpl(Device *device);
virtual ~FilesystemImpl();
int openFile(const boost::filesystem::path &path, int flags) override;
void flush(int descriptor) override;
void closeFile(int descriptor) override;
void lstat(const boost::filesystem::path &path, struct ::stat *stbuf) override;
void fstat(int descriptor, struct ::stat *stbuf) override;
void chmod(const boost::filesystem::path &path, mode_t mode) override;
void chown(const boost::filesystem::path &path, uid_t uid, gid_t gid) override;
void truncate(const boost::filesystem::path &path, off_t size) override;
void ftruncate(int descriptor, off_t size) override;
size_t read(int descriptor, void *buf, size_t count, off_t offset) override;
void write(int descriptor, const void *buf, size_t count, off_t offset) override;
void fsync(int descriptor) override;
void fdatasync(int descriptor) override;
void access(const boost::filesystem::path &path, int mask) override;
int createAndOpenFile(const boost::filesystem::path &path, mode_t mode, uid_t uid, gid_t gid) override;
void mkdir(const boost::filesystem::path &path, mode_t mode, uid_t uid, gid_t gid) override;
void rmdir(const boost::filesystem::path &path) override;
void unlink(const boost::filesystem::path &path) override;
void rename(const boost::filesystem::path &from, const boost::filesystem::path &to) override;
cpputils::unique_ref<std::vector<Dir::Entry>> readDir(const boost::filesystem::path &path) override;
void utimens(const boost::filesystem::path &path, timespec lastAccessTime, timespec lastModificationTime) override;
void statfs(const boost::filesystem::path &path, struct statvfs *fsstat) override;
void createSymlink(const boost::filesystem::path &to, const boost::filesystem::path &from, uid_t uid, gid_t gid) override;
void readSymlink(const boost::filesystem::path &path, char *buf, size_t size) override;
private:
cpputils::unique_ref<File> LoadFile(const boost::filesystem::path &path);
cpputils::unique_ref<Dir> LoadDir(const boost::filesystem::path &path);
cpputils::unique_ref<Symlink> LoadSymlink(const boost::filesystem::path &path);
cpputils::unique_ref<Node> LoadFileOrSymlink(const boost::filesystem::path &path);
int openFile(const File &file, int flags);
#ifdef FSPP_PROFILE
std::atomic<uint64_t> _loadFileNanosec;
std::atomic<uint64_t> _loadDirNanosec;
std::atomic<uint64_t> _loadSymlinkNanosec;
std::atomic<uint64_t> _loadFileOrSymlinkNanosec;
std::atomic<uint64_t> _openFileNanosec;
std::atomic<uint64_t> _flushNanosec;
std::atomic<uint64_t> _closeFileNanosec;
std::atomic<uint64_t> _lstatNanosec;
std::atomic<uint64_t> _fstatNanosec;
std::atomic<uint64_t> _chmodNanosec;
std::atomic<uint64_t> _chownNanosec;
std::atomic<uint64_t> _truncateNanosec;
std::atomic<uint64_t> _ftruncateNanosec;
std::atomic<uint64_t> _readNanosec;
std::atomic<uint64_t> _writeNanosec;
std::atomic<uint64_t> _fsyncNanosec;
std::atomic<uint64_t> _fdatasyncNanosec;
std::atomic<uint64_t> _accessNanosec;
std::atomic<uint64_t> _createAndOpenFileNanosec;
std::atomic<uint64_t> _createAndOpenFileNanosec_withoutLoading;
std::atomic<uint64_t> _mkdirNanosec;
std::atomic<uint64_t> _mkdirNanosec_withoutLoading;
std::atomic<uint64_t> _rmdirNanosec;
std::atomic<uint64_t> _rmdirNanosec_withoutLoading;
std::atomic<uint64_t> _unlinkNanosec;
std::atomic<uint64_t> _unlinkNanosec_withoutLoading;
std::atomic<uint64_t> _renameNanosec;
std::atomic<uint64_t> _readDirNanosec;
std::atomic<uint64_t> _readDirNanosec_withoutLoading;
std::atomic<uint64_t> _utimensNanosec;
std::atomic<uint64_t> _statfsNanosec;
std::atomic<uint64_t> _createSymlinkNanosec;
std::atomic<uint64_t> _createSymlinkNanosec_withoutLoading;
std::atomic<uint64_t> _readSymlinkNanosec;
std::atomic<uint64_t> _readSymlinkNanosec_withoutLoading;
#endif
Device *_device;
FuseOpenFileList _open_files;
DISALLOW_COPY_AND_ASSIGN(FilesystemImpl);
};
}
#endif

49
impl/FuseOpenFileList.h Normal file
View File

@ -0,0 +1,49 @@
#pragma once
#ifndef MESSMER_FSPP_IMPL_FUSEOPENFILELIST_H_
#define MESSMER_FSPP_IMPL_FUSEOPENFILELIST_H_
#include "../fs_interface/File.h"
#include "../fs_interface/OpenFile.h"
#include "messmer/cpp-utils/macros.h"
#include "IdList.h"
namespace fspp {
class FuseOpenFileList final {
public:
FuseOpenFileList();
~FuseOpenFileList();
int open(cpputils::unique_ref<OpenFile> file);
OpenFile *get(int descriptor);
void close(int descriptor);
private:
IdList<OpenFile> _open_files;
DISALLOW_COPY_AND_ASSIGN(FuseOpenFileList);
};
inline FuseOpenFileList::FuseOpenFileList()
:_open_files() {
}
inline FuseOpenFileList::~FuseOpenFileList() {
}
inline int FuseOpenFileList::open(cpputils::unique_ref<OpenFile> file) {
return _open_files.add(std::move(file));
}
inline OpenFile *FuseOpenFileList::get(int descriptor) {
return _open_files.get(descriptor);
}
inline void FuseOpenFileList::close(int descriptor) {
//The destructor of the stored FuseOpenFile closes the file
_open_files.remove(descriptor);
}
}
#endif

72
impl/IdList.h Normal file
View File

@ -0,0 +1,72 @@
#pragma once
#ifndef MESSMER_FSPP_IMPL_IDLIST_H_
#define MESSMER_FSPP_IMPL_IDLIST_H_
#include <map>
#include <mutex>
#include <stdexcept>
#include <messmer/cpp-utils/pointer/unique_ref.h>
namespace fspp {
template<class Entry>
class IdList final {
public:
IdList();
virtual ~IdList();
int add(cpputils::unique_ref<Entry> entry);
Entry *get(int id);
const Entry *get(int id) const;
void remove(int id);
private:
std::map<int, cpputils::unique_ref<Entry>> _entries;
int _id_counter;
mutable std::mutex _mutex;
DISALLOW_COPY_AND_ASSIGN(IdList<Entry>);
};
template<class Entry>
IdList<Entry>::IdList()
: _entries(), _id_counter(0), _mutex() {
}
template<class Entry>
IdList<Entry>::~IdList() {
}
template<class Entry>
int IdList<Entry>::add(cpputils::unique_ref<Entry> entry) {
std::lock_guard<std::mutex> lock(_mutex);
//TODO Reuse IDs (ids = descriptors)
int new_id = ++_id_counter;
_entries.insert(std::make_pair(new_id, std::move(entry)));
return new_id;
}
template<class Entry>
Entry *IdList<Entry>::get(int id) {
return const_cast<Entry*>(const_cast<const IdList<Entry>*>(this)->get(id));
}
template<class Entry>
const Entry *IdList<Entry>::get(int id) const {
std::lock_guard<std::mutex> lock(_mutex);
const Entry *result = _entries.at(id).get();
return result;
}
template<class Entry>
void IdList<Entry>::remove(int id) {
std::lock_guard<std::mutex> lock(_mutex);
auto found_iter = _entries.find(id);
if (found_iter == _entries.end()) {
throw std::out_of_range("Called IdList::remove() with an invalid ID");
}
_entries.erase(found_iter);
}
}
#endif

1
impl/Profiler.cpp Normal file
View File

@ -0,0 +1 @@
#include "Profiler.h"

32
impl/Profiler.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#ifndef MESSMER_FSPP_IMPL_PROFILER_H
#define MESSMER_FSPP_IMPL_PROFILER_H
#include <atomic>
#include <chrono>
#include <messmer/cpp-utils/macros.h>
namespace fspp {
class Profiler final {
public:
Profiler(std::atomic_uint_fast64_t *targetForAddingNanosec);
~Profiler();
private:
std::atomic_uint_fast64_t *_targetForAddingNanosec;
std::chrono::high_resolution_clock::time_point _beginTime;
DISALLOW_COPY_AND_ASSIGN(Profiler);
};
inline Profiler::Profiler(std::atomic_uint_fast64_t *targetForAddingNanosec)
: _targetForAddingNanosec(targetForAddingNanosec), _beginTime(std::chrono::high_resolution_clock::now()) {
}
inline Profiler::~Profiler() {
uint64_t timeDiff = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - _beginTime).count();
*_targetForAddingNanosec += timeDiff;
}
}
#endif

View File

@ -0,0 +1,4 @@
/*
* Tests that the header can be included without needing additional header includes as dependencies.
*/
#include "../../fs_interface/Device.h"

View File

@ -0,0 +1,4 @@
/*
* Tests that the header can be included without needing additional header includes as dependencies.
*/
#include "../../fs_interface/Dir.h"

View File

@ -0,0 +1,4 @@
/*
* Tests that the header can be included without needing additional header includes as dependencies.
*/
#include "../../fs_interface/File.h"

View File

@ -0,0 +1,4 @@
/*
* Tests that the header can be included without needing additional header includes as dependencies.
*/
#include "../../fs_interface/Node.h"

View File

@ -0,0 +1,4 @@
/*
* Tests that the header can be included without needing additional header includes as dependencies.
*/
#include "../../fs_interface/OpenFile.h"

View File

@ -0,0 +1,14 @@
#include "../testutils/FuseTest.h"
using namespace fspp::fuse;
using namespace fspp::fuse;
using ::testing::_;
using ::testing::StrEq;
typedef FuseTest BasicFuseTest;
//This test case simply checks whether a filesystem can be setup and teardown without crashing.
TEST_F(BasicFuseTest, setupAndTearDown) {
auto fs = TestFS();
}

View File

@ -0,0 +1,4 @@
/*
* Tests that the header can be included without needing additional header includes as dependencies.
*/
#include "../../fuse/Filesystem.h"

View File

@ -0,0 +1,24 @@
#include "testutils/FuseAccessTest.h"
#include "../../../fuse/FuseErrnoException.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::Throw;
using ::testing::WithParamInterface;
using ::testing::Values;
using namespace fspp::fuse;
class FuseAccessErrorTest: public FuseAccessTest, public WithParamInterface<int> {
};
INSTANTIATE_TEST_CASE_P(FuseAccessErrorTest, FuseAccessErrorTest, Values(EACCES, ELOOP, ENAMETOOLONG, ENOENT, ENOTDIR, EROFS, EFAULT, EINVAL, EIO, ENOMEM, ETXTBSY));
TEST_P(FuseAccessErrorTest, ReturnedErrorIsCorrect) {
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, access(StrEq(FILENAME), _))
.Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
int error = AccessFileReturnError(FILENAME, 0);
EXPECT_EQ(GetParam(), error);
}

View File

@ -0,0 +1,35 @@
#include "testutils/FuseAccessTest.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::Return;
class FuseAccessFilenameTest: public FuseAccessTest {
};
TEST_F(FuseAccessFilenameTest, AccessFile) {
ReturnIsFileOnLstat("/myfile");
EXPECT_CALL(fsimpl, access(StrEq("/myfile"), _))
.Times(1).WillOnce(Return());
AccessFile("/myfile", 0);
}
TEST_F(FuseAccessFilenameTest, AccessFileNested) {
ReturnIsDirOnLstat("/mydir");
ReturnIsFileOnLstat("/mydir/myfile");
EXPECT_CALL(fsimpl, access(StrEq("/mydir/myfile"), _))
.Times(1).WillOnce(Return());
AccessFile("/mydir/myfile", 0);
}
TEST_F(FuseAccessFilenameTest, AccessFileNested2) {
ReturnIsDirOnLstat("/mydir");
ReturnIsDirOnLstat("/mydir/mydir2");
ReturnIsFileOnLstat("/mydir/mydir2/myfile");
EXPECT_CALL(fsimpl, access(StrEq("/mydir/mydir2/myfile"), _))
.Times(1).WillOnce(Return());
AccessFile("/mydir/mydir2/myfile", 0);
}

View File

@ -0,0 +1,20 @@
#include "testutils/FuseAccessTest.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::Return;
using ::testing::WithParamInterface;
using ::testing::Values;
class FuseAccessModeTest: public FuseAccessTest, public WithParamInterface<int> {
};
INSTANTIATE_TEST_CASE_P(FuseAccessModeTest, FuseAccessModeTest, Values(0, F_OK, R_OK, W_OK, X_OK, R_OK|W_OK, W_OK|X_OK, R_OK|X_OK, R_OK|W_OK|X_OK));
TEST_P(FuseAccessModeTest, AccessFile) {
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, access(StrEq(FILENAME), GetParam()))
.Times(1).WillOnce(Return());
AccessFile(FILENAME, GetParam());
}

View File

@ -0,0 +1,18 @@
#include "FuseAccessTest.h"
void FuseAccessTest::AccessFile(const char *filename, int mode) {
int error = AccessFileReturnError(filename, mode);
EXPECT_EQ(0, error);
}
int FuseAccessTest::AccessFileReturnError(const char *filename, int mode) {
auto fs = TestFS();
auto realpath = fs->mountDir() / filename;
int retval = ::access(realpath.c_str(), mode);
if (retval == 0) {
return 0;
} else {
return errno;
}
}

View File

@ -0,0 +1,15 @@
#pragma once
#ifndef MESSMER_FSPP_TEST_FUSE_ACCESS_TESTUTILS_FUSEACCESSTEST_H_
#define MESSMER_FSPP_TEST_FUSE_ACCESS_TESTUTILS_FUSEACCESSTEST_H_
#include "../../../testutils/FuseTest.h"
class FuseAccessTest: public FuseTest {
public:
const char *FILENAME = "/myfile";
void AccessFile(const char *filename, int mode);
int AccessFileReturnError(const char *filename, int mode);
};
#endif

View File

@ -0,0 +1,99 @@
#include "../../testutils/FuseTest.h"
#include <condition_variable>
using ::testing::_;
using ::testing::StrEq;
using ::testing::Eq;
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Return;
using ::testing::Invoke;
using ::testing::InSequence;
using ::testing::AtLeast;
using std::string;
using std::mutex;
using std::unique_lock;
using std::condition_variable;
using std::chrono::duration;
using std::chrono::seconds;
// The fuse behaviour is: For each open(), there will be exactly one call to release().
// Directly before this call to release(), flush() will be called. After flush() returns,
// the ::close() syscall (in the process using the filesystem) returns. So the fuse release() call is
// called asynchronously afterwards. Errors have to be returned in the implementation of flush().
// Citing FUSE spec:
// 1) Flush is called on each close() of a file descriptor.
// 2) Filesystems shouldn't assume that flush will always be called after some writes, or that if will be called at all.
// I can't get these sentences together. For the test cases here, I go with the first one and assume that
// flush() will ALWAYS be called on a file close.
class Barrier {
public:
Barrier(): m(), cv(), finished(false) {}
template<class A, class B>
void WaitAtMost(const duration<A, B> &atMost) {
unique_lock<mutex> lock(m);
if (!finished) {
cv.wait_for(lock, atMost, [this] () {return finished;});
}
}
void Release() {
unique_lock<mutex> lock(m);
finished = true;
cv.notify_all();
}
private:
mutex m;
condition_variable cv;
bool finished;
};
class FuseCloseTest: public FuseTest, public WithParamInterface<int> {
public:
const string FILENAME = "/myfile";
void OpenAndCloseFile(const string &filename) {
auto fs = TestFS();
int fd = OpenFile(fs.get(), filename);
CloseFile(fd);
}
int OpenFile(const TempTestFS *fs, const string &filename) {
auto real_path = fs->mountDir() / filename;
int fd = ::open(real_path.c_str(), O_RDONLY);
EXPECT_GE(fd, 0) << "Opening file failed";
return fd;
}
void CloseFile(int fd) {
int retval = ::close(fd);
EXPECT_EQ(0, retval);
}
};
INSTANTIATE_TEST_CASE_P(FuseCloseTest, FuseCloseTest, Values(0, 1, 2, 100, 1024*1024*1024));
//TODO Figure out what's wrong and enable this test
//Disabled, because it is flaky. libfuse seems to not send the release() event sometimes.
/*TEST_P(FuseCloseTest, CloseFile) {
Barrier barrier;
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _)).WillOnce(Return(GetParam()));
{
//InSequence fileCloseSequence;
EXPECT_CALL(fsimpl, flush(Eq(GetParam()))).Times(1);
EXPECT_CALL(fsimpl, closeFile(Eq(GetParam()))).Times(1).WillOnce(Invoke([&barrier] (int) {
// Release the waiting lock at the end of this test case, because the fuse release() came in now.
barrier.Release();
}));
}
OpenAndCloseFile(FILENAME);
// Wait, until fuse release() was called, so we can check for the function call expectation.
barrier.WaitAtMost(seconds(10));
}*/

View File

@ -0,0 +1,34 @@
#include "testutils/FuseCreateAndOpenTest.h"
#include "../../../fuse/FuseErrnoException.h"
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Return;
using ::testing::Throw;
using ::testing::StrEq;
using ::testing::_;
using namespace fspp::fuse;
class FuseCreateAndOpenErrorTest: public FuseCreateAndOpenTest, public WithParamInterface<int> {
};
INSTANTIATE_TEST_CASE_P(FuseCreateAndOpenErrorTest, FuseCreateAndOpenErrorTest, Values(EACCES, EDQUOT, EEXIST, EFAULT, EFBIG, EINTR, EOVERFLOW, EINVAL, EISDIR, ELOOP, EMFILE, ENAMETOOLONG, ENFILE, ENODEV, ENOENT, ENOMEM, ENOSPC, ENOTDIR, ENXIO, EOPNOTSUPP, EPERM, EROFS, ETXTBSY, EWOULDBLOCK, EBADF, ENOTDIR));
TEST_F(FuseCreateAndOpenErrorTest, ReturnNoError) {
ReturnDoesntExistOnLstat(FILENAME);
EXPECT_CALL(fsimpl, createAndOpenFile(StrEq(FILENAME), _, _, _)).Times(1).WillOnce(Return(1));
//For the syscall to succeed, we also need to give an fstat implementation.
ReturnIsFileOnFstat(1);
int error = CreateAndOpenFileReturnError(FILENAME, O_RDONLY);
EXPECT_EQ(0, error);
}
TEST_P(FuseCreateAndOpenErrorTest, ReturnError) {
ReturnDoesntExistOnLstat(FILENAME);
EXPECT_CALL(fsimpl, createAndOpenFile(StrEq(FILENAME), _, _, _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
int error = CreateAndOpenFileReturnError(FILENAME, O_RDONLY);
EXPECT_EQ(GetParam(), error);
}

View File

@ -0,0 +1,42 @@
#include "testutils/FuseCreateAndOpenTest.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Return;
class FuseCreateAndOpenFileDescriptorTest: public FuseCreateAndOpenTest, public WithParamInterface<int> {
public:
void CreateAndOpenAndReadFile(const char *filename) {
auto fs = TestFS();
int fd = CreateAndOpenFile(fs.get(), filename);
ReadFile(fd);
}
private:
int CreateAndOpenFile(const TempTestFS *fs, const char *filename) {
auto realpath = fs->mountDir() / filename;
int fd = ::open(realpath.c_str(), O_RDONLY | O_CREAT, S_IRUSR | S_IRGRP | S_IROTH);
EXPECT_GE(fd, 0) << "Creating file failed";
return fd;
}
void ReadFile(int fd) {
uint8_t buf;
int retval = ::read(fd, &buf, 1);
EXPECT_EQ(1, retval) << "Reading file failed";
}
};
INSTANTIATE_TEST_CASE_P(FuseCreateAndOpenFileDescriptorTest, FuseCreateAndOpenFileDescriptorTest, Values(0, 2, 5, 1000, 1024*1024*1024));
TEST_P(FuseCreateAndOpenFileDescriptorTest, TestReturnedFileDescriptor) {
ReturnDoesntExistOnLstat(FILENAME);
EXPECT_CALL(fsimpl, createAndOpenFile(StrEq(FILENAME), _, _, _))
.Times(1).WillOnce(Return(GetParam()));
EXPECT_CALL(fsimpl, read(GetParam(), _, _, _)).Times(1).WillOnce(Return(1));
//For the syscall to succeed, we also need to give an fstat implementation.
ReturnIsFileOnFstatWithSize(GetParam(), 1);
CreateAndOpenAndReadFile(FILENAME);
}

View File

@ -0,0 +1,42 @@
#include "testutils/FuseCreateAndOpenTest.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::Return;
class FuseCreateAndOpenFilenameTest: public FuseCreateAndOpenTest {
public:
};
TEST_F(FuseCreateAndOpenFilenameTest, CreateAndOpenFile) {
ReturnDoesntExistOnLstat("/myfile");
EXPECT_CALL(fsimpl, createAndOpenFile(StrEq("/myfile"), _, _, _))
.Times(1).WillOnce(Return(0));
//For the syscall to succeed, we also need to give an fstat implementation.
ReturnIsFileOnFstat(0);
CreateAndOpenFile("/myfile", O_RDONLY);
}
TEST_F(FuseCreateAndOpenFilenameTest, CreateAndOpenFileNested) {
ReturnIsDirOnLstat("/mydir");
ReturnDoesntExistOnLstat("/mydir/myfile");
EXPECT_CALL(fsimpl, createAndOpenFile(StrEq("/mydir/myfile"), _, _, _))
.Times(1).WillOnce(Return(0));
//For the syscall to succeed, we also need to give an fstat implementation.
ReturnIsFileOnFstat(0);
CreateAndOpenFile("/mydir/myfile", O_RDONLY);
}
TEST_F(FuseCreateAndOpenFilenameTest, CreateAndOpenFileNested2) {
ReturnIsDirOnLstat("/mydir");
ReturnIsDirOnLstat("/mydir/mydir2");
ReturnDoesntExistOnLstat("/mydir/mydir2/myfile");
EXPECT_CALL(fsimpl, createAndOpenFile(StrEq("/mydir/mydir2/myfile"), _, _, _))
.Times(1).WillOnce(Return(0));
//For the syscall to succeed, we also need to give an fstat implementation.
ReturnIsFileOnFstat(0);
CreateAndOpenFile("/mydir/mydir2/myfile", O_RDONLY);
}

View File

@ -0,0 +1,22 @@
#include "testutils/FuseCreateAndOpenTest.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Return;
class FuseCreateAndOpenFlagsTest: public FuseCreateAndOpenTest, public WithParamInterface<mode_t> {
};
INSTANTIATE_TEST_CASE_P(FuseCreateAndOpenFlagsTest, FuseCreateAndOpenFlagsTest, Values(O_RDWR, O_RDONLY, O_WRONLY));
//TODO Disabled because it doesn't seem to work. Fuse doesn't seem to pass flags to create(). Why?
/*TEST_P(FuseCreateAndOpenFlagsTest, testFlags) {
ReturnDoesntExistOnLstat(FILENAME);
EXPECT_CALL(fsimpl, createAndOpenFile(StrEq(FILENAME), OpenFlagsEq(GetParam()), _, _))
.Times(1).WillOnce(Return(0));
//For the syscall to succeed, we also need to give an fstat implementation.
ReturnIsFileOnFstat(0);
CreateAndOpenFile(FILENAME, GetParam());
}*/

View File

@ -0,0 +1,30 @@
#include "FuseCreateAndOpenTest.h"
using ::testing::_;
int FuseCreateAndOpenTest::CreateAndOpenFile(const char *filename, int flags) {
int fd = CreateAndOpenFileAllowError(filename, flags);
EXPECT_GE(fd, 0);
return fd;
}
int FuseCreateAndOpenTest::CreateAndOpenFileReturnError(const char *filename, int flags) {
int fd = CreateAndOpenFileAllowError(filename, flags);
if (fd >= 0) {
return 0;
} else {
return -fd;
}
}
int FuseCreateAndOpenTest::CreateAndOpenFileAllowError(const char *filename, int flags) {
auto fs = TestFS();
auto realpath = fs->mountDir() / filename;
int fd = ::open(realpath.c_str(), flags | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd >= 0) {
return fd;
} else {
return -errno;
}
}

View File

@ -0,0 +1,21 @@
#pragma once
#ifndef MESSMER_FSPP_TEST_FUSE_CREATEANDOPENFILE_TESTUTILS_FUSECREATEANDOPENTEST_H_
#define MESSMER_FSPP_TEST_FUSE_CREATEANDOPENFILE_TESTUTILS_FUSECREATEANDOPENTEST_H_
#include "../../../testutils/FuseTest.h"
class FuseCreateAndOpenTest: public FuseTest {
public:
const char *FILENAME = "/myfile";
int CreateAndOpenFile(const char *FILENAME, int flags);
int CreateAndOpenFileReturnError(const char *FILENAME, int flags);
private:
int CreateAndOpenFileAllowError(const char *FILENAME, int flags);
};
MATCHER_P(OpenFlagsEq, expectedFlags, "") {
return expectedFlags == (O_ACCMODE & arg);
}
#endif

View File

@ -0,0 +1,25 @@
#include "testutils/FuseFdatasyncTest.h"
#include "../../../fuse/FuseErrnoException.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::Throw;
using ::testing::WithParamInterface;
using ::testing::Values;
using namespace fspp::fuse;
class FuseFdatasyncErrorTest: public FuseFdatasyncTest, public WithParamInterface<int> {
};
INSTANTIATE_TEST_CASE_P(FuseFdatasyncErrorTest, FuseFdatasyncErrorTest, Values(EBADF, EIO, EROFS, EINVAL));
TEST_P(FuseFdatasyncErrorTest, ReturnedErrorIsCorrect) {
ReturnIsFileOnLstat(FILENAME);
OnOpenReturnFileDescriptor(FILENAME, 0);
EXPECT_CALL(fsimpl, fdatasync(0))
.Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
int error = FdatasyncFileReturnError(FILENAME);
EXPECT_EQ(GetParam(), error);
}

View File

@ -0,0 +1,26 @@
#include "testutils/FuseFdatasyncTest.h"
#include "../../../fuse/FuseErrnoException.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Eq;
using ::testing::Return;
using namespace fspp::fuse;
class FuseFdatasyncFileDescriptorTest: public FuseFdatasyncTest, public WithParamInterface<int> {
};
INSTANTIATE_TEST_CASE_P(FuseFdatasyncFileDescriptorTest, FuseFdatasyncFileDescriptorTest, Values(0,1,10,1000,1024*1024*1024));
TEST_P(FuseFdatasyncFileDescriptorTest, FileDescriptorIsCorrect) {
ReturnIsFileOnLstat(FILENAME);
OnOpenReturnFileDescriptor(FILENAME, GetParam());
EXPECT_CALL(fsimpl, fdatasync(Eq(GetParam())))
.Times(1).WillOnce(Return());
FdatasyncFile(FILENAME);
}

View File

@ -0,0 +1,31 @@
#include "FuseFdatasyncTest.h"
#include <fcntl.h>
void FuseFdatasyncTest::FdatasyncFile(const char *filename) {
int error = FdatasyncFileReturnError(filename);
EXPECT_EQ(0, error);
}
int FuseFdatasyncTest::FdatasyncFileReturnError(const char *filename) {
auto fs = TestFS();
int fd = OpenFile(fs.get(), filename);
#ifdef F_FULLFSYNC
// This is MacOSX, which doesn't know fdatasync
int retval = fcntl(fd, F_FULLFSYNC);
#else
int retval = ::fdatasync(fd);
#endif
if (retval != -1) {
return 0;
} else {
return errno;
}
}
int FuseFdatasyncTest::OpenFile(const TempTestFS *fs, const char *filename) {
auto realpath = fs->mountDir() / filename;
int fd = ::open(realpath.c_str(), O_RDWR);
EXPECT_GE(fd, 0) << "Error opening file";
return fd;
}

View File

@ -0,0 +1,18 @@
#pragma once
#ifndef MESSMER_FSPP_TEST_FUSE_FDATASYNC_TESTUTILS_FUSEFDATASYNCTEST_H_
#define MESSMER_FSPP_TEST_FUSE_FDATASYNC_TESTUTILS_FUSEFDATASYNCTEST_H_
#include "../../../testutils/FuseTest.h"
class FuseFdatasyncTest: public FuseTest {
public:
const char *FILENAME = "/myfile";
void FdatasyncFile(const char *filename);
int FdatasyncFileReturnError(const char *filename);
private:
int OpenFile(const TempTestFS *fs, const char *filename);
};
#endif

View File

@ -0,0 +1,32 @@
#include "testutils/FuseFlushTest.h"
#include "../../../fuse/FuseErrnoException.h"
using ::testing::WithParamInterface;
using ::testing::StrEq;
using ::testing::Eq;
using ::testing::Return;
using ::testing::Throw;
using ::testing::AtLeast;
using ::testing::Values;
using ::testing::_;
using fspp::fuse::FuseErrnoException;
class FuseFlushErrorTest: public FuseFlushTest, public WithParamInterface<int> {
};
INSTANTIATE_TEST_CASE_P(FuseFlushErrorTest, FuseFlushErrorTest, Values(EBADF, EINTR, EIO));
TEST_P(FuseFlushErrorTest, ReturnErrorFromFlush) {
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _)).WillOnce(Return(GetParam()));
EXPECT_CALL(fsimpl, flush(Eq(GetParam()))).Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
auto fs = TestFS();
int fd = OpenFile(fs.get(), FILENAME);
int close_result = ::close(fd);
EXPECT_EQ(GetParam(), errno);
EXPECT_EQ(-1, close_result);
}

View File

@ -0,0 +1,35 @@
#include "testutils/FuseFlushTest.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::Eq;
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Return;
using ::testing::AtLeast;
using std::string;
// The fuse behaviour is: For each open(), there will be exactly one call to release().
// Directly before this call to release(), flush() will be called. After flush() returns,
// the ::close() syscall (in the process using the filesystem) returns. So the fuse release() call is
// called asynchronously afterwards. Errors have to be returned in the implementation of flush().
// Citing FUSE spec:
// 1) Flush is called on each close() of a file descriptor.
// 2) Filesystems shouldn't assume that flush will always be called after some writes, or that if will be called at all.
// I can't get these sentences together. For the test cases here, I go with the first one and assume that
// flush() will ALWAYS be called on a file close.
class FuseFlushFileDescriptorTest: public FuseFlushTest, public WithParamInterface<int> {
};
INSTANTIATE_TEST_CASE_P(FuseFlushFileDescriptorTest, FuseFlushFileDescriptorTest, Values(0, 1, 2, 100, 1024*1024*1024));
TEST_P(FuseFlushFileDescriptorTest, FlushOnCloseFile) {
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _)).WillOnce(Return(GetParam()));
EXPECT_CALL(fsimpl, flush(Eq(GetParam()))).Times(1);
OpenAndCloseFile(FILENAME);
}

View File

@ -0,0 +1,19 @@
#include "FuseFlushTest.h"
void FuseFlushTest::OpenAndCloseFile(const std::string &filename) {
auto fs = TestFS();
int fd = OpenFile(fs.get(), filename);
CloseFile(fd);
}
int FuseFlushTest::OpenFile(const TempTestFS *fs, const std::string &filename) {
auto real_path = fs->mountDir() / filename;
int fd = ::open(real_path.c_str(), O_RDONLY);
EXPECT_GE(fd, 0) << "Opening file failed";
return fd;
}
void FuseFlushTest::CloseFile(int fd) {
int retval = ::close(fd);
EXPECT_EQ(0, retval);
}

View File

@ -0,0 +1,17 @@
#pragma once
#ifndef MESSMER_FSPP_TEST_FUSE_FLUSH_TESTUTILS_FUSEFLUSHTEST_H_
#define MESSMER_FSPP_TEST_FUSE_FLUSH_TESTUTILS_FUSEFLUSHTEST_H_
#include "../../../testutils/FuseTest.h"
class FuseFlushTest: public FuseTest {
public:
const std::string FILENAME = "/myfile";
void OpenAndCloseFile(const std::string &filename);
int OpenFile(const TempTestFS *fs, const std::string &filename);
void CloseFile(int fd);
};
#endif

View File

@ -0,0 +1,39 @@
#include "testutils/FuseFstatTest.h"
#include "../../../fuse/FuseErrnoException.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Eq;
using ::testing::Return;
using ::testing::Throw;
using namespace fspp::fuse;
// Cite from FUSE documentation on the fgetattr function:
// "Currently this is only called after the create() method if that is implemented (see above).
// Later it may be called for invocations of fstat() too."
// So we need to issue a create to get our fstat called.
class FuseFstatErrorTest: public FuseFstatTest, public WithParamInterface<int> {
public:
int CreateFileAllowErrors(const TempTestFS *fs, const std::string &filename) {
auto real_path = fs->mountDir() / filename;
return ::open(real_path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
}
};
INSTANTIATE_TEST_CASE_P(FuseFstatErrorTest, FuseFstatErrorTest, Values(EACCES, EBADF, EFAULT, ELOOP, ENAMETOOLONG, ENOENT, ENOMEM, ENOTDIR, EOVERFLOW));
TEST_P(FuseFstatErrorTest, ReturnedErrorCodeIsCorrect) {
ReturnDoesntExistOnLstat(FILENAME);
OnCreateAndOpenReturnFileDescriptor(FILENAME, 0);
EXPECT_CALL(fsimpl, fstat(Eq(0), _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
auto fs = TestFS();
int error = CreateFileReturnError(fs.get(), FILENAME);
EXPECT_EQ(GetParam(), error);
}

View File

@ -0,0 +1,37 @@
#include "testutils/FuseFstatTest.h"
#include "../../../fuse/FuseErrnoException.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Eq;
using ::testing::Return;
using ::testing::Throw;
using namespace fspp::fuse;
// Cite from FUSE documentation on the fgetattr function:
// "Currently this is only called after the create() method if that is implemented (see above).
// Later it may be called for invocations of fstat() too."
// So we need to issue a create to get our fstat called.
class FuseFstatParameterTest: public FuseFstatTest, public WithParamInterface<int> {
public:
void CallFstat(const char *filename) {
auto fs = TestFS();
CreateFile(fs.get(), filename);
}
};
INSTANTIATE_TEST_CASE_P(FuseFstatParameterTest, FuseFstatParameterTest, Values(0,1,10,1000,1024*1024*1024));
TEST_P(FuseFstatParameterTest, FileDescriptorIsCorrect) {
ReturnDoesntExistOnLstat(FILENAME);
OnCreateAndOpenReturnFileDescriptor(FILENAME, GetParam());
EXPECT_CALL(fsimpl, fstat(Eq(GetParam()), _)).Times(1).WillOnce(ReturnIsFileFstat);
CallFstat(FILENAME);
}

6
test/fuse/fstat/README Normal file
View File

@ -0,0 +1,6 @@
Cite from FUSE documentation on the fgetattr function:
Currently this is only called after the create() method if that is implemented (see above).
Later it may be called for invocations of fstat() too.
So we need to issue a create to get our fstat called.
Since fstat is currently only called after create, we can't call it directly.
So we can't test the returned values.

View File

@ -0,0 +1,34 @@
#include "FuseFstatTest.h"
using ::testing::StrEq;
using ::testing::_;
using ::testing::Return;
int FuseFstatTest::CreateFile(const TempTestFS *fs, const std::string &filename) {
int fd = CreateFileAllowErrors(fs, filename);
EXPECT_GE(fd, 0) << "Opening file failed";
return fd;
}
int FuseFstatTest::CreateFileReturnError(const TempTestFS *fs, const std::string &filename) {
int fd = CreateFileAllowErrors(fs, filename);
if (fd >= 0) {
return 0;
} else {
return -fd;
}
}
int FuseFstatTest::CreateFileAllowErrors(const TempTestFS *fs, const std::string &filename) {
auto real_path = fs->mountDir() / filename;
int fd = ::open(real_path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd >= 0) {
return fd;
} else {
return -errno;
}
}
void FuseFstatTest::OnCreateAndOpenReturnFileDescriptor(const char *filename, int descriptor) {
EXPECT_CALL(fsimpl, createAndOpenFile(StrEq(filename), _, _, _)).Times(1).WillOnce(Return(descriptor));
}

View File

@ -0,0 +1,17 @@
#pragma once
#ifndef MESSMER_FSPP_TEST_FUSE_FSTAT_TESTUTILS_FUSEFSTATTEST_H_
#define MESSMER_FSPP_TEST_FUSE_FSTAT_TESTUTILS_FUSEFSTATTEST_H_
#include "../../../testutils/FuseTest.h"
class FuseFstatTest: public FuseTest {
public:
int CreateFile(const TempTestFS *fs, const std::string &filename);
int CreateFileReturnError(const TempTestFS *fs, const std::string &filename);
void OnCreateAndOpenReturnFileDescriptor(const char *filename, int descriptor);
private:
int CreateFileAllowErrors(const TempTestFS *fs, const std::string &filename);
};
#endif

View File

@ -0,0 +1,25 @@
#include "testutils/FuseFsyncTest.h"
#include "../../../fuse/FuseErrnoException.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::Throw;
using ::testing::WithParamInterface;
using ::testing::Values;
using namespace fspp::fuse;
class FuseFsyncErrorTest: public FuseFsyncTest, public WithParamInterface<int> {
};
INSTANTIATE_TEST_CASE_P(FuseFsyncErrorTest, FuseFsyncErrorTest, Values(EBADF, EIO, EROFS, EINVAL));
TEST_P(FuseFsyncErrorTest, ReturnedErrorIsCorrect) {
ReturnIsFileOnLstat(FILENAME);
OnOpenReturnFileDescriptor(FILENAME, 0);
EXPECT_CALL(fsimpl, fsync(0))
.Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
int error = FsyncFileReturnError(FILENAME);
EXPECT_EQ(GetParam(), error);
}

View File

@ -0,0 +1,26 @@
#include "testutils/FuseFsyncTest.h"
#include "../../../fuse/FuseErrnoException.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Eq;
using ::testing::Return;
using namespace fspp::fuse;
class FuseFsyncFileDescriptorTest: public FuseFsyncTest, public WithParamInterface<int> {
};
INSTANTIATE_TEST_CASE_P(FuseFsyncFileDescriptorTest, FuseFsyncFileDescriptorTest, Values(0,1,10,1000,1024*1024*1024));
TEST_P(FuseFsyncFileDescriptorTest, FileDescriptorIsCorrect) {
ReturnIsFileOnLstat(FILENAME);
OnOpenReturnFileDescriptor(FILENAME, GetParam());
EXPECT_CALL(fsimpl, fsync(Eq(GetParam())))
.Times(1).WillOnce(Return());
FsyncFile(FILENAME);
}

View File

@ -0,0 +1,25 @@
#include "FuseFsyncTest.h"
void FuseFsyncTest::FsyncFile(const char *filename) {
int error = FsyncFileReturnError(filename);
EXPECT_EQ(0, error);
}
int FuseFsyncTest::FsyncFileReturnError(const char *filename) {
auto fs = TestFS();
int fd = OpenFile(fs.get(), filename);
int retval = ::fsync(fd);
if (retval == 0) {
return 0;
} else {
return errno;
}
}
int FuseFsyncTest::OpenFile(const TempTestFS *fs, const char *filename) {
auto realpath = fs->mountDir() / filename;
int fd = ::open(realpath.c_str(), O_RDWR);
EXPECT_GE(fd, 0) << "Error opening file";
return fd;
}

View File

@ -0,0 +1,18 @@
#pragma once
#ifndef MESSMER_FSPP_TEST_FUSE_FSYNC_TESTUTILS_FUSEFSYNCTEST_H_
#define MESSMER_FSPP_TEST_FUSE_FSYNC_TESTUTILS_FUSEFSYNCTEST_H_
#include "../../../testutils/FuseTest.h"
class FuseFsyncTest: public FuseTest {
public:
const char *FILENAME = "/myfile";
void FsyncFile(const char *filename);
int FsyncFileReturnError(const char *filename);
private:
int OpenFile(const TempTestFS *fs, const char *filename);
};
#endif

View File

@ -0,0 +1,27 @@
#include "testutils/FuseFTruncateTest.h"
#include "../../../fuse/FuseErrnoException.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::Throw;
using ::testing::WithParamInterface;
using ::testing::Values;
using namespace fspp::fuse;
class FuseFTruncateErrorTest: public FuseFTruncateTest, public WithParamInterface<int> {
};
INSTANTIATE_TEST_CASE_P(FuseFTruncateErrorTest, FuseFTruncateErrorTest, Values(EACCES, EFAULT, EFBIG, EINTR, EINVAL, EIO, EISDIR, ELOOP, ENAMETOOLONG, ENOENT, ENOTDIR, EPERM, EROFS, ETXTBSY, EBADF));
TEST_P(FuseFTruncateErrorTest, ReturnedErrorIsCorrect) {
ReturnIsFileOnLstat(FILENAME);
OnOpenReturnFileDescriptor(FILENAME, 0);
EXPECT_CALL(fsimpl, ftruncate(0, _))
.Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
//Needed to make ::ftruncate system call return successfully
ReturnIsFileOnFstat(0);
int error = FTruncateFileReturnError(FILENAME, 0);
EXPECT_EQ(GetParam(), error);
}

View File

@ -0,0 +1,29 @@
#include "testutils/FuseFTruncateTest.h"
#include "../../../fuse/FuseErrnoException.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Eq;
using ::testing::Return;
using ::testing::Throw;
using namespace fspp::fuse;
class FuseFTruncateFileDescriptorTest: public FuseFTruncateTest, public WithParamInterface<int> {
};
INSTANTIATE_TEST_CASE_P(FuseFTruncateFileDescriptorTest, FuseFTruncateFileDescriptorTest, Values(0,1,10,1000,1024*1024*1024));
TEST_P(FuseFTruncateFileDescriptorTest, FileDescriptorIsCorrect) {
ReturnIsFileOnLstat(FILENAME);
OnOpenReturnFileDescriptor(FILENAME, GetParam());
EXPECT_CALL(fsimpl, ftruncate(Eq(GetParam()), _))
.Times(1).WillOnce(Return());
//Needed to make ::ftruncate system call return successfully
ReturnIsFileOnFstat(GetParam());
FTruncateFile(FILENAME, 0);
}

View File

@ -0,0 +1,24 @@
#include "testutils/FuseFTruncateTest.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::Eq;
using ::testing::Return;
using ::testing::WithParamInterface;
using ::testing::Values;
class FuseFTruncateSizeTest: public FuseFTruncateTest, public WithParamInterface<off_t> {
};
INSTANTIATE_TEST_CASE_P(FuseFTruncateSizeTest, FuseFTruncateSizeTest, Values(0, 1, 10, 1024, 1024*1024*1024));
TEST_P(FuseFTruncateSizeTest, FTruncateFile) {
ReturnIsFileOnLstat(FILENAME);
OnOpenReturnFileDescriptor(FILENAME, 0);
EXPECT_CALL(fsimpl, ftruncate(Eq(0), GetParam()))
.Times(1).WillOnce(Return());
//Needed to make ::ftruncate system call return successfully
ReturnIsFileOnFstat(0);
FTruncateFile(FILENAME, GetParam());
}

View File

@ -0,0 +1,25 @@
#include "FuseFTruncateTest.h"
void FuseFTruncateTest::FTruncateFile(const char *filename, off_t size) {
int error = FTruncateFileReturnError(filename, size);
EXPECT_EQ(0, error);
}
int FuseFTruncateTest::FTruncateFileReturnError(const char *filename, off_t size) {
auto fs = TestFS();
int fd = OpenFile(fs.get(), filename);
int retval = ::ftruncate(fd, size);
if (0 == retval) {
return 0;
} else {
return errno;
}
}
int FuseFTruncateTest::OpenFile(const TempTestFS *fs, const char *filename) {
auto realpath = fs->mountDir() / filename;
int fd = ::open(realpath.c_str(), O_RDWR);
EXPECT_GE(fd, 0) << "Error opening file";
return fd;
}

View File

@ -0,0 +1,18 @@
#pragma once
#ifndef MESSMER_FSPP_TEST_FUSE_FTRUNCATE_TESTUTILS_FUSEFTRUNCATETEST_H_
#define MESSMER_FSPP_TEST_FUSE_FTRUNCATE_TESTUTILS_FUSEFTRUNCATETEST_H_
#include "../../../testutils/FuseTest.h"
class FuseFTruncateTest: public FuseTest {
public:
const char *FILENAME = "/myfile";
void FTruncateFile(const char *filename, off_t size);
int FTruncateFileReturnError(const char *filename, off_t size);
private:
int OpenFile(const TempTestFS *fs, const char *filename);
};
#endif

View File

@ -0,0 +1,29 @@
#include "testutils/FuseLstatTest.h"
#include "../../../fuse/FuseErrnoException.h"
using ::testing::StrEq;
using ::testing::_;
using ::testing::Throw;
using ::testing::WithParamInterface;
using ::testing::Values;
using fspp::fuse::FuseErrnoException;
class FuseLstatErrorTest: public FuseLstatTest, public WithParamInterface<int> {
public:
};
INSTANTIATE_TEST_CASE_P(LstatErrorCodes, FuseLstatErrorTest, Values(EACCES, EBADF, EFAULT, ELOOP, ENAMETOOLONG, ENOENT, ENOMEM, ENOTDIR, EOVERFLOW, EINVAL, ENOTDIR));
TEST_F(FuseLstatErrorTest, ReturnNoError) {
EXPECT_CALL(fsimpl, lstat(StrEq(FILENAME), _)).Times(1).WillOnce(ReturnIsFile);
errno = 0;
int error = LstatPathReturnError(FILENAME);
EXPECT_EQ(0, error);
}
TEST_P(FuseLstatErrorTest, ReturnError) {
EXPECT_CALL(fsimpl, lstat(StrEq(FILENAME), _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
int error = LstatPathReturnError(FILENAME);
EXPECT_EQ(GetParam(), error);
}

View File

@ -0,0 +1,36 @@
#include "testutils/FuseLstatTest.h"
using ::testing::_;
using ::testing::StrEq;
class FuseLstatPathParameterTest: public FuseLstatTest {
};
TEST_F(FuseLstatPathParameterTest, PathParameterIsCorrectRoot) {
EXPECT_CALL(fsimpl, lstat(StrEq("/"), _)).Times(1).WillOnce(ReturnIsDir);
LstatPath("/");
}
TEST_F(FuseLstatPathParameterTest, PathParameterIsCorrectSimpleFile) {
EXPECT_CALL(fsimpl, lstat(StrEq("/myfile"), _)).Times(1).WillOnce(ReturnIsFile);
LstatPath("/myfile");
}
TEST_F(FuseLstatPathParameterTest, PathParameterIsCorrectSimpleDir) {
EXPECT_CALL(fsimpl, lstat(StrEq("/mydir"), _)).Times(1).WillOnce(ReturnIsDir);
LstatPath("/mydir/");
}
TEST_F(FuseLstatPathParameterTest, PathParameterIsCorrectNestedFile) {
ReturnIsDirOnLstat("/mydir");
ReturnIsDirOnLstat("/mydir/mydir2");
EXPECT_CALL(fsimpl, lstat(StrEq("/mydir/mydir2/myfile"), _)).Times(1).WillOnce(ReturnIsFile);
LstatPath("/mydir/mydir2/myfile");
}
TEST_F(FuseLstatPathParameterTest, PathParameterIsCorrectNestedDir) {
ReturnIsDirOnLstat("/mydir");
ReturnIsDirOnLstat("/mydir/mydir2");
EXPECT_CALL(fsimpl, lstat(StrEq("/mydir/mydir2/mydir3"), _)).Times(1).WillOnce(ReturnIsDir);
LstatPath("/mydir/mydir2/mydir3/");
}

View File

@ -0,0 +1,27 @@
#include "testutils/FuseLstatReturnTest.h"
using ::testing::WithParamInterface;
using ::testing::Values;
class FuseLstatReturnATimeTest: public FuseLstatReturnTest<time_t>, public WithParamInterface<time_t> {
private:
void set(struct stat *stat, time_t value) override {
stat->st_atime = value;
}
};
INSTANTIATE_TEST_CASE_P(FuseLstatReturnATimeTest, FuseLstatReturnATimeTest, Values(
0,
100,
1416496809, // current timestamp as of writing the test
32503680000 // needs a 64bit timestamp
));
TEST_P(FuseLstatReturnATimeTest, ReturnedFileAtimeIsCorrect) {
struct ::stat result = CallFileLstatWithValue(GetParam());
EXPECT_EQ(GetParam(), result.st_atime);
}
TEST_P(FuseLstatReturnATimeTest, ReturnedDirAtimeIsCorrect) {
struct ::stat result = CallDirLstatWithValue(GetParam());
EXPECT_EQ(GetParam(), result.st_atime);
}

View File

@ -0,0 +1,27 @@
#include "testutils/FuseLstatReturnTest.h"
using ::testing::WithParamInterface;
using ::testing::Values;
class FuseLstatReturnCtimeTest: public FuseLstatReturnTest<time_t>, public WithParamInterface<time_t> {
private:
void set(struct stat *stat, time_t value) override {
stat->st_ctime = value;
}
};
INSTANTIATE_TEST_CASE_P(FuseLstatReturnCtimeTest, FuseLstatReturnCtimeTest, Values(
0,
100,
1416496809, // current timestamp as of writing the test
32503680000 // needs a 64bit timestamp
));
TEST_P(FuseLstatReturnCtimeTest, ReturnedFileCtimeIsCorrect) {
struct ::stat result = CallFileLstatWithValue(GetParam());
EXPECT_EQ(GetParam(), result.st_ctime);
}
TEST_P(FuseLstatReturnCtimeTest, ReturnedDirCtimeIsCorrect) {
struct ::stat result = CallDirLstatWithValue(GetParam());
EXPECT_EQ(GetParam(), result.st_ctime);
}

View File

@ -0,0 +1,25 @@
#include "testutils/FuseLstatReturnTest.h"
using ::testing::WithParamInterface;
using ::testing::Values;
class FuseLstatReturnGidTest: public FuseLstatReturnTest<gid_t>, public WithParamInterface<gid_t> {
private:
void set(struct stat *stat, gid_t value) override {
stat->st_gid = value;
}
};
INSTANTIATE_TEST_CASE_P(FuseLstatReturnGidTest, FuseLstatReturnGidTest, Values(
0,
10
));
TEST_P(FuseLstatReturnGidTest, ReturnedFileGidIsCorrect) {
struct ::stat result = CallFileLstatWithValue(GetParam());
EXPECT_EQ(GetParam(), result.st_gid);
}
TEST_P(FuseLstatReturnGidTest, ReturnedDirGidIsCorrect) {
struct ::stat result = CallDirLstatWithValue(GetParam());
EXPECT_EQ(GetParam(), result.st_gid);
}

View File

@ -0,0 +1,24 @@
#include "testutils/FuseLstatReturnTest.h"
using ::testing::WithParamInterface;
using ::testing::Values;
class FuseLstatReturnModeTest: public FuseLstatTest, public WithParamInterface<mode_t> {
public:
struct stat CallLstatWithValue(mode_t mode) {
return CallLstatWithImpl([mode] (struct stat *stat) {
stat->st_mode = mode;
});
}
};
INSTANTIATE_TEST_CASE_P(FuseLstatReturnModeTest, FuseLstatReturnModeTest, Values(
S_IFREG,
S_IFDIR,
S_IFREG | S_IRUSR | S_IWGRP | S_IXOTH, // a file with some access bits set
S_IFDIR | S_IWUSR | S_IXGRP | S_IROTH // a dir with some access bits set
));
TEST_P(FuseLstatReturnModeTest, ReturnedModeIsCorrect) {
struct ::stat result = CallLstatWithValue(GetParam());
EXPECT_EQ(GetParam(), result.st_mode);
}

View File

@ -0,0 +1,27 @@
#include "testutils/FuseLstatReturnTest.h"
using ::testing::WithParamInterface;
using ::testing::Values;
class FuseLstatReturnMtimeTest: public FuseLstatReturnTest<time_t>, public WithParamInterface<time_t> {
private:
void set(struct stat *stat, time_t value) override {
stat->st_mtime = value;
}
};
INSTANTIATE_TEST_CASE_P(FuseLstatReturnMtimeTest, FuseLstatReturnMtimeTest, Values(
0,
100,
1416496809, // current timestamp as of writing the test
32503680000 // needs a 64bit timestamp
));
TEST_P(FuseLstatReturnMtimeTest, ReturnedFileMtimeIsCorrect) {
struct ::stat result = CallFileLstatWithValue(GetParam());
EXPECT_EQ(GetParam(), result.st_mtime);
}
TEST_P(FuseLstatReturnMtimeTest, ReturnedDirMtimeIsCorrect) {
struct ::stat result = CallDirLstatWithValue(GetParam());
EXPECT_EQ(GetParam(), result.st_mtime);
}

View File

@ -0,0 +1,28 @@
#include "testutils/FuseLstatReturnTest.h"
using ::testing::WithParamInterface;
using ::testing::Values;
class FuseLstatReturnNlinkTest: public FuseLstatReturnTest<nlink_t>, public WithParamInterface<nlink_t> {
private:
void set(struct stat *stat, nlink_t value) override {
stat->st_nlink = value;
}
};
INSTANTIATE_TEST_CASE_P(FuseLstatReturnNlinkTest, FuseLstatReturnNlinkTest, Values(
1,
2,
5,
100
));
TEST_P(FuseLstatReturnNlinkTest, ReturnedFileNlinkIsCorrect) {
struct ::stat result = CallDirLstatWithValue(GetParam());
EXPECT_EQ(GetParam(), result.st_nlink);
}
TEST_P(FuseLstatReturnNlinkTest, ReturnedDirNlinkIsCorrect) {
struct ::stat result = CallDirLstatWithValue(GetParam());
EXPECT_EQ(GetParam(), result.st_nlink);
}

View File

@ -0,0 +1,27 @@
#include "testutils/FuseLstatReturnTest.h"
using ::testing::WithParamInterface;
using ::testing::Values;
class FuseLstatReturnSizeTest: public FuseLstatReturnTest<off_t>, public WithParamInterface<off_t> {
private:
void set(struct stat *stat, off_t value) override {
stat->st_size = value;
}
};
INSTANTIATE_TEST_CASE_P(FuseLstatReturnSizeTest, FuseLstatReturnSizeTest, Values(
0,
1,
4096,
1024*1024*1024
));
TEST_P(FuseLstatReturnSizeTest, ReturnedFileSizeIsCorrect) {
struct ::stat result = CallDirLstatWithValue(GetParam());
EXPECT_EQ(GetParam(), result.st_size);
}
TEST_P(FuseLstatReturnSizeTest, ReturnedDirSizeIsCorrect) {
struct ::stat result = CallDirLstatWithValue(GetParam());
EXPECT_EQ(GetParam(), result.st_size);
}

View File

@ -0,0 +1,25 @@
#include "testutils/FuseLstatReturnTest.h"
using ::testing::WithParamInterface;
using ::testing::Values;
class FuseLstatReturnUidTest: public FuseLstatReturnTest<uid_t>, public WithParamInterface<uid_t> {
private:
void set(struct stat *stat, uid_t value) override {
stat->st_uid = value;
}
};
INSTANTIATE_TEST_CASE_P(FuseLstatReturnUidTest, FuseLstatReturnUidTest, Values(
0,
10
));
TEST_P(FuseLstatReturnUidTest, ReturnedFileUidIsCorrect) {
struct ::stat result = CallFileLstatWithValue(GetParam());
EXPECT_EQ(GetParam(), result.st_uid);
}
TEST_P(FuseLstatReturnUidTest, ReturnedDirUidIsCorrect) {
struct ::stat result = CallDirLstatWithValue(GetParam());
EXPECT_EQ(GetParam(), result.st_uid);
}

View File

@ -0,0 +1,43 @@
#pragma once
#ifndef MESSMER_FSPP_TEST_FUSE_LSTAT_TESTUTILS_FUSELSTATRETURNTEST_H_
#define MESSMER_FSPP_TEST_FUSE_LSTAT_TESTUTILS_FUSELSTATRETURNTEST_H_
#include "FuseLstatTest.h"
// This class offers test helpers for testing (struct stat) entries. We return them from
// our mock filesystem, set up a temporary filesystem, call lstat syscall on it, and
// then check the return value.
template<typename Property>
class FuseLstatReturnTest: public FuseLstatTest {
public:
// Set the specified (struct stat) entry to the given value, and test whether it is correctly returned from the syscall.
// The CallFile[...] version tests it on a file node of the filesystem, the CallDir[...] version on a dir node.
struct stat CallFileLstatWithValue(Property value);
struct stat CallDirLstatWithValue(Property value);
private:
std::function<void(struct stat*)> SetPropertyImpl(Property value);
// Override this function to specify, how to set the specified (struct stat) entry on the passed (struct stat *) object.
virtual void set(struct stat *stat, Property value) = 0;
};
template<typename Property>
struct stat FuseLstatReturnTest<Property>::CallFileLstatWithValue(Property value) {
return CallFileLstatWithImpl(SetPropertyImpl(value));
}
template<typename Property>
struct stat FuseLstatReturnTest<Property>::CallDirLstatWithValue(Property value) {
return CallDirLstatWithImpl(SetPropertyImpl(value));
}
template<typename Property>
std::function<void(struct stat*)> FuseLstatReturnTest<Property>::SetPropertyImpl(Property value) {
return [this, value] (struct stat *stat) {
set(stat, value);
};
}
#endif

View File

@ -0,0 +1,59 @@
#include "FuseLstatTest.h"
using std::function;
using ::testing::StrEq;
using ::testing::_;
using ::testing::Invoke;
void FuseLstatTest::LstatPath(const std::string &path) {
struct stat dummy;
LstatPath(path, &dummy);
}
int FuseLstatTest::LstatPathReturnError(const std::string &path) {
struct stat dummy;
return LstatPathReturnError(path, &dummy);
}
void FuseLstatTest::LstatPath(const std::string &path, struct stat *result) {
int error = LstatPathReturnError(path, result);
EXPECT_EQ(0, error) << "lstat syscall failed. errno: " << error;
}
int FuseLstatTest::LstatPathReturnError(const std::string &path, struct stat *result) {
auto fs = TestFS();
auto realpath = fs->mountDir() / path;
int retval = ::lstat(realpath.c_str(), result);
if (retval == 0) {
return 0;
} else {
return errno;
}
}
struct stat FuseLstatTest::CallFileLstatWithImpl(function<void(struct stat*)> implementation) {
return CallLstatWithModeAndImpl(S_IFREG, implementation);
}
struct stat FuseLstatTest::CallDirLstatWithImpl(function<void(struct stat*)> implementation) {
return CallLstatWithModeAndImpl(S_IFDIR, implementation);
}
struct stat FuseLstatTest::CallLstatWithImpl(function<void(struct stat*)> implementation) {
EXPECT_CALL(fsimpl, lstat(StrEq(FILENAME), _)).WillRepeatedly(Invoke([implementation](const char*, struct ::stat *stat) {
implementation(stat);
}));
struct stat result;
LstatPath(FILENAME, &result);
return result;
}
struct stat FuseLstatTest::CallLstatWithModeAndImpl(mode_t mode, function<void(struct stat*)> implementation) {
return CallLstatWithImpl([mode, implementation] (struct stat *stat) {
stat->st_mode = mode;
implementation(stat);
});
}

View File

@ -0,0 +1,43 @@
#pragma once
#ifndef MESSMER_FSPP_TEST_FUSE_LSTAT_TESTUTILS_FUSELSTATTEST_H_
#define MESSMER_FSPP_TEST_FUSE_LSTAT_TESTUTILS_FUSELSTATTEST_H_
#include <string>
#include <functional>
#include <sys/stat.h>
#include "../../../testutils/FuseTest.h"
// This class offers some utility functions for testing lstat().
class FuseLstatTest: public FuseTest {
protected:
const char *FILENAME = "/myfile";
// Set up a temporary filesystem (using the fsimpl mock in FuseTest as filesystem implementation)
// and call the lstat syscall on the given (filesystem-relative) path.
void LstatPath(const std::string &path);
// Same as LstatPath above, but also return the result of the lstat syscall.
void LstatPath(const std::string &path, struct stat *result);
// These two functions are the same as LstatPath above, but they don't fail the test when the lstat syscall
// crashes. Instead, they return the value of errno after calling ::lstat.
int LstatPathReturnError(const std::string &path);
int LstatPathReturnError(const std::string &path, struct stat *result);
// You can specify an implementation, which can modify the (struct stat *) result,
// our fuse mock filesystem implementation will then return this to fuse on an lstat call.
// This functions then set up a temporary filesystem with this mock, call lstat on a filesystem node
// and return the (struct stat) returned from an lstat syscall to this filesystem.
struct stat CallLstatWithImpl(std::function<void(struct stat*)> implementation);
// These two functions are like CallLstatWithImpl, but they also modify the (struct stat).st_mode
// field, so the node accessed is specified to be a file/directory.
struct stat CallFileLstatWithImpl(std::function<void(struct stat*)> implementation);
struct stat CallDirLstatWithImpl(std::function<void(struct stat*)> implementation);
private:
struct stat CallLstatWithModeAndImpl(mode_t mode, std::function<void(struct stat*)> implementation);
};
#endif

View File

@ -0,0 +1,43 @@
#include "testutils/FuseMkdirTest.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::Return;
using ::testing::Invoke;
using ::testing::Action;
class FuseMkdirDirnameTest: public FuseMkdirTest {
};
TEST_F(FuseMkdirDirnameTest, Mkdir) {
ReturnDoesntExistOnLstat("/mydir");
EXPECT_CALL(fsimpl, mkdir(StrEq("/mydir"), _, _, _))
// After mkdir was called, lstat should return that it is a dir.
// This is needed to make the ::mkdir() syscall pass.
.Times(1).WillOnce(FromNowOnReturnIsDirOnLstat());
Mkdir("/mydir", 0);
}
TEST_F(FuseMkdirDirnameTest, MkdirNested) {
ReturnIsDirOnLstat("/mydir");
ReturnDoesntExistOnLstat("/mydir/mysubdir");
EXPECT_CALL(fsimpl, mkdir(StrEq("/mydir/mysubdir"), _, _, _))
// After mkdir was called, lstat should return that it is a dir.
// This is needed to make the ::mkdir() syscall pass.
.Times(1).WillOnce(FromNowOnReturnIsDirOnLstat());
Mkdir("/mydir/mysubdir", 0);
}
TEST_F(FuseMkdirDirnameTest, MkdirNested2) {
ReturnIsDirOnLstat("/mydir");
ReturnIsDirOnLstat("/mydir/mydir2");
ReturnDoesntExistOnLstat("/mydir/mydir2/mydir3");
EXPECT_CALL(fsimpl, mkdir(StrEq("/mydir/mydir2/mydir3"), _, _, _))
// After mkdir was called, lstat should return that it is a dir.
// This is needed to make the ::mkdir() syscall pass.
.Times(1).WillOnce(FromNowOnReturnIsDirOnLstat());
Mkdir("/mydir/mydir2/mydir3", 0);
}

View File

@ -0,0 +1,33 @@
#include "testutils/FuseMkdirTest.h"
#include "../../../fuse/FuseErrnoException.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::Throw;
using ::testing::WithParamInterface;
using ::testing::Values;
using namespace fspp::fuse;
class FuseMkdirErrorTest: public FuseMkdirTest, public WithParamInterface<int> {
};
INSTANTIATE_TEST_CASE_P(FuseMkdirErrorTest, FuseMkdirErrorTest, Values(EACCES, EDQUOT, EEXIST, EFAULT, ELOOP, EMLINK, ENAMETOOLONG, ENOENT, ENOMEM, ENOSPC, ENOTDIR, EPERM, EROFS, EBADF));
TEST_F(FuseMkdirErrorTest, NoError) {
ReturnDoesntExistOnLstat(DIRNAME);
EXPECT_CALL(fsimpl, mkdir(StrEq(DIRNAME), _, _, _))
.Times(1).WillOnce(FromNowOnReturnIsDirOnLstat());
int error = MkdirReturnError(DIRNAME, 0);
EXPECT_EQ(0, error);
}
TEST_P(FuseMkdirErrorTest, ReturnedErrorIsCorrect) {
ReturnDoesntExistOnLstat(DIRNAME);
EXPECT_CALL(fsimpl, mkdir(StrEq(DIRNAME), _, _, _))
.Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
int error = MkdirReturnError(DIRNAME, 0);
EXPECT_EQ(GetParam(), error);
}

View File

@ -0,0 +1,20 @@
#include "testutils/FuseMkdirTest.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::Return;
using ::testing::WithParamInterface;
using ::testing::Values;
class FuseMkdirModeTest: public FuseMkdirTest, public WithParamInterface<mode_t> {
};
INSTANTIATE_TEST_CASE_P(FuseMkdirModeTest, FuseMkdirModeTest, Values(0, S_IRUSR, S_IRGRP, S_IXOTH, S_IRUSR|S_IRGRP|S_IROTH|S_IRGRP));
TEST_P(FuseMkdirModeTest, Mkdir) {
ReturnDoesntExistOnLstat(DIRNAME);
EXPECT_CALL(fsimpl, mkdir(StrEq(DIRNAME), GetParam(), _, _))
.Times(1).WillOnce(FromNowOnReturnIsDirOnLstat());
Mkdir(DIRNAME, GetParam());
}

View File

@ -0,0 +1,27 @@
#include "FuseMkdirTest.h"
using ::testing::Action;
using ::testing::Invoke;
void FuseMkdirTest::Mkdir(const char *dirname, mode_t mode) {
int error = MkdirReturnError(dirname, mode);
EXPECT_EQ(0, error);
}
int FuseMkdirTest::MkdirReturnError(const char *dirname, mode_t mode) {
auto fs = TestFS();
auto realpath = fs->mountDir() / dirname;
int retval = ::mkdir(realpath.c_str(), mode);
if (retval == 0) {
return 0;
} else {
return errno;
}
}
Action<void(const char*, mode_t, uid_t, gid_t)> FuseMkdirTest::FromNowOnReturnIsDirOnLstat() {
return Invoke([this](const char *dirname, mode_t, uid_t, gid_t) {
ReturnIsDirOnLstat(dirname);
});
}

View File

@ -0,0 +1,17 @@
#pragma once
#ifndef MESSMER_FSPP_TEST_FUSE_MKDIR_TESTUTILS_FUSEMKDIRTEST_H_
#define MESSMER_FSPP_TEST_FUSE_MKDIR_TESTUTILS_FUSEMKDIRTEST_H_
#include "../../../testutils/FuseTest.h"
class FuseMkdirTest: public FuseTest {
public:
const char *DIRNAME = "/mydir";
void Mkdir(const char *dirname, mode_t mode);
int MkdirReturnError(const char *dirname, mode_t mode);
::testing::Action<void(const char*, mode_t, uid_t, gid_t)> FromNowOnReturnIsDirOnLstat();
};
#endif

View File

@ -0,0 +1,31 @@
#include "testutils/FuseOpenTest.h"
#include "../../../fuse/FuseErrnoException.h"
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Return;
using ::testing::Throw;
using ::testing::StrEq;
using ::testing::_;
using namespace fspp::fuse;
class FuseOpenErrorTest: public FuseOpenTest, public WithParamInterface<int> {
};
INSTANTIATE_TEST_CASE_P(FuseOpenErrorTest, FuseOpenErrorTest, Values(EACCES, EDQUOT, EEXIST, EFAULT, EFBIG, EINTR, EOVERFLOW, EINVAL, EISDIR, ELOOP, EMFILE, ENAMETOOLONG, ENFILE, ENODEV, ENOENT, ENOMEM, ENOSPC, ENOTDIR, ENXIO, EOPNOTSUPP, EPERM, EROFS, ETXTBSY, EWOULDBLOCK, EBADF, ENOTDIR));
TEST_F(FuseOpenErrorTest, ReturnNoError) {
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _)).Times(1).WillOnce(Return(1));
errno = 0;
int error = OpenFileReturnError(FILENAME, O_RDONLY);
EXPECT_EQ(0, error);
}
TEST_P(FuseOpenErrorTest, ReturnError) {
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
int error = OpenFileReturnError(FILENAME, O_RDONLY);
EXPECT_EQ(GetParam(), error);
}

View File

@ -0,0 +1,41 @@
#include "testutils/FuseOpenTest.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Return;
class FuseOpenFileDescriptorTest: public FuseOpenTest, public WithParamInterface<int> {
public:
void OpenAndReadFile(const char *filename) {
auto fs = TestFS();
int fd = OpenFile(fs.get(), filename);
ReadFile(fd);
}
private:
int OpenFile(const TempTestFS *fs, const char *filename) {
auto realpath = fs->mountDir() / filename;
int fd = ::open(realpath.c_str(), O_RDONLY);
EXPECT_GE(fd, 0) << "Opening file failed";
return fd;
}
void ReadFile(int fd) {
uint8_t buf;
int retval = ::read(fd, &buf, 1);
EXPECT_EQ(1, retval) << "Reading file failed";
}
};
INSTANTIATE_TEST_CASE_P(FuseOpenFileDescriptorTest, FuseOpenFileDescriptorTest, Values(0, 2, 5, 1000, 1024*1024*1024));
TEST_P(FuseOpenFileDescriptorTest, TestReturnedFileDescriptor) {
ReturnIsFileOnLstatWithSize(FILENAME, 1);
EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _))
.Times(1).WillOnce(Return(GetParam()));
EXPECT_CALL(fsimpl, read(GetParam(), _, _, _)).Times(1).WillOnce(Return(1));
OpenAndReadFile(FILENAME);
}

View File

@ -0,0 +1,36 @@
#include "testutils/FuseOpenTest.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::Return;
class FuseOpenFilenameTest: public FuseOpenTest {
public:
};
TEST_F(FuseOpenFilenameTest, OpenFile) {
ReturnIsFileOnLstat("/myfile");
EXPECT_CALL(fsimpl, openFile(StrEq("/myfile"), _))
.Times(1).WillOnce(Return(0));
OpenFile("/myfile", O_RDONLY);
}
TEST_F(FuseOpenFilenameTest, OpenFileNested) {
ReturnIsDirOnLstat("/mydir");
ReturnIsFileOnLstat("/mydir/myfile");
EXPECT_CALL(fsimpl, openFile(StrEq("/mydir/myfile"), _))
.Times(1).WillOnce(Return(0));
OpenFile("/mydir/myfile", O_RDONLY);
}
TEST_F(FuseOpenFilenameTest, OpenFileNested2) {
ReturnIsDirOnLstat("/mydir");
ReturnIsDirOnLstat("/mydir/mydir2");
ReturnIsFileOnLstat("/mydir/mydir2/myfile");
EXPECT_CALL(fsimpl, openFile(StrEq("/mydir/mydir2/myfile"), _))
.Times(1).WillOnce(Return(0));
OpenFile("/mydir/mydir2/myfile", O_RDONLY);
}

View File

@ -0,0 +1,19 @@
#include "testutils/FuseOpenTest.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Return;
class FuseOpenFlagsTest: public FuseOpenTest, public WithParamInterface<int> {
};
INSTANTIATE_TEST_CASE_P(FuseOpenFlagsTest, FuseOpenFlagsTest, Values(O_RDWR, O_RDONLY, O_WRONLY));
TEST_P(FuseOpenFlagsTest, testFlags) {
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), OpenFlagsEq(GetParam())))
.Times(1).WillOnce(Return(0));
OpenFile(FILENAME, GetParam());
}

View File

@ -0,0 +1,28 @@
#include "FuseOpenTest.h"
int FuseOpenTest::OpenFile(const char *filename, int flags) {
int fd = OpenFileAllowError(filename, flags);
EXPECT_GE(fd, 0);
return fd;
}
int FuseOpenTest::OpenFileReturnError(const char *filename, int flags) {
int fd = OpenFileAllowError(filename, flags);
if (fd >= 0) {
return 0;
} else {
return -fd;
}
}
int FuseOpenTest::OpenFileAllowError(const char *filename, int flags) {
auto fs = TestFS();
auto realpath = fs->mountDir() / filename;
int fd = ::open(realpath.c_str(), flags);
if (fd >= 0) {
return fd;
} else {
return -errno;
}
}

View File

@ -0,0 +1,21 @@
#pragma once
#ifndef MESSMER_FSPP_TEST_FUSE_OPENFILE_TESTUTILS_FUSEOPENTEST_H_
#define MESSMER_FSPP_TEST_FUSE_OPENFILE_TESTUTILS_FUSEOPENTEST_H_
#include "../../../testutils/FuseTest.h"
class FuseOpenTest: public FuseTest {
public:
const char *FILENAME = "/myfile";
int OpenFile(const char *FILENAME, int flags);
int OpenFileReturnError(const char *FILENAME, int flags);
private:
int OpenFileAllowError(const char *FILENAME, int flags);
};
MATCHER_P(OpenFlagsEq, expectedFlags, "") {
return expectedFlags == (O_ACCMODE & arg);
}
#endif

View File

@ -0,0 +1,61 @@
#include "testutils/FuseReadTest.h"
#include "../../../fuse/FuseErrnoException.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Eq;
using ::testing::Ne;
using ::testing::Return;
using ::testing::Invoke;
using ::testing::Throw;
using namespace fspp::fuse;
class FuseReadErrorTest: public FuseReadTest, public WithParamInterface<int> {
public:
size_t FILESIZE = 64*1024*1024;
size_t READCOUNT = 32*1024*1024;
void SetUp() override {
//Make the file size big enough that fuse should issue at least two reads
ReturnIsFileOnLstatWithSize(FILENAME, FILESIZE);
OnOpenReturnFileDescriptor(FILENAME, 0);
}
};
INSTANTIATE_TEST_CASE_P(FuseReadErrorTest, FuseReadErrorTest, Values(EAGAIN, EBADF, EFAULT, EINTR, EINVAL, EIO, EISDIR, EOVERFLOW, ESPIPE, ENXIO));
TEST_P(FuseReadErrorTest, ReturnErrorOnFirstReadCall) {
EXPECT_CALL(fsimpl, read(0, _, _, _))
.WillRepeatedly(Throw(FuseErrnoException(GetParam())));
char *buf = new char[READCOUNT];
auto retval = ReadFileReturnError(FILENAME, buf, READCOUNT, 0);
EXPECT_EQ(GetParam(), retval.error);
delete[] buf;
}
TEST_P(FuseReadErrorTest, ReturnErrorOnSecondReadCall) {
// The first read request is from the beginning of the file and works, but the later ones fail.
// We store the number of bytes the first call could successfully read and check later that our
// read syscall returns exactly this number of bytes
size_t successfullyReadBytes = -1;
EXPECT_CALL(fsimpl, read(0, _, _, Eq(0)))
.Times(1)
.WillOnce(Invoke([&successfullyReadBytes](int, void*, size_t count, off_t) {
// Store the number of successfully read bytes
successfullyReadBytes = count;
return count;
}));
EXPECT_CALL(fsimpl, read(0, _, _, Ne(0)))
.WillRepeatedly(Throw(FuseErrnoException(GetParam())));
char *buf = new char[READCOUNT];
auto retval = ReadFileReturnError(FILENAME, buf, READCOUNT, 0);
EXPECT_EQ(0, retval.error);
EXPECT_EQ(successfullyReadBytes, retval.read_bytes); // Check that we're getting the number of successfully read bytes (the first read call) returned
delete[] buf;
}

View File

@ -0,0 +1,29 @@
#include "testutils/FuseReadTest.h"
#include "../../../fuse/FuseErrnoException.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Eq;
using ::testing::Return;
using ::testing::Invoke;
using ::testing::Throw;
using namespace fspp::fuse;
class FuseReadFileDescriptorTest: public FuseReadTest, public WithParamInterface<int> {
};
INSTANTIATE_TEST_CASE_P(FuseReadFileDescriptorTest, FuseReadFileDescriptorTest, Values(0,1,10,1000,1024*1024*1024));
TEST_P(FuseReadFileDescriptorTest, FileDescriptorIsCorrect) {
ReturnIsFileOnLstatWithSize(FILENAME, 1);
OnOpenReturnFileDescriptor(FILENAME, GetParam());
EXPECT_CALL(fsimpl, read(Eq(GetParam()), _, _, _))
.Times(1).WillOnce(ReturnSuccessfulRead);
char buf[1];
ReadFile(FILENAME, buf, 1, 0);
}

View File

@ -0,0 +1,40 @@
#include "testutils/FuseReadTest.h"
#include "../../../fuse/FuseErrnoException.h"
using ::testing::_;
using ::testing::StrEq;
using ::testing::Eq;
using ::testing::Return;
using ::testing::Invoke;
using ::testing::Action;
using std::min;
using namespace fspp::fuse;
class FuseReadOverflowTest: public FuseReadTest {
public:
const size_t FILESIZE = 1000;
const size_t READSIZE = 2000;
const size_t OFFSET = 500;
void SetUp() override {
ReturnIsFileOnLstatWithSize(FILENAME, FILESIZE);
OnOpenReturnFileDescriptor(FILENAME, 0);
EXPECT_CALL(fsimpl, read(0, _, _, _)).WillRepeatedly(ReturnSuccessfulReadRegardingSize(FILESIZE));
}
};
TEST_F(FuseReadOverflowTest, ReadMoreThanFileSizeFromBeginning) {
char buf[READSIZE];
auto retval = ReadFileReturnError(FILENAME, buf, READSIZE, 0);
EXPECT_EQ(FILESIZE, retval.read_bytes);
}
TEST_F(FuseReadOverflowTest, ReadMoreThanFileSizeFromMiddle) {
char buf[READSIZE];
auto retval = ReadFileReturnError(FILENAME, buf, READSIZE, OFFSET);
EXPECT_EQ(FILESIZE-OFFSET, retval.read_bytes);
}

View File

@ -0,0 +1,75 @@
#include <messmer/cpp-utils/data/DataFixture.h>
#include <messmer/cpp-utils/data/Data.h>
#include "../../testutils/InMemoryFile.h"
#include "testutils/FuseReadTest.h"
#include <messmer/cpp-utils/pointer/unique_ref.h>
#include "../../../fuse/FuseErrnoException.h"
#include <tuple>
#include <cstdlib>
using ::testing::_;
using ::testing::StrEq;
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Combine;
using ::testing::Eq;
using ::testing::Return;
using ::testing::Invoke;
using ::testing::Action;
using std::tuple;
using std::get;
using std::min;
using cpputils::Data;
using cpputils::DataFixture;
using namespace fspp::fuse;
// We can't test the count or size parameter directly, because fuse doesn't pass them 1:1.
// It usually asks to read bigger blocks (probably does some caching).
// But we can test that the data returned from the ::read syscall is the correct data region.
struct TestData {
TestData(): count(0), offset(0), additional_bytes_at_end_of_file(0) {}
TestData(const tuple<size_t, off_t, size_t> &data): count(get<0>(data)), offset(get<1>(data)), additional_bytes_at_end_of_file(get<2>(data)) {}
size_t count;
off_t offset;
//How many more bytes does the file have after the read block?
size_t additional_bytes_at_end_of_file;
size_t fileSize() {
return count + offset + additional_bytes_at_end_of_file;
}
};
// The testcase creates random data in memory, offers a mock read() implementation to read from this
// memory region and check methods to check for data equality of a region.
class FuseReadReturnedDataTest: public FuseReadTest, public WithParamInterface<tuple<size_t, off_t, size_t>> {
public:
std::unique_ptr<InMemoryFile> testFile;
TestData testData;
FuseReadReturnedDataTest()
: testFile(nullptr),
testData(GetParam()) {
testFile = std::make_unique<InMemoryFile>(DataFixture::generate(testData.fileSize()));
ReturnIsFileOnLstatWithSize(FILENAME, testData.fileSize());
OnOpenReturnFileDescriptor(FILENAME, 0);
EXPECT_CALL(fsimpl, read(0, _, _, _))
.WillRepeatedly(ReadFromFile);
}
// This read() mock implementation reads from the stored virtual file (testFile).
Action<size_t(int, void*, size_t, off_t)> ReadFromFile = Invoke([this](int, void *buf, size_t count, off_t offset) {
return testFile->read(buf, count, offset);
});
};
INSTANTIATE_TEST_CASE_P(FuseReadReturnedDataTest, FuseReadReturnedDataTest, Combine(Values(0,1,10,1000,1024, 10*1024*1024), Values(0, 1, 10, 1024, 10*1024*1024), Values(0, 1, 10, 1024, 10*1024*1024)));
TEST_P(FuseReadReturnedDataTest, ReturnedDataRangeIsCorrect) {
Data buf(testData.count);
ReadFile(FILENAME, buf.data(), testData.count, testData.offset);
EXPECT_TRUE(testFile->fileContentEquals(buf, testData.offset));
}

View File

@ -0,0 +1,26 @@
#include "FuseReadTest.h"
void FuseReadTest::ReadFile(const char *filename, void *buf, size_t count, off_t offset) {
auto retval = ReadFileReturnError(filename, buf, count, offset);
EXPECT_EQ(0, retval.error);
EXPECT_EQ(count, retval.read_bytes);
}
FuseReadTest::ReadError FuseReadTest::ReadFileReturnError(const char *filename, void *buf, size_t count, off_t offset) {
auto fs = TestFS();
int fd = OpenFile(fs.get(), filename);
ReadError result;
errno = 0;
result.read_bytes = ::pread(fd, buf, count, offset);
result.error = errno;
return result;
}
int FuseReadTest::OpenFile(const TempTestFS *fs, const char *filename) {
auto realpath = fs->mountDir() / filename;
int fd = ::open(realpath.c_str(), O_RDONLY);
EXPECT_GE(fd, 0) << "Error opening file";
return fd;
}

View File

@ -0,0 +1,35 @@
#pragma once
#ifndef MESSMER_FSPP_TEST_FUSE_READ_TESTUTILS_FUSEREADTEST_H_
#define MESSMER_FSPP_TEST_FUSE_READ_TESTUTILS_FUSEREADTEST_H_
#include "../../../testutils/FuseTest.h"
class FuseReadTest: public FuseTest {
public:
const char *FILENAME = "/myfile";
struct ReadError {
int error;
size_t read_bytes;
};
void ReadFile(const char *filename, void *buf, size_t count, off_t offset);
ReadError ReadFileReturnError(const char *filename, void *buf, size_t count, off_t offset);
::testing::Action<size_t(int, void*, size_t, off_t)> ReturnSuccessfulRead =
::testing::Invoke([](int, void *, size_t count, off_t) {
return count;
});
::testing::Action<size_t(int, void*, size_t, off_t)> ReturnSuccessfulReadRegardingSize(size_t filesize) {
return ::testing::Invoke([filesize](int, void *, size_t count, off_t offset) {
size_t ableToReadCount = std::min(count, (size_t)(filesize - offset));
return ableToReadCount;
});
}
private:
int OpenFile(const TempTestFS *fs, const char *filename);
};
#endif

Some files were not shown because too many files have changed in this diff Show More