Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 36 additions & 15 deletions include/microStore/Adapters/PosixFileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <string>

namespace microStore { namespace Adapters {

Expand Down Expand Up @@ -198,7 +199,7 @@ class PosixFileSystem : public microStore::FileSystem {
class FileSystemImpl : public microStore::FileSystemImpl {

public:
FileSystemImpl(const char* basepath = "") : _basepath(basepath) {}
FileSystemImpl(const char* basepath = "") : _basepath(basepath ? basepath : "") {}
virtual ~FileSystemImpl() {}

public:
Expand All @@ -220,7 +221,7 @@ class PosixFileSystem : public microStore::FileSystem {
USTORE_LOG("[ustore] Initializing PosixFileSystem\n");
#if defined(ESP32)
// Initialize LittleFS for POSIX file access
if (!LittleFS.begin(true, _basepath)) {
if (!LittleFS.begin(true, _basepath.c_str())) {
USTORE_LOG("[ustore] Failed to initialize PosixFileSystem!\n");
return false;
}
Expand All @@ -247,7 +248,9 @@ class PosixFileSystem : public microStore::FileSystem {
return true;
}

virtual microStore::File open(const char* path, microStore::File::Mode mode, const bool create = false) override {
virtual microStore::File open(const char* path_in, microStore::File::Mode mode, const bool create = false) override {
const std::string full = resolve(path_in);
const char* path = full.c_str();
int flags;
switch (mode) {
// Read only. File must exist. ("r")
Expand Down Expand Up @@ -283,7 +286,9 @@ class PosixFileSystem : public microStore::FileSystem {
}


inline virtual bool exists(const char* path) override {
inline virtual bool exists(const char* path_in) override {
const std::string full = resolve(path_in);
const char* path = full.c_str();
int fd = ::open(path, O_RDONLY);
if (fd != -1) {
::close(fd);
Expand All @@ -292,15 +297,19 @@ class PosixFileSystem : public microStore::FileSystem {
return false;
}

inline virtual bool remove(const char* path) override {
inline virtual bool remove(const char* path_in) override {
const std::string full = resolve(path_in);
const char* path = full.c_str();
return (::unlink(path) == 0);
}

inline virtual bool rename(const char* from_path, const char* to_path) override {
return (::rename(from_path, to_path) == 0);
return (::rename(resolve(from_path).c_str(), resolve(to_path).c_str()) == 0);
}

inline virtual bool mkdir(const char* path) override {
inline virtual bool mkdir(const char* path_in) override {
const std::string full = resolve(path_in);
const char* path = full.c_str();
struct stat st = {0};
if (::stat(path, &st) == 0) {
if (S_ISDIR(st.st_mode)) {
Expand All @@ -315,27 +324,30 @@ class PosixFileSystem : public microStore::FileSystem {
}

inline virtual bool rmdir(const char* path) override {
if (::rmdir(path) == 0) {
return false;
}
return true;
return (::rmdir(resolve(path).c_str()) == 0);
}


inline virtual size_t size(const char* path) override {
inline virtual size_t size(const char* path_in) override {
const std::string full = resolve(path_in);
const char* path = full.c_str();
struct stat st;
if (stat(path, &st) != 0) {
return 0;
}
return st.st_size;
}

inline virtual bool isDirectory(const char* path) override {
inline virtual bool isDirectory(const char* path_in) override {
const std::string full = resolve(path_in);
const char* path = full.c_str();
struct stat st = {0};
return (::stat(path, &st) == 0 && S_ISDIR(st.st_mode));
}

virtual std::list<std::string> listDirectory(const char* path, Callbacks::DirectoryListing callback = nullptr) override {
virtual std::list<std::string> listDirectory(const char* path_in, Callbacks::DirectoryListing callback = nullptr) override {
const std::string full = resolve(path_in);
const char* path = full.c_str();
std::list<std::string> files;
DIR *dir = ::opendir(path);
if (dir == NULL) {
Expand Down Expand Up @@ -372,7 +384,16 @@ class PosixFileSystem : public microStore::FileSystem {
}

private:
const char* _basepath = "";
std::string resolve(const char* path) const {
if (path == nullptr || _basepath.empty() || path[0] == '/') {
return path ? path : "";
}
if (path[0] == '.' && path[1] == '/') {
path += 2;
}
return _basepath + "/" + path;
}
std::string _basepath;
};

};
Expand Down
5 changes: 1 addition & 4 deletions include/microStore/Adapters/StdioFileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,7 @@ class StdioFileSystem : public microStore::FileSystem {
}

inline virtual bool rmdir(const char* path) override {
if (::rmdir(path) == 0) {
return false;
}
return true;
return (::rmdir(path) == 0);
}


Expand Down
132 changes: 132 additions & 0 deletions test/test_posix_fs/test_posix_fs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Tests for the POSIX and stdio filesystem adapters against a real
* temporary directory.
*/

#include <unity.h>
#include <microStore/Adapters/PosixFileSystem.h>
#include <microStore/Adapters/StdioFileSystem.h>
#include <microStore/FileStore.h>

#include <cstdlib>
#include <string>
#include <sys/stat.h>
#include <unistd.h>

static std::string tmp;

static bool is_dir(const std::string& path) {
struct stat st;
return ::stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode);
}

/* ---- rmdir ---- */

static void check_rmdir(microStore::FileSystem fs) {
const std::string dir = tmp + "/sub";
TEST_ASSERT_TRUE(fs.mkdir(dir.c_str()));
TEST_ASSERT_TRUE(fs.rmdir(dir.c_str()));
TEST_ASSERT_FALSE(is_dir(dir));
TEST_ASSERT_FALSE(fs.rmdir(dir.c_str()));
}

void test_posix_rmdir_reports_result() {
check_rmdir(microStore::Adapters::PosixFileSystem());
}

void test_stdio_rmdir_reports_result() {
check_rmdir(microStore::Adapters::StdioFileSystem());
}

/* ---- Base path ---- */

static void write_file(microStore::FileSystem& fs, const char* path) {
microStore::File f = fs.open(path, microStore::File::ModeWrite, true);
TEST_ASSERT_TRUE((bool)f);
TEST_ASSERT_EQUAL(4, f.write("test", 4));
f.close();
}

void test_posix_relative_paths_resolve_against_basepath() {
microStore::Adapters::PosixFileSystem fs(tmp.c_str());
TEST_ASSERT_TRUE(fs.mkdir("./store/"));
TEST_ASSERT_TRUE(is_dir(tmp + "/store"));
TEST_ASSERT_TRUE(fs.isDirectory("store"));

write_file(fs, "./store/a");
TEST_ASSERT_EQUAL(0, ::access((tmp + "/store/a").c_str(), F_OK));
TEST_ASSERT_TRUE(fs.exists("store/a"));
TEST_ASSERT_EQUAL(4, fs.size("./store/a"));
TEST_ASSERT_EQUAL(1, fs.listDirectory("./store/").size());

TEST_ASSERT_TRUE(fs.rename("./store/a", "./store/b"));
TEST_ASSERT_EQUAL(0, ::access((tmp + "/store/b").c_str(), F_OK));
TEST_ASSERT_TRUE(fs.remove("./store/b"));
TEST_ASSERT_FALSE(fs.exists("./store/b"));
TEST_ASSERT_TRUE(fs.rmdir("./store/"));
TEST_ASSERT_FALSE(is_dir(tmp + "/store"));
}

void test_posix_absolute_paths_ignore_basepath() {
microStore::Adapters::PosixFileSystem fs("/nonexistent-base");
const std::string file = tmp + "/abs";
write_file(fs, file.c_str());
TEST_ASSERT_TRUE(fs.exists(file.c_str()));
}

void test_posix_basepath_is_copied() {
std::string* base = new std::string(tmp);
microStore::Adapters::PosixFileSystem fs(base->c_str());
base->assign(base->size(), 'x');
delete base;
write_file(fs, "./copied");
TEST_ASSERT_EQUAL(0, ::access((tmp + "/copied").c_str(), F_OK));
}

void test_posix_store_lives_under_basepath() {
microStore::Adapters::PosixFileSystem fs(tmp.c_str());
microStore::FileStore store;
TEST_ASSERT_TRUE(store.init(fs, "./kv/"));
TEST_ASSERT_TRUE(is_dir(tmp + "/kv"));
}

/* ---- Main ---- */

void setUp() {
char tmpl[] = "/tmp/microstore_fs_XXXXXX";
TEST_ASSERT_NOT_NULL(::mkdtemp(tmpl));
tmp = tmpl;
}

void tearDown() {
std::string cmd = "rm -rf '" + tmp + "'";
(void)std::system(cmd.c_str());
}

int runUnityTests(void) {
UNITY_BEGIN();
RUN_TEST(test_posix_rmdir_reports_result);
RUN_TEST(test_stdio_rmdir_reports_result);
RUN_TEST(test_posix_relative_paths_resolve_against_basepath);
RUN_TEST(test_posix_absolute_paths_ignore_basepath);
RUN_TEST(test_posix_basepath_is_copied);
RUN_TEST(test_posix_store_lives_under_basepath);
return UNITY_END();
}

// For native dev-platform or for some embedded frameworks
int main(void) {
return runUnityTests();
}

#ifdef ARDUINO
// For Arduino framework
void setup() {
// Wait ~2 seconds before the Unity test runner
// establishes connection with a board Serial interface
delay(2000);

runUnityTests();
}
void loop() {}
#endif