diff --git a/CMakeLists.txt b/CMakeLists.txt
index 682501b68bb24ecabe808f6efda5cf9b5e07cf4e..69cd73b4786ed6acd763f765988b692362e54982 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -235,9 +235,9 @@ if (BUILD_TESTING AND NOT MSVC)
     target_link_libraries(tests_connectionManager PRIVATE dhtnet fmt::fmt PkgConfig::Cppunit)
     add_test(NAME tests_connectionManager COMMAND tests_connectionManager)
 
-    #add_executable(tests_fileutils tests/testFileutils.cpp)
-    #target_link_libraries(tests_fileutils PRIVATE dhtnet fmt::fmt PkgConfig::Cppunit)
-    #add_test(NAME tests_fileutils COMMAND tests_fileutils)
+    add_executable(tests_fileutils tests/testFileutils.cpp)
+    target_link_libraries(tests_fileutils PRIVATE dhtnet fmt::fmt PkgConfig::Cppunit)
+    add_test(NAME tests_fileutils COMMAND tests_fileutils)
 
     #add_executable(tests_stringutils tests/testString_utils.cpp)
     #target_link_libraries(tests_stringutils PRIVATE dhtnet fmt::fmt PkgConfig::Cppunit)
diff --git a/include/certstore.h b/include/certstore.h
index f92c8a15fa0ab28d82709f44d79a2857a952aa83..1893ed6e8a400b2bc53457b685e1beffcfe577b1 100644
--- a/include/certstore.h
+++ b/include/certstore.h
@@ -24,6 +24,7 @@
 #include <set>
 #include <future>
 #include <mutex>
+#include <filesystem>
 
 namespace crypto = ::dht::crypto;
 
@@ -49,7 +50,7 @@ const char* statusToStr(TrustStatus s);
 class CertificateStore
 {
 public:
-    explicit CertificateStore(const std::string& path, std::shared_ptr<Logger> logger);
+    explicit CertificateStore(const std::filesystem::path& path, std::shared_ptr<Logger> logger);
 
     std::vector<std::string> getPinnedCertificates() const;
     /**
@@ -102,9 +103,9 @@ private:
     void pinRevocationList(const std::string& id, const dht::crypto::RevocationList& crl);
     std::shared_ptr<Logger> logger_;
 
-    const std::string certPath_;
-    const std::string crlPath_;
-    const std::string ocspPath_;
+    const std::filesystem::path certPath_;
+    const std::filesystem::path crlPath_;
+    const std::filesystem::path ocspPath_;
 
     mutable std::mutex lock_;
     std::map<std::string, std::shared_ptr<crypto::Certificate>> certs_;
diff --git a/include/connectionmanager.h b/include/connectionmanager.h
index b03c345fe8666c6e89da42050b52e96dce72bb7c..b42b6339f9512b2cda6bcdaf7530e2e96026cf48 100644
--- a/include/connectionmanager.h
+++ b/include/connectionmanager.h
@@ -292,7 +292,7 @@ struct ConnectionManager::Config
 
     std::shared_ptr<TurnCache> turnCache;
 
-    std::string cachePath {};
+    std::filesystem::path cachePath {};
 
     std::shared_ptr<asio::io_context> ioContext;
     std::shared_ptr<dht::DhtRunner> dht;
diff --git a/include/diffie-hellman.h b/include/diffie-hellman.h
index f6bd64d42ffd6ee21758848fa84c9618df4596af..92f29067f3b5c0313079057bcc9839d3c26e242b 100644
--- a/include/diffie-hellman.h
+++ b/include/diffie-hellman.h
@@ -22,6 +22,7 @@
 #include <memory>
 #include <cstdint>
 #include <string>
+#include <filesystem>
 
 namespace dhtnet {
 namespace tls {
@@ -57,7 +58,7 @@ public:
 
     static DhParams generate();
 
-    static DhParams loadDhParams(const std::string& path);
+    static DhParams loadDhParams(const std::filesystem::path& path);
 
 private:
     std::unique_ptr<gnutls_dh_params_int, decltype(gnutls_dh_params_deinit)*>
diff --git a/include/fileutils.h b/include/fileutils.h
index 7539e11c62c7c03e9341f66fdbe217b04562c449..8362cacbfd637935cd5e344f4f22b77423aa21eb 100644
--- a/include/fileutils.h
+++ b/include/fileutils.h
@@ -22,18 +22,7 @@
 #include <mutex>
 #include <cstdio>
 #include <ios>
-
-#ifndef _WIN32
-#include <sys/stat.h>               // mode_t
-#define DIR_SEPARATOR_STR     "/"   // Directory separator string
-#define DIR_SEPARATOR_CH      '/'   // Directory separator char
-#define DIR_SEPARATOR_STR_ESC "\\/" // Escaped directory separator string
-#else
-#define mode_t                unsigned
-#define DIR_SEPARATOR_STR     "\\"  // Directory separator string
-#define DIR_SEPARATOR_CH      '\\'  // Directory separator char
-#define DIR_SEPARATOR_STR_ESC "//*" // Escaped directory separator string
-#endif
+#include <filesystem>
 
 namespace dhtnet {
 namespace fileutils {
@@ -44,100 +33,70 @@ namespace fileutils {
  * @param dir last directory creation mode
  * @param parents default mode for all created directories except the last
  */
-bool check_dir(const char* path, mode_t dir = 0755, mode_t parents = 0755);
-/*std::string expand_path(const std::string& path);*/
-bool isDirectoryWritable(const std::string& directory);
-
-bool recursive_mkdir(const std::string& path, mode_t mode = 0755);
-
-bool isPathRelative(const std::string& path);
-/**
- * If path is contained in base, return the suffix, otherwise return the full path.
- * @param base must not finish with DIR_SEPARATOR_STR, can be empty
- * @param path the path
- */
-//std::string getCleanPath(const std::string& base, const std::string& path);
-/**
- * If path is relative, it is appended to base.
- */
-//std::string getFullPath(const std::string& base, const std::string& path);
+bool check_dir(const std::filesystem::path& path, mode_t dir = 0755, mode_t parents = 0755);
 
-bool isFile(const std::string& path, bool resolveSymlink = true);
-bool isDirectory(const std::string& path);
-bool isSymLink(const std::string& path);
-bool hasHardLink(const std::string& path);
+bool recursive_mkdir(const std::filesystem::path& path, mode_t mode = 0755);
 
-std::chrono::system_clock::time_point writeTime(const std::string& path);
-
-/*void createFileLink(const std::string& src, const std::string& dest, bool hard = false);
+inline bool isPathRelative(const std::filesystem::path& path) {
+    return path.is_relative();
+}
 
-std::string_view getFileExtension(std::string_view filename);*/
+bool isFile(const std::filesystem::path& path, bool resolveSymlink = true);
+bool isDirectory(const std::filesystem::path& path);
+bool isSymLink(const std::filesystem::path& path);
+bool hasHardLink(const std::filesystem::path& path);
 
 /**
  * Read content of the directory.
  * The result is a list of relative (to @param dir) paths of all entries
  * in the directory, without "." and "..".
  */
-std::vector<std::string> readDirectory(const std::string& dir);
+std::vector<std::string> readDirectory(const std::filesystem::path& dir);
 
 /**
  * Read the full content of a file at path.
  * If path is relative, it is appended to default_dir.
  */
-std::vector<uint8_t> loadFile(const std::string& path, const std::string& default_dir = {});
-std::string loadTextFile(const std::string& path, const std::string& default_dir = {});
+std::vector<uint8_t> loadFile(const std::filesystem::path& path);
 
-void saveFile(const std::string& path, const uint8_t* data, size_t data_size, mode_t mode = 0644);
+void saveFile(const std::filesystem::path& path, const uint8_t* data, size_t data_size, mode_t mode = 0644);
 inline void
-saveFile(const std::string& path, const std::vector<uint8_t>& data, mode_t mode = 0644)
+saveFile(const std::filesystem::path& path, const std::vector<uint8_t>& data, mode_t mode = 0644)
 {
     saveFile(path, data.data(), data.size(), mode);
 }
 
-/*std::vector<uint8_t> loadCacheFile(const std::string& path,
-                                   std::chrono::system_clock::duration maxAge);
-std::string loadCacheTextFile(const std::string& path, std::chrono::system_clock::duration maxAge);
-
-std::vector<uint8_t> readArchive(const std::string& path, const std::string& password = {});
-void writeArchive(const std::string& data,
-                  const std::string& path,
-                  const std::string& password = {});*/
-
-std::mutex& getFileLock(const std::string& path);
+std::mutex& getFileLock(const std::filesystem::path& path);
 
 /**
  * Remove a file with optional erasing of content.
  * Return the same value as std::remove().
  */
-int remove(const std::string& path, bool erase = false);
+int remove(const std::filesystem::path& path, bool erase = false);
 
 /**
  * Prune given directory's content and remove it, symlinks are not followed.
  * Return 0 if succeed, -1 if directory is not removed (content can be removed partially).
  */
-int removeAll(const std::string& path, bool erase = false);
+int removeAll(const std::filesystem::path& path, bool erase = false);
 
 /**
  * Wrappers for fstream opening that will convert paths to wstring
  * on windows
  */
 void openStream(std::ifstream& file,
-                const std::string& path,
+                const std::filesystem::path& path,
                 std::ios_base::openmode mode = std::ios_base::in);
 void openStream(std::ofstream& file,
-                const std::string& path,
+                const std::filesystem::path& path,
                 std::ios_base::openmode mode = std::ios_base::out);
-std::ifstream ifstream(const std::string& path, std::ios_base::openmode mode = std::ios_base::in);
-std::ofstream ofstream(const std::string& path, std::ios_base::openmode mode = std::ios_base::out);
-
-int64_t size(const std::string& path);
+std::ifstream ifstream(const std::filesystem::path& path, std::ios_base::openmode mode = std::ios_base::in);
+std::ofstream ofstream(const std::filesystem::path& path, std::ios_base::openmode mode = std::ios_base::out);
 
 /**
  * Windows compatibility wrapper for checking read-only attribute
  */
 int accessFile(const std::string& file, int mode);
 
-uint64_t lastWriteTime(const std::string& p);
-
 } // namespace fileutils
 } // namespace dhtnet
diff --git a/include/turn_cache.h b/include/turn_cache.h
index deeb9197a8da7aba92f75b29a2b08667d7b420b7..a971e7f3d6722e1f6957c903094aa585a80626e8 100644
--- a/include/turn_cache.h
+++ b/include/turn_cache.h
@@ -28,6 +28,7 @@
 #include <mutex>
 #include <optional>
 #include <string>
+#include <filesystem>
 
 namespace dht {
 namespace log {
@@ -65,7 +66,7 @@ public:
 
 private:
     std::string accountId_;
-    std::string cachePath_;
+    std::filesystem::path cachePath_;
     TurnTransportParams params_;
     std::atomic_bool enabled_ {false};
     /**
diff --git a/src/connectionmanager.cpp b/src/connectionmanager.cpp
index baab5af5f579805f52c4e76e7a19385d817c3733..55a6929ebe1f66e8e8e297c2a0f21ea4e9a8c233 100644
--- a/src/connectionmanager.cpp
+++ b/src/connectionmanager.cpp
@@ -1300,7 +1300,7 @@ const std::shared_future<tls::DhParams>
 ConnectionManager::Impl::dhParams() const
 {
     return dht::ThreadPool::computation().get<tls::DhParams>(
-        std::bind(tls::DhParams::loadDhParams, config_->cachePath + DIR_SEPARATOR_STR "dhParams"));
+        std::bind(tls::DhParams::loadDhParams, config_->cachePath / "dhParams"));
 }
 
 template<typename ID = dht::Value::Id>
@@ -1330,7 +1330,7 @@ loadIdList(const std::string& path)
 
 template<typename List = std::set<dht::Value::Id>>
 void
-saveIdList(const std::string& path, const List& ids)
+saveIdList(const std::filesystem::path& path, const List& ids)
 {
     std::ofstream file = fileutils::ofstream(path, std::ios::trunc | std::ios::binary);
     if (!file.is_open()) {
@@ -1345,7 +1345,7 @@ void
 ConnectionManager::Impl::loadTreatedMessages()
 {
     std::lock_guard<std::mutex> lock(messageMutex_);
-    auto path = config_->cachePath + DIR_SEPARATOR_STR "treatedMessages";
+    auto path = config_->cachePath / "treatedMessages";
     treatedMessages_ = loadIdList<std::string>(path);
     if (treatedMessages_.empty()) {
         auto messages = loadIdList(path);
@@ -1362,8 +1362,7 @@ ConnectionManager::Impl::saveTreatedMessages() const
             auto& this_ = *sthis;
             std::lock_guard<std::mutex> lock(this_.messageMutex_);
             fileutils::check_dir(this_.config_->cachePath.c_str());
-            saveIdList<decltype(this_.treatedMessages_)>(this_.config_->cachePath
-                                                             + DIR_SEPARATOR_STR "treatedMessages",
+            saveIdList<decltype(this_.treatedMessages_)>(this_.config_->cachePath / "treatedMessages",
                                                          this_.treatedMessages_);
         }
     });
diff --git a/src/fileutils.cpp b/src/fileutils.cpp
index c3aa05959a07b985bf615f1ef491edfb6c044c1c..6e60fd45b4e895c11d6cea449fb0d1f0c800f57b 100644
--- a/src/fileutils.cpp
+++ b/src/fileutils.cpp
@@ -15,6 +15,7 @@
  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
  */
 #include "fileutils.h"
+
 #include <opendht/crypto.h>
 
 #ifdef RING_UWP
@@ -32,29 +33,12 @@
 
 #include <sys/types.h>
 #include <sys/stat.h>
-
 #ifndef _MSC_VER
 #include <libgen.h>
 #endif
-
-#ifdef _MSC_VER
-#include "windirent.h"
-#else
-#include <dirent.h>
-#endif
-
+#include <fcntl.h>
 #include <signal.h>
 #include <unistd.h>
-#include <fcntl.h>
-#ifndef _WIN32
-#include <pwd.h>
-#else
-#include <shlobj.h>
-#define NAME_MAX 255
-#endif
-#if !defined __ANDROID__ && !defined _WIN32
-#include <wordexp.h>
-#endif
 
 #include <sstream>
 #include <fstream>
@@ -62,6 +46,7 @@
 #include <stdexcept>
 #include <limits>
 #include <array>
+#include <filesystem>
 
 #include <cstdlib>
 #include <cstring>
@@ -69,218 +54,67 @@
 #include <cstddef>
 #include <ciso646>
 
-extern "C" {
-#include <pj/ctype.h>
-#include <pjlib-util/md5.h>
-}
 
-#include <filesystem>
-
-#define PIDFILE     ".ring.pid"
 #define ERASE_BLOCK 4096
 
 namespace dhtnet {
 namespace fileutils {
 
-// returns true if directory exists
+// returns true if directory exists or was created
 bool
-check_dir(const char* path, [[maybe_unused]] mode_t dirmode, mode_t parentmode)
-{
-    DIR* dir = opendir(path);
-
-    if (!dir) { // doesn't exist
-        if (not recursive_mkdir(path, parentmode)) {
-            perror(path);
-            return false;
-        }
-#ifndef _WIN32
-        if (chmod(path, dirmode) < 0) {
-            //JAMI_ERR("fileutils::check_dir(): chmod() failed on '%s', %s", path, strerror(errno));
-            return false;
-        }
-#endif
-    } else
-        closedir(dir);
-    return true;
-}
-
-std::string
-expand_path(const std::string& path)
+check_dir(const std::filesystem::path& path, mode_t dirmode, mode_t parentmode)
 {
-#if defined __ANDROID__ || defined _MSC_VER || defined WIN32 || defined __APPLE__
-    //JAMI_ERR("Path expansion not implemented, returning original");
-    return path;
-#else
-
-    std::string result;
-
-    wordexp_t p;
-    int ret = wordexp(path.c_str(), &p, 0);
-
-    switch (ret) {
-    case WRDE_BADCHAR:
-        /*JAMI_ERR("Illegal occurrence of newline or one of |, &, ;, <, >, "
-                 "(, ), {, }.");*/
-        return result;
-    case WRDE_BADVAL:
-        //JAMI_ERR("An undefined shell variable was referenced");
-        return result;
-    case WRDE_CMDSUB:
-        //JAMI_ERR("Command substitution occurred");
-        return result;
-    case WRDE_SYNTAX:
-        //JAMI_ERR("Shell syntax error");
-        return result;
-    case WRDE_NOSPACE:
-        //JAMI_ERR("Out of memory.");
-        // This is the only error where we must call wordfree
-        break;
-    default:
-        if (p.we_wordc > 0)
-            result = std::string(p.we_wordv[0]);
-        break;
+    if (std::filesystem::exists(path))
+        return true;
+    if (path.has_parent_path())
+        check_dir(path.parent_path(), parentmode, parentmode);
+    if (std::filesystem::create_directory(path)) {
+        std::filesystem::permissions(path, (std::filesystem::perms)dirmode);
+        return true;
     }
-
-    wordfree(&p);
-
-    return result;
-#endif
+    return false;
 }
 
 std::mutex&
-getFileLock(const std::string& path)
+getFileLock(const std::filesystem::path& path)
 {
     static std::mutex fileLockLock {};
     static std::map<std::string, std::mutex> fileLocks {};
 
     std::lock_guard<std::mutex> l(fileLockLock);
-    return fileLocks[path];
+    return fileLocks[path.string()];
 }
 
 bool
-isFile(const std::string& path, bool resolveSymlink)
+isFile(const std::filesystem::path& path, bool resolveSymlink)
 {
-    if (path.empty())
-        return false;
-#ifdef _WIN32
-    if (resolveSymlink) {
-        struct _stat64i32 s;
-        if (_wstat(dhtnet::to_wstring(path).c_str(), &s) == 0)
-            return S_ISREG(s.st_mode);
-    } else {
-        DWORD attr = GetFileAttributes(dhtnet::to_wstring(path).c_str());
-        if ((attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_DIRECTORY)
-            && !(attr & FILE_ATTRIBUTE_REPARSE_POINT))
-            return true;
-    }
-#else
-    if (resolveSymlink) {
-        struct stat s;
-        if (stat(path.c_str(), &s) == 0)
-            return S_ISREG(s.st_mode);
-    } else {
-        struct stat s;
-        if (lstat(path.c_str(), &s) == 0)
-            return S_ISREG(s.st_mode);
-    }
-#endif
-
-    return false;
-}
-
-bool
-isDirectory(const std::string& path)
-{
-    struct stat s;
-    if (stat(path.c_str(), &s) == 0)
-        return s.st_mode & S_IFDIR;
-    return false;
+    auto status = resolveSymlink ? std::filesystem::status(path) : std::filesystem::symlink_status(path);
+    return std::filesystem::is_regular_file(status);
 }
 
 bool
-isDirectoryWritable(const std::string& directory)
+isDirectory(const std::filesystem::path& path)
 {
-    return accessFile(directory, W_OK) == 0;
+    return std::filesystem::is_directory(path);
 }
 
 bool
-hasHardLink(const std::string& path)
+hasHardLink(const std::filesystem::path& path)
 {
-#ifndef _WIN32
-    struct stat s;
-    if (lstat(path.c_str(), &s) == 0)
-        return s.st_nlink > 1;
-#endif
-    return false;
+    return std::filesystem::hard_link_count(path) > 1;
 }
 
 bool
-isSymLink(const std::string& path)
+isSymLink(const std::filesystem::path& path)
 {
-#ifndef _WIN32
-    struct stat s;
-    if (lstat(path.c_str(), &s) == 0)
-        return S_ISLNK(s.st_mode);
-#elif !defined(_MSC_VER)
-    DWORD attr = GetFileAttributes(dhtnet::to_wstring(path).c_str());
-    if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
-        return true;
-#endif
-    return false;
+    return std::filesystem::is_symlink(path);
 }
 
-std::chrono::system_clock::time_point
-writeTime(const std::string& path)
+template <typename TP>
+std::chrono::system_clock::time_point to_sysclock(TP tp)
 {
-#ifndef _WIN32
-    struct stat s;
-    auto ret = stat(path.c_str(), &s);
-    if (ret)
-        throw std::runtime_error("Can't check write time for: " + path);
-    return std::chrono::system_clock::from_time_t(s.st_mtime);
-#else
-#if RING_UWP
-    _CREATEFILE2_EXTENDED_PARAMETERS ext_params = {0};
-    ext_params.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
-    ext_params.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
-    ext_params.dwFileFlags = FILE_FLAG_NO_BUFFERING;
-    ext_params.dwSecurityQosFlags = SECURITY_ANONYMOUS;
-    ext_params.lpSecurityAttributes = nullptr;
-    ext_params.hTemplateFile = nullptr;
-    HANDLE h = CreateFile2(dhtnet::to_wstring(path).c_str(),
-                           GENERIC_READ,
-                           FILE_SHARE_READ,
-                           OPEN_EXISTING,
-                           &ext_params);
-#elif _WIN32
-    HANDLE h = CreateFileW(dhtnet::to_wstring(path).c_str(),
-                           GENERIC_READ,
-                           FILE_SHARE_READ,
-                           nullptr,
-                           OPEN_EXISTING,
-                           FILE_ATTRIBUTE_NORMAL,
-                           nullptr);
-#endif
-    if (h == INVALID_HANDLE_VALUE)
-        throw std::runtime_error("Can't open: " + path);
-    FILETIME lastWriteTime;
-    if (!GetFileTime(h, nullptr, nullptr, &lastWriteTime))
-        throw std::runtime_error("Can't check write time for: " + path);
-    CloseHandle(h);
-    SYSTEMTIME sTime;
-    if (!FileTimeToSystemTime(&lastWriteTime, &sTime))
-        throw std::runtime_error("Can't check write time for: " + path);
-    struct tm tm
-    {};
-    tm.tm_year = sTime.wYear - 1900;
-    tm.tm_mon = sTime.wMonth - 1;
-    tm.tm_mday = sTime.wDay;
-    tm.tm_hour = sTime.wHour;
-    tm.tm_min = sTime.wMinute;
-    tm.tm_sec = sTime.wSecond;
-    tm.tm_isdst = -1;
-    return std::chrono::system_clock::from_time_t(mktime(&tm));
-#endif
+    using namespace std::chrono;
+    return time_point_cast<system_clock::duration>(tp - TP::clock::now() + system_clock::now());
 }
 
 bool
@@ -314,85 +148,26 @@ createFileLink(const std::string& linkFile, const std::string& target, bool hard
         createSymlink(linkFile, target);
 }
 
-std::string_view
-getFileExtension(std::string_view filename)
-{
-    std::string_view result;
-    auto sep = filename.find_last_of('.');
-    if (sep != std::string_view::npos && sep != filename.size() - 1)
-        result = filename.substr(sep + 1);
-    if (result.size() >= 8)
-        return {};
-    return result;
-}
-
-bool
-isPathRelative(const std::string& path)
-{
-#ifndef _WIN32
-    return not path.empty() and not(path[0] == '/');
-#else
-    return not path.empty() and path.find(":") == std::string::npos;
-#endif
-}
-
-std::string
-getCleanPath(const std::string& base, const std::string& path)
-{
-    if (base.empty() or path.size() < base.size())
-        return path;
-    auto base_sep = base + DIR_SEPARATOR_STR;
-    if (path.compare(0, base_sep.size(), base_sep) == 0)
-        return path.substr(base_sep.size());
-    else
-        return path;
-}
-
-std::string
-getFullPath(const std::string& base, const std::string& path)
-{
-    bool isRelative {not base.empty() and isPathRelative(path)};
-    return isRelative ? base + DIR_SEPARATOR_STR + path : path;
-}
-
 std::vector<uint8_t>
-loadFile(const std::string& path, const std::string& default_dir)
+loadFile(const std::filesystem::path& path)
 {
     std::vector<uint8_t> buffer;
-    std::ifstream file = ifstream(getFullPath(default_dir, path), std::ios::binary);
-    if (!file)
-        throw std::runtime_error("Can't read file: " + path);
-    file.seekg(0, std::ios::end);
-    auto size = file.tellg();
-    if (size > std::numeric_limits<unsigned>::max())
-        throw std::runtime_error("File is too big: " + path);
-    buffer.resize(size);
-    file.seekg(0, std::ios::beg);
-    if (!file.read((char*) buffer.data(), size))
-        throw std::runtime_error("Can't load file: " + path);
-    return buffer;
-}
-
-std::string
-loadTextFile(const std::string& path, const std::string& default_dir)
-{
-    std::string buffer;
-    std::ifstream file = ifstream(getFullPath(default_dir, path));
+    std::ifstream file = ifstream(path, std::ios::binary);
     if (!file)
-        throw std::runtime_error("Can't read file: " + path);
+        throw std::runtime_error("Can't read file: " + path.string());
     file.seekg(0, std::ios::end);
     auto size = file.tellg();
     if (size > std::numeric_limits<unsigned>::max())
-        throw std::runtime_error("File is too big: " + path);
+        throw std::runtime_error("File is too big: " + path.string());
     buffer.resize(size);
     file.seekg(0, std::ios::beg);
     if (!file.read((char*) buffer.data(), size))
-        throw std::runtime_error("Can't load file: " + path);
+        throw std::runtime_error("Can't load file: " + path.string());
     return buffer;
 }
 
 void
-saveFile(const std::string& path, const uint8_t* data, size_t data_size, [[maybe_unused]] mode_t mode)
+saveFile(const std::filesystem::path& path, const uint8_t* data, size_t data_size, mode_t mode)
 {
     std::ofstream file = fileutils::ofstream(path, std::ios::trunc | std::ios::binary);
     if (!file.is_open()) {
@@ -400,188 +175,32 @@ saveFile(const std::string& path, const uint8_t* data, size_t data_size, [[maybe
         return;
     }
     file.write((char*) data, data_size);
-#ifndef _WIN32
-    if (chmod(path.c_str(), mode) < 0)
-        /*JAMI_WARN("fileutils::saveFile(): chmod() failed on '%s', %s",
-                  path.c_str(),
-                  strerror(errno))*/;
-#endif
-}
-
-std::vector<uint8_t>
-loadCacheFile(const std::string& path, std::chrono::system_clock::duration maxAge)
-{
-    // writeTime throws exception if file doesn't exist
-    auto duration = std::chrono::system_clock::now() - writeTime(path);
-    if (duration > maxAge)
-        throw std::runtime_error("file too old");
-
-    //JAMI_DBG("Loading cache file '%.*s'", (int) path.size(), path.c_str());
-    return loadFile(path);
-}
-
-std::string
-loadCacheTextFile(const std::string& path, std::chrono::system_clock::duration maxAge)
-{
-    // writeTime throws exception if file doesn't exist
-    auto duration = std::chrono::system_clock::now() - writeTime(path);
-    if (duration > maxAge)
-        throw std::runtime_error("file too old");
-
-    //JAMI_DBG("Loading cache file '%.*s'", (int) path.size(), path.c_str());
-    return loadTextFile(path);
-}
-
-static size_t
-dirent_buf_size([[maybe_unused]] DIR* dirp)
-{
-    long name_max;
-#if defined(HAVE_FPATHCONF) && defined(HAVE_DIRFD) && defined(_PC_NAME_MAX)
-    name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
-    if (name_max == -1)
-#if defined(NAME_MAX)
-        name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
-#else
-        return (size_t) (-1);
-#endif
-#else
-#if defined(NAME_MAX)
-    name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
-#else
-#error "buffer size for readdir_r cannot be determined"
-#endif
-#endif
-    size_t name_end = (size_t) offsetof(struct dirent, d_name) + name_max + 1;
-    return name_end > sizeof(struct dirent) ? name_end : sizeof(struct dirent);
+    file.close();
+    std::filesystem::permissions(path, (std::filesystem::perms)mode);
 }
 
 std::vector<std::string>
-readDirectory(const std::string& dir)
+readDirectory(const std::filesystem::path& dir)
 {
-    DIR* dp = opendir(dir.c_str());
-    if (!dp)
-        return {};
-
-    size_t size = dirent_buf_size(dp);
-    if (size == (size_t) (-1))
-        return {};
-    std::vector<uint8_t> buf(size);
-    dirent* entry;
-
     std::vector<std::string> files;
-#ifndef _WIN32
-    while (!readdir_r(dp, reinterpret_cast<dirent*>(buf.data()), &entry) && entry) {
-#else
-    while ((entry = readdir(dp)) != nullptr) {
-#endif
-        std::string fname {entry->d_name};
+    std::error_code ec;
+    for (const auto& entry : std::filesystem::directory_iterator(dir, ec)) {
+        std::string fname {entry.path().filename().string()};
         if (fname == "." || fname == "..")
             continue;
         files.emplace_back(std::move(fname));
     }
-    closedir(dp);
     return files;
-} // namespace fileutils
-
-/*
-std::vector<uint8_t>
-readArchive(const std::string& path, const std::string& pwd)
-{
-    JAMI_DBG("Reading archive from %s", path.c_str());
-
-    auto isUnencryptedGzip = [](const std::vector<uint8_t>& data) {
-        // NOTE: some webserver modify gzip files and this can end with a gunzip in a gunzip
-        // file. So, to make the readArchive more robust, we can support this case by detecting
-        // gzip header via 1f8b 08
-        // We don't need to support more than 2 level, else somebody may be able to send
-        // gunzip in loops and abuse.
-        return data.size() > 3 && data[0] == 0x1f && data[1] == 0x8b && data[2] == 0x08;
-    };
-
-    auto decompress = [](std::vector<uint8_t>& data) {
-        try {
-            data = archiver::decompress(data);
-        } catch (const std::exception& e) {
-            JAMI_ERR("Error decrypting archive: %s", e.what());
-            throw e;
-        }
-    };
-
-    std::vector<uint8_t> data;
-    // Read file
-    try {
-        data = loadFile(path);
-    } catch (const std::exception& e) {
-        JAMI_ERR("Error loading archive: %s", e.what());
-        throw e;
-    }
-
-    if (isUnencryptedGzip(data)) {
-        if (!pwd.empty())
-            JAMI_WARN() << "A gunzip in a gunzip is detected. A webserver may have a bad config";
-
-        decompress(data);
-    }
-
-    if (!pwd.empty()) {
-        // Decrypt
-        try {
-            data = dht::crypto::aesDecrypt(data, pwd);
-        } catch (const std::exception& e) {
-            JAMI_ERR("Error decrypting archive: %s", e.what());
-            throw e;
-        }
-        decompress(data);
-    } else if (isUnencryptedGzip(data)) {
-        JAMI_WARN() << "A gunzip in a gunzip is detected. A webserver may have a bad config";
-        decompress(data);
-    }
-    return data;
 }
 
-void
-writeArchive(const std::string& archive_str, const std::string& path, const std::string& password)
-{
-    JAMI_DBG("Writing archive to %s", path.c_str());
-
-    if (not password.empty()) {
-        // Encrypt using provided password
-        std::vector<uint8_t> data = dht::crypto::aesEncrypt(archiver::compress(archive_str),
-                                                            password);
-        // Write
-        try {
-            saveFile(path, data);
-        } catch (const std::runtime_error& ex) {
-            JAMI_ERR("Export failed: %s", ex.what());
-            return;
-        }
-    } else {
-        JAMI_WARN("Unsecured archiving (no password)");
-        archiver::compressGzip(archive_str, path);
-    }
-}*/
-
 bool
-recursive_mkdir(const std::string& path, mode_t mode)
+recursive_mkdir(const std::filesystem::path& path, mode_t mode)
 {
-#ifndef _WIN32
-    if (mkdir(path.data(), mode) != 0) {
-#else
-    if (_wmkdir(dhtnet::to_wstring(path.data()).c_str()) != 0) {
-#endif
-        if (errno == ENOENT) {
-            recursive_mkdir(path.substr(0, path.find_last_of(DIR_SEPARATOR_CH)), mode);
-#ifndef _WIN32
-            if (mkdir(path.data(), mode) != 0) {
-#else
-            if (_wmkdir(dhtnet::to_wstring(path.data()).c_str()) != 0) {
-#endif
-                //JAMI_ERR("Could not create directory.");
-                return false;
-            }
-        }
-    } // namespace dhtnet
-    return true;
+    std::error_code ec;
+    std::filesystem::create_directories(path, ec);
+    if (!ec)
+        std::filesystem::permissions(path, (std::filesystem::perms)mode, ec);
+    return !ec;
 }
 
 #ifdef _WIN32
@@ -700,7 +319,7 @@ eraseFile(const std::string& path, bool dosync)
 }
 
 int
-remove(const std::string& path, bool erase)
+remove(const std::filesystem::path& path, bool erase)
 {
     if (erase and isFile(path, false) and !hasHardLink(path))
         eraseFile(path, true);
@@ -715,22 +334,25 @@ remove(const std::string& path, bool erase)
 }
 
 int
-removeAll(const std::string& path, bool erase)
+removeAll(const std::filesystem::path& path, bool erase)
 {
     if (path.empty())
         return -1;
-    if (isDirectory(path) and !isSymLink(path)) {
-        auto dir = path;
-        if (dir.back() != DIR_SEPARATOR_CH)
-            dir += DIR_SEPARATOR_CH;
-        for (auto& entry : fileutils::readDirectory(dir))
-            removeAll(dir + entry, erase);
+
+    auto status = std::filesystem::status(path);
+    if (std::filesystem::is_directory(status) and not std::filesystem::is_symlink(status)) {
+        for (const auto& entry: std::filesystem::directory_iterator(path)) {
+            auto fname = entry.path().filename().string();
+            if (fname == "." || fname == "..")
+                continue;
+            removeAll(entry.path(), erase);
+        }
     }
     return remove(path, erase);
 }
 
 void
-openStream(std::ifstream& file, const std::string& path, std::ios_base::openmode mode)
+openStream(std::ifstream& file, const std::filesystem::path& path, std::ios_base::openmode mode)
 {
 #ifdef _WIN32
     file.open(dhtnet::to_wstring(path), mode);
@@ -740,7 +362,7 @@ openStream(std::ifstream& file, const std::string& path, std::ios_base::openmode
 }
 
 void
-openStream(std::ofstream& file, const std::string& path, std::ios_base::openmode mode)
+openStream(std::ofstream& file, const std::filesystem::path& path, std::ios_base::openmode mode)
 {
 #ifdef _WIN32
     file.open(dhtnet::to_wstring(path), mode);
@@ -750,7 +372,7 @@ openStream(std::ofstream& file, const std::string& path, std::ios_base::openmode
 }
 
 std::ifstream
-ifstream(const std::string& path, std::ios_base::openmode mode)
+ifstream(const std::filesystem::path& path, std::ios_base::openmode mode)
 {
 #ifdef _WIN32
     return std::ifstream(dhtnet::to_wstring(path), mode);
@@ -760,7 +382,7 @@ ifstream(const std::string& path, std::ios_base::openmode mode)
 }
 
 std::ofstream
-ofstream(const std::string& path, std::ios_base::openmode mode)
+ofstream(const std::filesystem::path& path, std::ios_base::openmode mode)
 {
 #ifdef _WIN32
     return std::ofstream(dhtnet::to_wstring(path), mode);
@@ -769,23 +391,8 @@ ofstream(const std::string& path, std::ios_base::openmode mode)
 #endif
 }
 
-int64_t
-size(const std::string& path)
-{
-    int64_t size = 0;
-    try {
-        std::ifstream file;
-        openStream(file, path, std::ios::binary | std::ios::in);
-        file.seekg(0, std::ios_base::end);
-        size = file.tellg();
-        file.close();
-    } catch (...) {
-    }
-    return size;
-}
-
 int
-accessFile(const std::string& file, int mode)
+accessFile(const std::filesystem::path& file, int mode)
 {
 #ifdef _WIN32
     return _waccess(dhtnet::to_wstring(file).c_str(), mode);
@@ -794,20 +401,5 @@ accessFile(const std::string& file, int mode)
 #endif
 }
 
-uint64_t
-lastWriteTime(const std::string& p)
-{
-#if USE_STD_FILESYSTEM
-    return std::chrono::duration_cast<std::chrono::milliseconds>(
-               std::filesystem::last_write_time(std::filesystem::path(p)).time_since_epoch())
-        .count();
-#else
-    struct stat result;
-    if (stat(p.c_str(), &result) == 0)
-        return result.st_mtime;
-    return 0;
-#endif
-}
-
 } // namespace fileutils
 } // namespace dhtnet
diff --git a/src/security/certstore.cpp b/src/security/certstore.cpp
index 9b6bb9698be903086d1f4a3a7f1b1d7027c5e927..aa3e14cb35dcb1b818c88c09cd1fffdad5bd108e 100644
--- a/src/security/certstore.cpp
+++ b/src/security/certstore.cpp
@@ -24,6 +24,8 @@
 
 #include <gnutls/ocsp.h>
 
+#include <fmt/std.h>
+
 #include <thread>
 #include <sstream>
 #include <fmt/format.h>
@@ -31,15 +33,15 @@
 namespace dhtnet {
 namespace tls {
 
-CertificateStore::CertificateStore(const std::string& path, std::shared_ptr<Logger> logger)
+CertificateStore::CertificateStore(const std::filesystem::path& path, std::shared_ptr<Logger> logger)
     : logger_(std::move(logger))
-    , certPath_(fmt::format("{}/certificates", path))
-    , crlPath_(fmt::format("{}/crls", path))
-    , ocspPath_(fmt::format("{}/oscp", path))
+    , certPath_(path / "certificates")
+    , crlPath_(path /"crls")
+    , ocspPath_(path /"oscp")
 {
-    fileutils::check_dir(certPath_.c_str());
-    fileutils::check_dir(crlPath_.c_str());
-    fileutils::check_dir(ocspPath_.c_str());
+    fileutils::check_dir(certPath_);
+    fileutils::check_dir(crlPath_);
+    fileutils::check_dir(ocspPath_);
     loadLocalCertificates();
 }
 
@@ -47,16 +49,20 @@ unsigned
 CertificateStore::loadLocalCertificates()
 {
     std::lock_guard<std::mutex> l(lock_);
+    if (logger_)
+        logger_->debug("CertificateStore: loading certificates from {}", certPath_);
 
-    auto dir_content = fileutils::readDirectory(certPath_);
     unsigned n = 0;
-    for (const auto& f : dir_content) {
+    std::error_code ec;
+    for (const auto& crtPath : std::filesystem::directory_iterator(certPath_, ec)) {
+        const auto& path = crtPath.path();
+        auto fileName = path.filename().string();
         try {
             auto crt = std::make_shared<crypto::Certificate>(
-                fileutils::loadFile(certPath_ + DIR_SEPARATOR_CH + f));
+                fileutils::loadFile(crtPath));
             auto id = crt->getId().toString();
             auto longId = crt->getLongId().toString();
-            if (id != f && longId != f)
+            if (id != fileName && longId != fileName)
                 throw std::logic_error("Certificate id mismatch");
             while (crt) {
                 id = crt->getId().toString();
@@ -69,8 +75,8 @@ CertificateStore::loadLocalCertificates()
             }
         } catch (const std::exception& e) {
             if (logger_)
-                logger_->warn("Remove cert. {}", e.what());
-            remove(fmt::format("{}/{}", certPath_, f).c_str());
+                logger_->warn("loadLocalCertificates: error loading {}: {}", path, e.what());
+            remove(path);
         }
     }
     if (logger_)
@@ -81,21 +87,23 @@ CertificateStore::loadLocalCertificates()
 void
 CertificateStore::loadRevocations(crypto::Certificate& crt) const
 {
-    auto dir = fmt::format("{:s}/{:s}", crlPath_, crt.getId().toString());
-    for (const auto& crl : fileutils::readDirectory(dir)) {
+    std::error_code ec;
+    auto dir = crlPath_ / crt.getId().toString();
+    for (const auto& crl : std::filesystem::directory_iterator(dir, ec)) {
         try {
             crt.addRevocationList(std::make_shared<crypto::RevocationList>(
-                fileutils::loadFile(fmt::format("{}/{}", dir, crl))));
+                fileutils::loadFile(crl)));
         } catch (const std::exception& e) {
             if (logger_)
                 logger_->warn("Can't load revocation list: %s", e.what());
         }
     }
-    auto ocsp_dir = ocspPath_ + DIR_SEPARATOR_CH + crt.getId().toString();
-    for (const auto& ocsp : fileutils::readDirectory(ocsp_dir)) {
+
+    auto ocsp_dir = ocspPath_ / crt.getId().toString();
+    for (const auto& ocsp_filepath : std::filesystem::directory_iterator(ocsp_dir, ec)) {
         try {
-            auto ocsp_filepath = fmt::format("{}/{}", ocsp_dir, ocsp);
-            if (logger_) logger_->debug("Found {:s}", ocsp_filepath);
+            auto ocsp = ocsp_filepath.path().filename().string();
+            if (logger_) logger_->debug("Found {}", ocsp_filepath.path());
             auto serial = crt.getSerialNumber();
             if (dht::toHex(serial.data(), serial.size()) != ocsp)
                 continue;
@@ -237,13 +245,12 @@ CertificateStore::findIssuer(const std::shared_ptr<crypto::Certificate>& crt) co
 }
 
 static std::vector<crypto::Certificate>
-readCertificates(const std::string& path, const std::string& crl_path)
+readCertificates(const std::filesystem::path& path, const std::string& crl_path)
 {
     std::vector<crypto::Certificate> ret;
-    if (fileutils::isDirectory(path)) {
-        auto files = fileutils::readDirectory(path);
-        for (const auto& file : files) {
-            auto certs = readCertificates(fmt::format("{}/{}", path, file), crl_path);
+    if (std::filesystem::is_directory(path)) {
+        for (const auto& file : std::filesystem::directory_iterator(path)) {
+            auto certs = readCertificates(file, crl_path);
             ret.insert(std::end(ret),
                        std::make_move_iterator(std::begin(certs)),
                        std::make_move_iterator(std::end(certs)));
@@ -364,7 +371,7 @@ CertificateStore::pinCertificate(const std::shared_ptr<crypto::Certificate>& cer
         }
         if (local) {
             if (sig)
-                fileutils::saveFile(certPath_ + DIR_SEPARATOR_CH + ids.front(), cert->getPacked());
+                fileutils::saveFile(certPath_ / ids.front(), cert->getPacked());
         }
     }
     //for (const auto& id : ids)
@@ -378,7 +385,7 @@ CertificateStore::unpinCertificate(const std::string& id)
     std::lock_guard<std::mutex> l(lock_);
 
     certs_.erase(id);
-    return remove((certPath_ + DIR_SEPARATOR_CH + id).c_str()) == 0;
+    return remove(certPath_ / id);
 }
 
 bool
@@ -430,9 +437,8 @@ CertificateStore::pinRevocationList(const std::string& id,
 void
 CertificateStore::pinRevocationList(const std::string& id, const dht::crypto::RevocationList& crl)
 {
-    fileutils::check_dir((crlPath_ + DIR_SEPARATOR_CH + id).c_str());
-    fileutils::saveFile(crlPath_ + DIR_SEPARATOR_CH + id + DIR_SEPARATOR_CH
-                            + dht::toHex(crl.getNumber()),
+    fileutils::check_dir(crlPath_ / id);
+    fileutils::saveFile(crlPath_ / id / dht::toHex(crl.getNumber()),
                         crl.getPacked());
 }
 
@@ -450,7 +456,7 @@ CertificateStore::pinOcspResponse(const dht::crypto::Certificate& cert)
     auto id = cert.getId().toString();
     auto serial = cert.getSerialNumber();
     auto serialhex = dht::toHex(serial);
-    auto dir = ocspPath_ + DIR_SEPARATOR_CH + id;
+    auto dir = ocspPath_ / id;
 
     if (auto localCert = getCertificate(id)) {
         // Update certificate in the local store if relevant
@@ -461,7 +467,7 @@ CertificateStore::pinOcspResponse(const dht::crypto::Certificate& cert)
     }
 
     dht::ThreadPool::io().run([l=logger_,
-                               path = dir + DIR_SEPARATOR_CH + serialhex,
+                               path = dir / serialhex,
                                dir = std::move(dir),
                                id = std::move(id),
                                serialhex = std::move(serialhex),
diff --git a/src/security/diffie-hellman.cpp b/src/security/diffie-hellman.cpp
index 78f901fbb2afe8f3d5e342e140095aeb3d9c912a..216e23380d6d5910cad2690527088f4fe2de052b 100644
--- a/src/security/diffie-hellman.cpp
+++ b/src/security/diffie-hellman.cpp
@@ -19,6 +19,7 @@
 
 #include <chrono>
 #include <ciso646>
+#include <filesystem>
 
 namespace dhtnet {
 namespace tls {
@@ -103,12 +104,13 @@ DhParams::generate()
 }
 
 DhParams
-DhParams::loadDhParams(const std::string& path)
+DhParams::loadDhParams(const std::filesystem::path& path)
 {
     std::lock_guard<std::mutex> l(fileutils::getFileLock(path));
     try {
         // writeTime throw exception if file doesn't exist
-        auto duration = std::chrono::system_clock::now() - fileutils::writeTime(path);
+        auto writeTime = std::filesystem::last_write_time(path);
+        auto duration = decltype(writeTime)::clock::now() - writeTime;
         if (duration >= std::chrono::hours(24 * 3)) // file is valid only 3 days
             throw std::runtime_error("file too old");
 
diff --git a/src/turn/turn_cache.cpp b/src/turn/turn_cache.cpp
index f351347e1a97c0c219c729fb1a214739290a6759..65862cbf4da58d51be9c3da00cd0f1063b74aa4c 100644
--- a/src/turn/turn_cache.cpp
+++ b/src/turn/turn_cache.cpp
@@ -127,15 +127,15 @@ TurnCache::refresh(const asio::error_code& ec)
         return;
     }
     // Else cache resolution result
-    fileutils::recursive_mkdir(cachePath_ + DIR_SEPARATOR_STR + "domains", 0700);
-    auto pathV4 = cachePath_ + DIR_SEPARATOR_STR + "domains" + DIR_SEPARATOR_STR + "v4." + server;
+    fileutils::recursive_mkdir(cachePath_ / "domains", 0700);
+    auto pathV4 = cachePath_ / "domains" / ("v4." + server);
     IpAddr testV4, testV6;
     if (auto turnV4File = std::ifstream(pathV4)) {
         std::string content((std::istreambuf_iterator<char>(turnV4File)),
                             std::istreambuf_iterator<char>());
         testV4 = IpAddr(content, AF_INET);
     }
-    auto pathV6 = cachePath_ + DIR_SEPARATOR_STR + "domains" + DIR_SEPARATOR_STR + "v6." + server;
+    auto pathV6 = cachePath_ / "domains" / ("v6." + server);
     if (auto turnV6File = std::ifstream(pathV6)) {
         std::string content((std::istreambuf_iterator<char>(turnV6File)),
                             std::istreambuf_iterator<char>());
diff --git a/tests/testFileutils.cpp b/tests/testFileutils.cpp
index fa353a6ad2bb619aa98abd8e51638a89bd6caa49..9bc3367645c0e5f3da8e50480a1ab6d36742606c 100644
--- a/tests/testFileutils.cpp
+++ b/tests/testFileutils.cpp
@@ -40,22 +40,20 @@ private:
     void testPath();
     void testReadDirectory();
     void testLoadFile();
-    void testIsDirectoryWritable();
 
     CPPUNIT_TEST_SUITE(FileutilsTest);
     CPPUNIT_TEST(testCheckDir);
     CPPUNIT_TEST(testPath);
     CPPUNIT_TEST(testReadDirectory);
     CPPUNIT_TEST(testLoadFile);
-    CPPUNIT_TEST(testIsDirectoryWritable);
     CPPUNIT_TEST_SUITE_END();
 
     static constexpr auto tmpFileName = "temp_file";
 
-    std::string TEST_PATH;
-    std::string NON_EXISTANT_PATH_BASE;
-    std::string NON_EXISTANT_PATH;
-    std::string EXISTANT_FILE;
+    std::filesystem::path TEST_PATH;
+    std::filesystem::path NON_EXISTANT_PATH_BASE;
+    std::filesystem::path NON_EXISTANT_PATH;
+    std::filesystem::path EXISTANT_FILE;
 };
 
 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(FileutilsTest, FileutilsTest::name());
@@ -70,9 +68,9 @@ FileutilsTest::setUp()
     CPPUNIT_ASSERT(directory);
 
     TEST_PATH = directory;
-    EXISTANT_FILE = TEST_PATH + DIR_SEPARATOR_STR + tmpFileName;
-    NON_EXISTANT_PATH_BASE = TEST_PATH + DIR_SEPARATOR_STR + "not_existing_path";
-    NON_EXISTANT_PATH = NON_EXISTANT_PATH_BASE + DIR_SEPARATOR_STR + "test";
+    EXISTANT_FILE = TEST_PATH / tmpFileName;
+    NON_EXISTANT_PATH_BASE = TEST_PATH / "not_existing_path";
+    NON_EXISTANT_PATH = NON_EXISTANT_PATH_BASE / "test";
 
     auto* fd = fopen(EXISTANT_FILE.c_str(), "w");
     fwrite("RING", 1, 4, fd);
@@ -90,11 +88,11 @@ void
 FileutilsTest::testCheckDir()
 {
     // check existed directory
-    CPPUNIT_ASSERT(check_dir(TEST_PATH.c_str()));
-    CPPUNIT_ASSERT(isDirectory(TEST_PATH.c_str()));
+    CPPUNIT_ASSERT(check_dir(TEST_PATH));
+    CPPUNIT_ASSERT(isDirectory(TEST_PATH));
     // check non-existent directory
     CPPUNIT_ASSERT(!isDirectory(NON_EXISTANT_PATH));
-    CPPUNIT_ASSERT(check_dir(NON_EXISTANT_PATH.c_str()));
+    CPPUNIT_ASSERT(check_dir(NON_EXISTANT_PATH));
     CPPUNIT_ASSERT(isDirectory(NON_EXISTANT_PATH));
     CPPUNIT_ASSERT(removeAll(NON_EXISTANT_PATH_BASE) == 0);
     CPPUNIT_ASSERT(!isDirectory(NON_EXISTANT_PATH_BASE));
@@ -114,14 +112,14 @@ FileutilsTest::testPath()
 void
 FileutilsTest::testReadDirectory()
 {
-    CPPUNIT_ASSERT(recursive_mkdir(TEST_PATH + DIR_SEPARATOR_STR + "readDirectory" + DIR_SEPARATOR_STR + "test1"));
-    CPPUNIT_ASSERT(recursive_mkdir(TEST_PATH + DIR_SEPARATOR_STR + "readDirectory" + DIR_SEPARATOR_STR + "test2"));
-    auto dirs = readDirectory(TEST_PATH + DIR_SEPARATOR_STR + "readDirectory");
+    CPPUNIT_ASSERT(recursive_mkdir(TEST_PATH / "readDirectory" / "test1"));
+    CPPUNIT_ASSERT(recursive_mkdir(TEST_PATH / "readDirectory" / "test2"));
+    auto dirs = readDirectory(TEST_PATH / "readDirectory");
     CPPUNIT_ASSERT(dirs.size() == 2);
     CPPUNIT_ASSERT(
         (dirs.at(0).compare("test1") == 0 && dirs.at(1).compare("test2") == 0)
         || (dirs.at(1).compare("test1") == 0 && dirs.at(0).compare("test2") == 0));
-    CPPUNIT_ASSERT(removeAll(TEST_PATH + DIR_SEPARATOR_STR + "readDirectory") == 0);
+    CPPUNIT_ASSERT(removeAll(TEST_PATH / "readDirectory") == 0);
 }
 
 void
@@ -135,18 +133,6 @@ FileutilsTest::testLoadFile()
     CPPUNIT_ASSERT(file.at(3) == 'G');
 }
 
-void
-FileutilsTest::testIsDirectoryWritable()
-{
-    CPPUNIT_ASSERT(recursive_mkdir(NON_EXISTANT_PATH_BASE));
-    CPPUNIT_ASSERT(isDirectoryWritable(NON_EXISTANT_PATH_BASE));
-    CPPUNIT_ASSERT(removeAll(NON_EXISTANT_PATH_BASE) == 0);
-    // Create directory with permission: read by owner
-    CPPUNIT_ASSERT(recursive_mkdir(NON_EXISTANT_PATH_BASE, 0400));
-    CPPUNIT_ASSERT(!isDirectoryWritable(NON_EXISTANT_PATH_BASE));
-    CPPUNIT_ASSERT(removeAll(NON_EXISTANT_PATH_BASE) == 0);
-}
-
 }}} // namespace dhtnet::test::fileutils
 
 JAMI_TEST_RUNNER(dhtnet::fileutils::test::FileutilsTest::name());