Merge fspp repository into here
This commit is contained in:
commit
0fb710ac69
@ -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
112
CMakeLists.fspp.txt
Normal 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
|
@ -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
44
biicode.conf
Normal 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
22
fs_interface/Device.h
Normal 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
40
fs_interface/Dir.h
Normal 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
22
fs_interface/File.h
Normal 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
26
fs_interface/Node.h
Normal 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
26
fs_interface/OpenFile.h
Normal 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
21
fs_interface/Symlink.h
Normal 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
19
fstest/FsTest.h
Normal 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
105
fstest/FsppDeviceTest.h
Normal 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
289
fstest/FsppDirTest.h
Normal 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
179
fstest/FsppFileTest.h
Normal 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
30
fstest/FsppOpenFileTest.h
Normal 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
78
fstest/FsppSymlinkTest.h
Normal 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
|
64
fstest/testutils/FileSystemTest.h
Normal file
64
fstest/testutils/FileSystemTest.h
Normal 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
|
51
fstest/testutils/FileTest.h
Normal file
51
fstest/testutils/FileTest.h
Normal 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
53
fuse/Filesystem.h
Normal 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
831
fuse/Fuse.cpp
Normal 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
72
fuse/Fuse.h
Normal 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
39
fuse/FuseErrnoException.h
Normal 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
8
fuse/params.h
Normal 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
321
impl/FilesystemImpl.cpp
Normal 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
105
impl/FilesystemImpl.h
Normal 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
49
impl/FuseOpenFileList.h
Normal 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
72
impl/IdList.h
Normal 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
1
impl/Profiler.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "Profiler.h"
|
32
impl/Profiler.h
Normal file
32
impl/Profiler.h
Normal 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
|
4
test/fs_interface/DeviceTest.cpp
Normal file
4
test/fs_interface/DeviceTest.cpp
Normal file
@ -0,0 +1,4 @@
|
||||
/*
|
||||
* Tests that the header can be included without needing additional header includes as dependencies.
|
||||
*/
|
||||
#include "../../fs_interface/Device.h"
|
4
test/fs_interface/DirTest.cpp
Normal file
4
test/fs_interface/DirTest.cpp
Normal file
@ -0,0 +1,4 @@
|
||||
/*
|
||||
* Tests that the header can be included without needing additional header includes as dependencies.
|
||||
*/
|
||||
#include "../../fs_interface/Dir.h"
|
4
test/fs_interface/FileTest.cpp
Normal file
4
test/fs_interface/FileTest.cpp
Normal file
@ -0,0 +1,4 @@
|
||||
/*
|
||||
* Tests that the header can be included without needing additional header includes as dependencies.
|
||||
*/
|
||||
#include "../../fs_interface/File.h"
|
4
test/fs_interface/NodeTest.cpp
Normal file
4
test/fs_interface/NodeTest.cpp
Normal file
@ -0,0 +1,4 @@
|
||||
/*
|
||||
* Tests that the header can be included without needing additional header includes as dependencies.
|
||||
*/
|
||||
#include "../../fs_interface/Node.h"
|
4
test/fs_interface/OpenFileTest.cpp
Normal file
4
test/fs_interface/OpenFileTest.cpp
Normal file
@ -0,0 +1,4 @@
|
||||
/*
|
||||
* Tests that the header can be included without needing additional header includes as dependencies.
|
||||
*/
|
||||
#include "../../fs_interface/OpenFile.h"
|
14
test/fuse/BasicFuseTest.cpp
Normal file
14
test/fuse/BasicFuseTest.cpp
Normal 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();
|
||||
}
|
4
test/fuse/FilesystemTest.cpp
Normal file
4
test/fuse/FilesystemTest.cpp
Normal file
@ -0,0 +1,4 @@
|
||||
/*
|
||||
* Tests that the header can be included without needing additional header includes as dependencies.
|
||||
*/
|
||||
#include "../../fuse/Filesystem.h"
|
24
test/fuse/access/FuseAccessErrorTest.cpp
Normal file
24
test/fuse/access/FuseAccessErrorTest.cpp
Normal 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);
|
||||
}
|
35
test/fuse/access/FuseAccessFilenameTest.cpp
Normal file
35
test/fuse/access/FuseAccessFilenameTest.cpp
Normal 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);
|
||||
}
|
20
test/fuse/access/FuseAccessModeTest.cpp
Normal file
20
test/fuse/access/FuseAccessModeTest.cpp
Normal 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());
|
||||
}
|
18
test/fuse/access/testutils/FuseAccessTest.cpp
Normal file
18
test/fuse/access/testutils/FuseAccessTest.cpp
Normal 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;
|
||||
}
|
||||
}
|
15
test/fuse/access/testutils/FuseAccessTest.h
Normal file
15
test/fuse/access/testutils/FuseAccessTest.h
Normal 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
|
99
test/fuse/closeFile/FuseCloseTest.cpp
Normal file
99
test/fuse/closeFile/FuseCloseTest.cpp
Normal 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));
|
||||
}*/
|
34
test/fuse/createAndOpenFile/FuseCreateAndOpenErrorTest.cpp
Normal file
34
test/fuse/createAndOpenFile/FuseCreateAndOpenErrorTest.cpp
Normal 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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
22
test/fuse/createAndOpenFile/FuseCreateAndOpenFlagsTest.cpp
Normal file
22
test/fuse/createAndOpenFile/FuseCreateAndOpenFlagsTest.cpp
Normal 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());
|
||||
}*/
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
25
test/fuse/fdatasync/FuseFdatasyncErrorTest.cpp
Normal file
25
test/fuse/fdatasync/FuseFdatasyncErrorTest.cpp
Normal 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);
|
||||
}
|
26
test/fuse/fdatasync/FuseFdatasyncFileDescriptorTest.cpp
Normal file
26
test/fuse/fdatasync/FuseFdatasyncFileDescriptorTest.cpp
Normal 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);
|
||||
}
|
31
test/fuse/fdatasync/testutils/FuseFdatasyncTest.cpp
Normal file
31
test/fuse/fdatasync/testutils/FuseFdatasyncTest.cpp
Normal 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;
|
||||
}
|
18
test/fuse/fdatasync/testutils/FuseFdatasyncTest.h
Normal file
18
test/fuse/fdatasync/testutils/FuseFdatasyncTest.h
Normal 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
|
32
test/fuse/flush/FuseFlushErrorTest.cpp
Normal file
32
test/fuse/flush/FuseFlushErrorTest.cpp
Normal 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);
|
||||
}
|
35
test/fuse/flush/FuseFlushFileDescriptorTest.cpp
Normal file
35
test/fuse/flush/FuseFlushFileDescriptorTest.cpp
Normal 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);
|
||||
}
|
19
test/fuse/flush/testutils/FuseFlushTest.cpp
Normal file
19
test/fuse/flush/testutils/FuseFlushTest.cpp
Normal 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);
|
||||
}
|
17
test/fuse/flush/testutils/FuseFlushTest.h
Normal file
17
test/fuse/flush/testutils/FuseFlushTest.h
Normal 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
|
39
test/fuse/fstat/FuseFstatErrorTest.cpp
Normal file
39
test/fuse/fstat/FuseFstatErrorTest.cpp
Normal 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);
|
||||
}
|
37
test/fuse/fstat/FuseFstatParameterTest.cpp
Normal file
37
test/fuse/fstat/FuseFstatParameterTest.cpp
Normal 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
6
test/fuse/fstat/README
Normal 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.
|
34
test/fuse/fstat/testutils/FuseFstatTest.cpp
Normal file
34
test/fuse/fstat/testutils/FuseFstatTest.cpp
Normal 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));
|
||||
}
|
17
test/fuse/fstat/testutils/FuseFstatTest.h
Normal file
17
test/fuse/fstat/testutils/FuseFstatTest.h
Normal 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
|
25
test/fuse/fsync/FuseFsyncErrorTest.cpp
Normal file
25
test/fuse/fsync/FuseFsyncErrorTest.cpp
Normal 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);
|
||||
}
|
26
test/fuse/fsync/FuseFsyncFileDescriptorTest.cpp
Normal file
26
test/fuse/fsync/FuseFsyncFileDescriptorTest.cpp
Normal 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);
|
||||
}
|
25
test/fuse/fsync/testutils/FuseFsyncTest.cpp
Normal file
25
test/fuse/fsync/testutils/FuseFsyncTest.cpp
Normal 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;
|
||||
}
|
18
test/fuse/fsync/testutils/FuseFsyncTest.h
Normal file
18
test/fuse/fsync/testutils/FuseFsyncTest.h
Normal 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
|
27
test/fuse/ftruncate/FuseFTruncateErrorTest.cpp
Normal file
27
test/fuse/ftruncate/FuseFTruncateErrorTest.cpp
Normal 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);
|
||||
}
|
29
test/fuse/ftruncate/FuseFTruncateFileDescriptorTest.cpp
Normal file
29
test/fuse/ftruncate/FuseFTruncateFileDescriptorTest.cpp
Normal 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);
|
||||
}
|
24
test/fuse/ftruncate/FuseFTruncateSizeTest.cpp
Normal file
24
test/fuse/ftruncate/FuseFTruncateSizeTest.cpp
Normal 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());
|
||||
}
|
25
test/fuse/ftruncate/testutils/FuseFTruncateTest.cpp
Normal file
25
test/fuse/ftruncate/testutils/FuseFTruncateTest.cpp
Normal 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;
|
||||
}
|
18
test/fuse/ftruncate/testutils/FuseFTruncateTest.h
Normal file
18
test/fuse/ftruncate/testutils/FuseFTruncateTest.h
Normal 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
|
29
test/fuse/lstat/FuseLstatErrorTest.cpp
Normal file
29
test/fuse/lstat/FuseLstatErrorTest.cpp
Normal 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);
|
||||
}
|
36
test/fuse/lstat/FuseLstatPathParameterTest.cpp
Normal file
36
test/fuse/lstat/FuseLstatPathParameterTest.cpp
Normal 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/");
|
||||
}
|
27
test/fuse/lstat/FuseLstatReturnAtimeTest.cpp
Normal file
27
test/fuse/lstat/FuseLstatReturnAtimeTest.cpp
Normal 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);
|
||||
}
|
27
test/fuse/lstat/FuseLstatReturnCtimeTest.cpp
Normal file
27
test/fuse/lstat/FuseLstatReturnCtimeTest.cpp
Normal 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);
|
||||
}
|
25
test/fuse/lstat/FuseLstatReturnGidTest.cpp
Normal file
25
test/fuse/lstat/FuseLstatReturnGidTest.cpp
Normal 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);
|
||||
}
|
24
test/fuse/lstat/FuseLstatReturnModeTest.cpp
Normal file
24
test/fuse/lstat/FuseLstatReturnModeTest.cpp
Normal 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);
|
||||
}
|
27
test/fuse/lstat/FuseLstatReturnMtimeTest.cpp
Normal file
27
test/fuse/lstat/FuseLstatReturnMtimeTest.cpp
Normal 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);
|
||||
}
|
28
test/fuse/lstat/FuseLstatReturnNlinkTest.cpp
Normal file
28
test/fuse/lstat/FuseLstatReturnNlinkTest.cpp
Normal 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);
|
||||
}
|
||||
|
27
test/fuse/lstat/FuseLstatReturnSizeTest.cpp
Normal file
27
test/fuse/lstat/FuseLstatReturnSizeTest.cpp
Normal 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);
|
||||
}
|
25
test/fuse/lstat/FuseLstatReturnUidTest.cpp
Normal file
25
test/fuse/lstat/FuseLstatReturnUidTest.cpp
Normal 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);
|
||||
}
|
43
test/fuse/lstat/testutils/FuseLstatReturnTest.h
Normal file
43
test/fuse/lstat/testutils/FuseLstatReturnTest.h
Normal 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
|
59
test/fuse/lstat/testutils/FuseLstatTest.cpp
Normal file
59
test/fuse/lstat/testutils/FuseLstatTest.cpp
Normal 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);
|
||||
});
|
||||
}
|
43
test/fuse/lstat/testutils/FuseLstatTest.h
Normal file
43
test/fuse/lstat/testutils/FuseLstatTest.h
Normal 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
|
43
test/fuse/mkdir/FuseMkdirDirnameTest.cpp
Normal file
43
test/fuse/mkdir/FuseMkdirDirnameTest.cpp
Normal 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);
|
||||
}
|
33
test/fuse/mkdir/FuseMkdirErrorTest.cpp
Normal file
33
test/fuse/mkdir/FuseMkdirErrorTest.cpp
Normal 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);
|
||||
}
|
20
test/fuse/mkdir/FuseMkdirModeTest.cpp
Normal file
20
test/fuse/mkdir/FuseMkdirModeTest.cpp
Normal 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());
|
||||
}
|
27
test/fuse/mkdir/testutils/FuseMkdirTest.cpp
Normal file
27
test/fuse/mkdir/testutils/FuseMkdirTest.cpp
Normal 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);
|
||||
});
|
||||
}
|
17
test/fuse/mkdir/testutils/FuseMkdirTest.h
Normal file
17
test/fuse/mkdir/testutils/FuseMkdirTest.h
Normal 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
|
31
test/fuse/openFile/FuseOpenErrorTest.cpp
Normal file
31
test/fuse/openFile/FuseOpenErrorTest.cpp
Normal 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);
|
||||
}
|
41
test/fuse/openFile/FuseOpenFileDescriptorTest.cpp
Normal file
41
test/fuse/openFile/FuseOpenFileDescriptorTest.cpp
Normal 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);
|
||||
}
|
||||
|
36
test/fuse/openFile/FuseOpenFilenameTest.cpp
Normal file
36
test/fuse/openFile/FuseOpenFilenameTest.cpp
Normal 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);
|
||||
}
|
19
test/fuse/openFile/FuseOpenFlagsTest.cpp
Normal file
19
test/fuse/openFile/FuseOpenFlagsTest.cpp
Normal 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());
|
||||
}
|
28
test/fuse/openFile/testutils/FuseOpenTest.cpp
Normal file
28
test/fuse/openFile/testutils/FuseOpenTest.cpp
Normal 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;
|
||||
}
|
||||
}
|
21
test/fuse/openFile/testutils/FuseOpenTest.h
Normal file
21
test/fuse/openFile/testutils/FuseOpenTest.h
Normal 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
|
61
test/fuse/read/FuseReadErrorTest.cpp
Normal file
61
test/fuse/read/FuseReadErrorTest.cpp
Normal 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;
|
||||
}
|
29
test/fuse/read/FuseReadFileDescriptorTest.cpp
Normal file
29
test/fuse/read/FuseReadFileDescriptorTest.cpp
Normal 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);
|
||||
}
|
40
test/fuse/read/FuseReadOverflowTest.cpp
Normal file
40
test/fuse/read/FuseReadOverflowTest.cpp
Normal 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);
|
||||
}
|
75
test/fuse/read/FuseReadReturnedDataTest.cpp
Normal file
75
test/fuse/read/FuseReadReturnedDataTest.cpp
Normal 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));
|
||||
}
|
26
test/fuse/read/testutils/FuseReadTest.cpp
Normal file
26
test/fuse/read/testutils/FuseReadTest.cpp
Normal 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;
|
||||
}
|
35
test/fuse/read/testutils/FuseReadTest.h
Normal file
35
test/fuse/read/testutils/FuseReadTest.h
Normal 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
Loading…
x
Reference in New Issue
Block a user