Switch to std::filesystem

Drop the dependency on Boost.Filesystem

Change-Id: I5d6f6fe38cd0cf1c6996221188fa63db146dc9f9
diff --git a/.jenkins.d/00-deps.sh b/.jenkins.d/00-deps.sh
index 17b544a..a6c0054 100755
--- a/.jenkins.d/00-deps.sh
+++ b/.jenkins.d/00-deps.sh
@@ -7,7 +7,6 @@
     libboost-chrono-dev
     libboost-date-time-dev
     libboost-dev
-    libboost-filesystem-dev
     libboost-log-dev
     libboost-program-options-dev
     libboost-stacktrace-dev
diff --git a/.waf-tools/default-compiler-flags.py b/.waf-tools/default-compiler-flags.py
index 3e001a9..3c724fe 100644
--- a/.waf-tools/default-compiler-flags.py
+++ b/.waf-tools/default-compiler-flags.py
@@ -136,7 +136,7 @@
         return {
             'CXXFLAGS': [],
             'LINKFLAGS': [],
-            'DEFINES': ['BOOST_ASIO_NO_DEPRECATED', 'BOOST_FILESYSTEM_NO_DEPRECATED'],
+            'DEFINES': ['BOOST_ASIO_NO_DEPRECATED'],
         }
 
     def getOptimizedFlags(self, conf):
diff --git a/Dockerfile b/Dockerfile
index 5ade25a..9906703 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -8,7 +8,6 @@
         git \
         libboost-chrono-dev \
         libboost-dev \
-        libboost-filesystem-dev \
         libboost-log-dev \
         libboost-program-options-dev \
         libboost-stacktrace-dev \
diff --git a/ndn-cxx/security/pib/impl/pib-sqlite3.cpp b/ndn-cxx/security/pib/impl/pib-sqlite3.cpp
index c910d1b..cbc29a7 100644
--- a/ndn-cxx/security/pib/impl/pib-sqlite3.cpp
+++ b/ndn-cxx/security/pib/impl/pib-sqlite3.cpp
@@ -22,11 +22,10 @@
 #include "ndn-cxx/security/pib/impl/pib-sqlite3.hpp"
 #include "ndn-cxx/util/sqlite3-statement.hpp"
 
+#include <cstdlib>
+#include <filesystem>
 #include <sqlite3.h>
 
-#include <boost/filesystem/operations.hpp>
-#include <boost/filesystem/path.hpp>
-
 namespace ndn::security::pib {
 
 using ndn::util::Sqlite3Statement;
@@ -189,22 +188,22 @@
 PibSqlite3::PibSqlite3(const std::string& location)
 {
   // Determine the path of PIB DB
-  boost::filesystem::path dbDir;
+  std::filesystem::path dbDir;
   if (!location.empty()) {
-    dbDir = boost::filesystem::path(location);
+    dbDir = std::filesystem::path(location);
   }
 #ifdef NDN_CXX_WITH_TESTS
-  else if (getenv("TEST_HOME") != nullptr) {
-    dbDir = boost::filesystem::path(getenv("TEST_HOME")) / ".ndn";
+  else if (const char* testHome = std::getenv("TEST_HOME"); testHome != nullptr) {
+    dbDir = std::filesystem::path(testHome) / ".ndn";
   }
 #endif
-  else if (getenv("HOME") != nullptr) {
-    dbDir = boost::filesystem::path(getenv("HOME")) / ".ndn";
+  else if (const char* home = std::getenv("HOME"); home != nullptr) {
+    dbDir = std::filesystem::path(home) / ".ndn";
   }
   else {
-    dbDir = boost::filesystem::current_path() / ".ndn";
+    dbDir = std::filesystem::current_path() / ".ndn";
   }
-  boost::filesystem::create_directories(dbDir);
+  std::filesystem::create_directories(dbDir);
 
   // Open PIB
   int result = sqlite3_open_v2((dbDir / "pib.db").c_str(), &m_database,
@@ -217,7 +216,7 @@
                                );
 
   if (result != SQLITE_OK) {
-    NDN_THROW(PibImpl::Error("PIB database cannot be opened/created in " + dbDir.string()));
+    NDN_THROW(PibImpl::Error("PIB database cannot be opened/created in " + dbDir.native()));
   }
 
   // enable foreign key
diff --git a/ndn-cxx/security/tpm/impl/back-end-file.cpp b/ndn-cxx/security/tpm/impl/back-end-file.cpp
index 1666296..9032af3 100644
--- a/ndn-cxx/security/tpm/impl/back-end-file.cpp
+++ b/ndn-cxx/security/tpm/impl/back-end-file.cpp
@@ -30,19 +30,14 @@
 #include "ndn-cxx/security/transform/stream-sink.hpp"
 
 #include <cstdlib>
+#include <filesystem>
 #include <fstream>
-#include <sys/stat.h>
 
-#if BOOST_VERSION >= 107200
-#include <boost/filesystem/exception.hpp>
-#endif
-#include <boost/filesystem/operations.hpp>
-#include <boost/filesystem/path.hpp>
 #include <boost/lexical_cast.hpp>
 
 namespace ndn::security::tpm {
 
-namespace fs = boost::filesystem;
+namespace fs = std::filesystem;
 using ndn::security::transform::PrivateKey;
 
 class BackEndFile::Impl
@@ -55,12 +50,12 @@
       m_keystorePath = fs::path(dir);
     }
 #ifdef NDN_CXX_WITH_TESTS
-    else if (std::getenv("TEST_HOME") != nullptr) {
-      m_keystorePath = fs::path(std::getenv("TEST_HOME")) / ".ndn";
+    else if (const char* testHome = std::getenv("TEST_HOME"); testHome != nullptr) {
+      m_keystorePath = fs::path(testHome) / ".ndn";
     }
 #endif
-    else if (std::getenv("HOME") != nullptr) {
-      m_keystorePath = fs::path(std::getenv("HOME")) / ".ndn";
+    else if (const char* home = std::getenv("HOME"); home != nullptr) {
+      m_keystorePath = fs::path(home) / ".ndn";
     }
     else {
       m_keystorePath = fs::current_path() / ".ndn";
@@ -211,7 +206,7 @@
 unique_ptr<PrivateKey>
 BackEndFile::loadKey(const Name& keyName) const
 {
-  std::ifstream is(m_impl->toFileName(keyName).string());
+  std::ifstream is(m_impl->toFileName(keyName));
   auto key = make_unique<PrivateKey>();
   key->loadPkcs1Base64(is);
   return key;
@@ -220,12 +215,10 @@
 void
 BackEndFile::saveKey(const Name& keyName, const PrivateKey& key)
 {
-  std::string fileName = m_impl->toFileName(keyName).string();
+  auto fileName = m_impl->toFileName(keyName);
   std::ofstream os(fileName);
   key.savePkcs1Base64(os);
-
-  // set file permission
-  ::chmod(fileName.data(), 0000400);
+  fs::permissions(fileName, fs::perms::owner_read);
 }
 
 } // namespace ndn::security::tpm
diff --git a/ndn-cxx/security/trust-anchor-container.cpp b/ndn-cxx/security/trust-anchor-container.cpp
index 93856bf..8107fa5 100644
--- a/ndn-cxx/security/trust-anchor-container.cpp
+++ b/ndn-cxx/security/trust-anchor-container.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2023 Regents of the University of California.
+ * Copyright (c) 2013-2024 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -57,7 +57,7 @@
 }
 
 void
-TrustAnchorContainer::insert(const std::string& groupId, const boost::filesystem::path& path,
+TrustAnchorContainer::insert(const std::string& groupId, const std::filesystem::path& path,
                              time::nanoseconds refreshPeriod, bool isDir)
 {
   if (m_groups.count(groupId) != 0) {
diff --git a/ndn-cxx/security/trust-anchor-container.hpp b/ndn-cxx/security/trust-anchor-container.hpp
index 6fe208d..89771b0 100644
--- a/ndn-cxx/security/trust-anchor-container.hpp
+++ b/ndn-cxx/security/trust-anchor-container.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2023 Regents of the University of California.
+ * Copyright (c) 2013-2024 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -86,7 +86,7 @@
    * @throw Error a group with @p groupId already exists
    */
   void
-  insert(const std::string& groupId, const boost::filesystem::path& path,
+  insert(const std::string& groupId, const std::filesystem::path& path,
          time::nanoseconds refreshPeriod, bool isDir = false);
 
   /**
diff --git a/ndn-cxx/security/trust-anchor-group.cpp b/ndn-cxx/security/trust-anchor-group.cpp
index d3eaf92..c396eb5 100644
--- a/ndn-cxx/security/trust-anchor-group.cpp
+++ b/ndn-cxx/security/trust-anchor-group.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2023 Regents of the University of California.
+ * Copyright (c) 2013-2024 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -23,10 +23,6 @@
 #include "ndn-cxx/util/io.hpp"
 #include "ndn-cxx/util/logger.hpp"
 
-#if BOOST_VERSION >= 107200
-#include <boost/filesystem/directory.hpp>
-#endif
-#include <boost/filesystem/operations.hpp>
 #include <boost/range/adaptor/map.hpp>
 #include <boost/range/algorithm/copy.hpp>
 #include <boost/range/iterator_range.hpp>
@@ -35,8 +31,6 @@
 
 NDN_LOG_INIT(ndn.security.TrustAnchorGroup);
 
-namespace fs = boost::filesystem;
-
 TrustAnchorGroup::TrustAnchorGroup(CertContainerInterface& certContainer, const std::string& id)
   : m_certs(certContainer)
   , m_id(id)
@@ -84,8 +78,9 @@
 
 /////////////
 
-DynamicTrustAnchorGroup::DynamicTrustAnchorGroup(CertContainerInterface& certContainer, const std::string& id,
-                                                 const boost::filesystem::path& path,
+DynamicTrustAnchorGroup::DynamicTrustAnchorGroup(CertContainerInterface& certContainer,
+                                                 const std::string& id,
+                                                 const std::filesystem::path& path,
                                                  time::nanoseconds refreshPeriod, bool isDir)
   : TrustAnchorGroup(certContainer, id)
   , m_isDir(isDir)
@@ -112,8 +107,8 @@
 
   std::set<Name> oldAnchorNames = m_anchorNames;
 
-  auto loadCert = [this, &oldAnchorNames] (const fs::path& file) {
-    auto cert = io::load<Certificate>(file.string());
+  auto loadCert = [this, &oldAnchorNames] (const std::filesystem::path& file) {
+    auto cert = io::load<Certificate>(file);
     if (cert != nullptr) {
       if (m_anchorNames.count(cert->getName()) == 0) {
         m_anchorNames.insert(cert->getName());
@@ -128,8 +123,10 @@
   if (!m_isDir) {
     loadCert(m_path);
   }
-  else if (fs::exists(m_path)) {
-    std::for_each(fs::directory_iterator(m_path), fs::directory_iterator(), loadCert);
+  else if (std::filesystem::exists(m_path)) {
+    for (const auto& entry : std::filesystem::directory_iterator(m_path)) {
+      loadCert(entry);
+    }
   }
 
   // remove old certs
diff --git a/ndn-cxx/security/trust-anchor-group.hpp b/ndn-cxx/security/trust-anchor-group.hpp
index afd5aa2..061111d 100644
--- a/ndn-cxx/security/trust-anchor-group.hpp
+++ b/ndn-cxx/security/trust-anchor-group.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2023 Regents of the University of California.
+ * Copyright (c) 2013-2024 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -25,7 +25,7 @@
 #include "ndn-cxx/data.hpp"
 #include "ndn-cxx/security/certificate.hpp"
 
-#include <boost/filesystem/path.hpp>
+#include <filesystem>
 #include <set>
 
 namespace ndn::security {
@@ -148,7 +148,7 @@
    * @throw std::invalid_argument @p refreshPeriod is negative
    */
   DynamicTrustAnchorGroup(CertContainerInterface& certContainer, const std::string& id,
-                          const boost::filesystem::path& path, time::nanoseconds refreshPeriod,
+                          const std::filesystem::path& path, time::nanoseconds refreshPeriod,
                           bool isDir = false);
 
   void
@@ -156,7 +156,7 @@
 
 private:
   bool m_isDir;
-  boost::filesystem::path m_path;
+  std::filesystem::path m_path;
   time::nanoseconds m_refreshPeriod;
   time::steady_clock::time_point m_expireTime;
 };
diff --git a/ndn-cxx/security/validation-policy-config.cpp b/ndn-cxx/security/validation-policy-config.cpp
index 29d0a86..2222fc2 100644
--- a/ndn-cxx/security/validation-policy-config.cpp
+++ b/ndn-cxx/security/validation-policy-config.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2023 Regents of the University of California.
+ * Copyright (c) 2013-2024 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -24,11 +24,10 @@
 #include "ndn-cxx/util/io.hpp"
 
 #include <boost/algorithm/string/predicate.hpp>
-#include <boost/filesystem/operations.hpp>
-#include <boost/filesystem/path.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/property_tree/info_parser.hpp>
 
+#include <filesystem>
 #include <fstream>
 
 namespace ndn::security::validator_config {
@@ -108,8 +107,6 @@
 ValidationPolicyConfig::processConfigTrustAnchor(const ConfigSection& configSection,
                                                  const std::string& filename)
 {
-  using namespace boost::filesystem;
-
   auto propertyIt = configSection.begin();
 
   // Get trust-anchor.type
@@ -117,6 +114,7 @@
     NDN_THROW(Error("Expecting <trust-anchor.type>"));
   }
 
+  auto baseDir = std::filesystem::absolute(filename).parent_path().lexically_normal();
   std::string type = propertyIt->second.data();
   propertyIt++;
 
@@ -133,8 +131,7 @@
     if (propertyIt != configSection.end())
       NDN_THROW(Error("Expecting end of <trust-anchor>"));
 
-    m_validator->loadAnchor(file, absolute(file, path(filename).parent_path()).string(),
-                            refresh, false);
+    m_validator->loadAnchor(file, baseDir / file, refresh, false);
   }
   else if (boost::iequals(type, "base64")) {
     // Get trust-anchor.base64-string
@@ -166,8 +163,7 @@
     if (propertyIt != configSection.end())
       NDN_THROW(Error("Expecting end of <trust-anchor>"));
 
-    path dirPath = absolute(dirString, path(filename).parent_path());
-    m_validator->loadAnchor(dirString, dirPath.string(), refresh, true);
+    m_validator->loadAnchor(dirString, baseDir / dirString, refresh, true);
   }
   else if (boost::iequals(type, "any")) {
     m_shouldBypass = true;
diff --git a/ndn-cxx/util/config-file.cpp b/ndn-cxx/util/config-file.cpp
index 53afb05..172ca71 100644
--- a/ndn-cxx/util/config-file.cpp
+++ b/ndn-cxx/util/config-file.cpp
@@ -21,8 +21,8 @@
 
 #include "ndn-cxx/util/config-file.hpp"
 
-#include <boost/filesystem/operations.hpp>
 #include <boost/property_tree/ini_parser.hpp>
+#include <cstdlib>
 
 namespace ndn {
 
@@ -40,10 +40,10 @@
   close();
 }
 
-boost::filesystem::path
+std::filesystem::path
 ConfigFile::findConfigFile()
 {
-  using namespace boost::filesystem;
+  using namespace std::filesystem;
 
 #ifdef NDN_CXX_WITH_TESTS
   if (std::getenv("TEST_HOME")) {
@@ -86,7 +86,7 @@
     return false;
   }
 
-  m_input.open(m_path.c_str());
+  m_input.open(m_path);
   if (!m_input.good() || !m_input.is_open()) {
     return false;
   }
diff --git a/ndn-cxx/util/config-file.hpp b/ndn-cxx/util/config-file.hpp
index 00750dd..cc2132c 100644
--- a/ndn-cxx/util/config-file.hpp
+++ b/ndn-cxx/util/config-file.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2021 Regents of the University of California.
+ * Copyright (c) 2013-2024 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -24,9 +24,9 @@
 
 #include "ndn-cxx/detail/common.hpp"
 
+#include <filesystem>
 #include <fstream>
 
-#include <boost/filesystem/path.hpp>
 #include <boost/property_tree/ptree.hpp>
 
 namespace ndn {
@@ -65,11 +65,17 @@
 
   ~ConfigFile();
 
-  const boost::filesystem::path&
-  getPath() const;
+  const std::filesystem::path&
+  getPath() const noexcept
+  {
+    return m_path;
+  }
 
   const Parsed&
-  getParsedConfiguration() const;
+  getParsedConfiguration() const noexcept
+  {
+    return m_config;
+  }
 
 private:
   bool
@@ -102,27 +108,15 @@
    *
    * @return path to preferred configuration (according to above order) or empty path on failure
    */
-  boost::filesystem::path
+  std::filesystem::path
   findConfigFile();
 
 private:
-  boost::filesystem::path m_path; // absolute path to active configuration file (if any)
+  std::filesystem::path m_path; // absolute path to active configuration file (if any)
   std::ifstream m_input;
   Parsed m_config;
 };
 
-inline const boost::filesystem::path&
-ConfigFile::getPath() const
-{
-  return m_path;
-}
-
-inline const ConfigFile::Parsed&
-ConfigFile::getParsedConfiguration() const
-{
-  return m_config;
-}
-
 } // namespace ndn
 
 #endif // NDN_CXX_MANAGEMENT_CONFIG_FILE_HPP
diff --git a/tests/key-chain-fixture.cpp b/tests/key-chain-fixture.cpp
index fefc7cf..9c5c17a 100644
--- a/tests/key-chain-fixture.cpp
+++ b/tests/key-chain-fixture.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2023 Regents of the University of California.
+ * Copyright (c) 2013-2024 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -23,7 +23,8 @@
 
 #include "ndn-cxx/util/io.hpp"
 
-#include <boost/filesystem/operations.hpp>
+#include <filesystem>
+#include <system_error>
 
 namespace ndn::tests {
 
@@ -36,9 +37,9 @@
 
 KeyChainFixture::~KeyChainFixture()
 {
-  boost::system::error_code ec;
+  std::error_code ec;
   for (const auto& certFile : m_certFiles) {
-    boost::filesystem::remove(certFile, ec); // ignore error
+    std::filesystem::remove(certFile, ec); // ignore error
   }
 }
 
diff --git a/tests/test-home-fixture.hpp b/tests/test-home-fixture.hpp
index 9801c92..fc82af0 100644
--- a/tests/test-home-fixture.hpp
+++ b/tests/test-home-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2023 Regents of the University of California.
+ * Copyright (c) 2013-2024 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -25,12 +25,11 @@
 #include "ndn-cxx/security/key-chain.hpp"
 
 #include <cstdlib>
+#include <filesystem>
 #include <fstream>
 #include <initializer_list>
 
 #include <boost/algorithm/string/replace.hpp>
-#include <boost/filesystem/operations.hpp>
-#include <boost/filesystem/path.hpp>
 
 namespace ndn::tests {
 
@@ -46,11 +45,11 @@
 public:
   PibDirFixture()
   {
-    if (std::getenv("NDN_CLIENT_PIB") != nullptr) {
-      m_oldPib = std::getenv("NDN_CLIENT_PIB");
+    if (const char* envPib = std::getenv("NDN_CLIENT_PIB"); envPib != nullptr) {
+      m_oldPib = envPib;
     }
-    if (std::getenv("NDN_CLIENT_TPM") != nullptr) {
-      m_oldTpm = std::getenv("NDN_CLIENT_TPM");
+    if (const char* envTpm = std::getenv("NDN_CLIENT_TPM"); envTpm != nullptr) {
+      m_oldTpm = envTpm;
     }
 
     /// @todo Consider change to an in-memory PIB/TPM
@@ -74,7 +73,7 @@
       unsetenv("NDN_CLIENT_TPM");
     }
 
-    boost::filesystem::remove_all(m_pibDir);
+    std::filesystem::remove_all(m_pibDir);
     KeyChain::resetDefaultLocators();
   }
 
@@ -106,8 +105,9 @@
   void
   createClientConf(std::initializer_list<std::string> lines) const
   {
-    boost::filesystem::create_directories(boost::filesystem::path(this->m_pibDir) / ".ndn");
-    std::ofstream of((boost::filesystem::path(this->m_pibDir) / ".ndn" / "client.conf").c_str());
+    auto ndnDir = std::filesystem::path(this->m_pibDir) / ".ndn";
+    std::filesystem::create_directories(ndnDir);
+    std::ofstream of(ndnDir / "client.conf");
     for (auto line : lines) {
       boost::replace_all(line, "%PATH%", this->m_pibDir);
       of << line << std::endl;
diff --git a/tests/tests-pch.hpp b/tests/tests-pch.hpp
index 30e0fdc..c277bcf 100644
--- a/tests/tests-pch.hpp
+++ b/tests/tests-pch.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2020 Regents of the University of California.
+ * Copyright (c) 2013-2024 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -31,7 +31,7 @@
 
 #include "tests/boost-test.hpp"
 
+#include <filesystem>
 #include <fstream>
-#include <boost/filesystem.hpp>
 
 #endif // NDN_CXX_TESTS_TESTS_PCH_HPP
diff --git a/tests/unit/security/pib/pib-impl.t.cpp b/tests/unit/security/pib/pib-impl.t.cpp
index d3d73de..7f624fd 100644
--- a/tests/unit/security/pib/pib-impl.t.cpp
+++ b/tests/unit/security/pib/pib-impl.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2023 Regents of the University of California.
+ * Copyright (c) 2013-2024 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -25,7 +25,8 @@
 #include "tests/boost-test.hpp"
 #include "tests/unit/security/pib/pib-data-fixture.hpp"
 
-#include <boost/filesystem.hpp>
+#include <filesystem>
+
 #include <boost/mp11/list.hpp>
 
 namespace ndn::tests {
@@ -46,14 +47,14 @@
 public:
   ~PibSqlite3Fixture()
   {
-    boost::filesystem::remove_all(m_path);
+    std::filesystem::remove_all(m_path);
   }
 
 private:
-  const boost::filesystem::path m_path{boost::filesystem::path(UNIT_TESTS_TMPDIR) / "TestPibImpl"};
+  const std::filesystem::path m_path{std::filesystem::path(UNIT_TESTS_TMPDIR) / "TestPibImpl"};
 
 public:
-  PibSqlite3 pib{m_path.string()};
+  PibSqlite3 pib{m_path};
 };
 
 using PibImpls = boost::mp11::mp_list<PibMemoryFixture, PibSqlite3Fixture>;
diff --git a/tests/unit/security/tpm/back-end-wrapper-file.hpp b/tests/unit/security/tpm/back-end-wrapper-file.hpp
index 6d8a663..cd25978 100644
--- a/tests/unit/security/tpm/back-end-wrapper-file.hpp
+++ b/tests/unit/security/tpm/back-end-wrapper-file.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2023 Regents of the University of California.
+ * Copyright (c) 2013-2024 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -24,7 +24,7 @@
 
 #include "ndn-cxx/security/tpm/impl/back-end-file.hpp"
 
-#include <boost/filesystem.hpp>
+#include <filesystem>
 
 namespace ndn::tests {
 
@@ -35,14 +35,14 @@
 {
 public:
   BackEndWrapperFile()
-    : m_tmpPath(boost::filesystem::path(UNIT_TESTS_TMPDIR) / "TpmBackEndFile")
-    , m_impl(make_unique<security::tpm::BackEndFile>(m_tmpPath.string()))
+    : m_tmpPath(std::filesystem::path(UNIT_TESTS_TMPDIR) / "TpmBackEndFile")
+    , m_impl(make_unique<security::tpm::BackEndFile>(m_tmpPath))
   {
   }
 
   ~BackEndWrapperFile()
   {
-    boost::filesystem::remove_all(m_tmpPath);
+    std::filesystem::remove_all(m_tmpPath);
   }
 
   security::tpm::BackEnd&
@@ -58,7 +58,7 @@
   }
 
 private:
-  const boost::filesystem::path m_tmpPath;
+  const std::filesystem::path m_tmpPath;
   const unique_ptr<security::tpm::BackEnd> m_impl;
 };
 
diff --git a/tests/unit/security/trust-anchor-container.t.cpp b/tests/unit/security/trust-anchor-container.t.cpp
index 246ef1c..b711ba6 100644
--- a/tests/unit/security/trust-anchor-container.t.cpp
+++ b/tests/unit/security/trust-anchor-container.t.cpp
@@ -25,8 +25,6 @@
 #include "tests/key-chain-fixture.hpp"
 #include "tests/unit/clock-fixture.hpp"
 
-#include <boost/filesystem/operations.hpp>
-
 namespace ndn::tests {
 
 using namespace ndn::security;
@@ -38,20 +36,20 @@
 public:
   TrustAnchorContainerFixture()
   {
-    boost::filesystem::create_directories(certDirPath);
+    std::filesystem::create_directories(certDirPath);
 
     identity1 = m_keyChain.createIdentity("/TestAnchorContainer/First");
     cert1 = identity1.getDefaultKey().getDefaultCertificate();
-    saveCert(cert1, certPath1.string());
+    saveCert(cert1, certPath1);
 
     identity2 = m_keyChain.createIdentity("/TestAnchorContainer/Second");
     cert2 = identity2.getDefaultKey().getDefaultCertificate();
-    saveCert(cert2, certPath2.string());
+    saveCert(cert2, certPath2);
   }
 
   ~TrustAnchorContainerFixture() override
   {
-    boost::filesystem::remove_all(certDirPath);
+    std::filesystem::remove_all(certDirPath);
   }
 
   void
@@ -73,9 +71,9 @@
   }
 
 public:
-  const boost::filesystem::path certDirPath{boost::filesystem::path(UNIT_TESTS_TMPDIR) / "test-cert-dir"};
-  const boost::filesystem::path certPath1{certDirPath / "trust-anchor-1.cert"};
-  const boost::filesystem::path certPath2{certDirPath / "trust-anchor-2.cert"};
+  const std::filesystem::path certDirPath{std::filesystem::path(UNIT_TESTS_TMPDIR) / "test-cert-dir"};
+  const std::filesystem::path certPath1{certDirPath / "trust-anchor-1.cert"};
+  const std::filesystem::path certPath2{certDirPath / "trust-anchor-2.cert"};
 
   TrustAnchorContainer anchorContainer;
 
@@ -100,20 +98,20 @@
   BOOST_CHECK_NO_THROW(anchorContainer.insert("group1", Certificate(cert1)));
   BOOST_CHECK_EQUAL(cert, anchorContainer.find(cert1.getName())); // still the same instance of the certificate
   // cannot add dynamic group when static already exists
-  BOOST_CHECK_THROW(anchorContainer.insert("group1", certPath1.string(), 1_s), TrustAnchorContainer::Error);
+  BOOST_CHECK_THROW(anchorContainer.insert("group1", certPath1, 1_s), TrustAnchorContainer::Error);
   BOOST_CHECK_EQUAL(anchorContainer.getGroup("group1").size(), 1);
   BOOST_CHECK_EQUAL(anchorContainer.size(), 1);
 
   // From file
-  anchorContainer.insert("group2", certPath2.string(), 1_s);
+  anchorContainer.insert("group2", certPath2, 1_s);
   BOOST_CHECK(anchorContainer.find(cert2.getName()) != nullptr);
   BOOST_CHECK(anchorContainer.find(identity2.getName()) != nullptr);
   BOOST_CHECK_THROW(anchorContainer.insert("group2", Certificate(cert2)), TrustAnchorContainer::Error);
-  BOOST_CHECK_THROW(anchorContainer.insert("group2", certPath2.string(), 1_s), TrustAnchorContainer::Error);
+  BOOST_CHECK_THROW(anchorContainer.insert("group2", certPath2, 1_s), TrustAnchorContainer::Error);
   BOOST_CHECK_EQUAL(anchorContainer.getGroup("group2").size(), 1);
   BOOST_CHECK_EQUAL(anchorContainer.size(), 2);
 
-  boost::filesystem::remove(certPath2);
+  std::filesystem::remove(certPath2);
   advanceClocks(1_s, 11);
 
   BOOST_CHECK(anchorContainer.find(identity2.getName()) == nullptr);
@@ -134,15 +132,15 @@
 
 BOOST_AUTO_TEST_CASE(DynamicAnchorFromDir)
 {
-  boost::filesystem::remove(certPath2);
+  std::filesystem::remove(certPath2);
 
-  anchorContainer.insert("group", certDirPath.string(), 1_s, true /* isDir */);
+  anchorContainer.insert("group", certDirPath, 1_s, /*isDir*/ true);
 
   BOOST_CHECK(anchorContainer.find(identity1.getName()) != nullptr);
   BOOST_CHECK(anchorContainer.find(identity2.getName()) == nullptr);
   BOOST_CHECK_EQUAL(anchorContainer.getGroup("group").size(), 1);
 
-  saveCert(cert2, certPath2.string());
+  saveCert(cert2, certPath2);
 
   advanceClocks(100_ms, 11);
 
@@ -150,7 +148,7 @@
   BOOST_CHECK(anchorContainer.find(identity2.getName()) != nullptr);
   BOOST_CHECK_EQUAL(anchorContainer.getGroup("group").size(), 2);
 
-  boost::filesystem::remove_all(certDirPath);
+  std::filesystem::remove_all(certDirPath);
 
   advanceClocks(100_ms, 11);
 
@@ -161,7 +159,7 @@
 
 BOOST_AUTO_TEST_CASE(FindByInterest)
 {
-  anchorContainer.insert("group1", certPath1.string(), 1_s);
+  anchorContainer.insert("group1", certPath1, 1_s);
 
   checkFindByInterest(identity1.getName(), true, cert1);
   checkFindByInterest(identity1.getName().getPrefix(-1), true, cert1);
diff --git a/tests/unit/security/validation-policy-config.t.cpp b/tests/unit/security/validation-policy-config.t.cpp
index 746ac90..34412a1 100644
--- a/tests/unit/security/validation-policy-config.t.cpp
+++ b/tests/unit/security/validation-policy-config.t.cpp
@@ -62,7 +62,7 @@
 public:
   ValidationPolicyConfigFixture()
   {
-    boost::filesystem::create_directories(path);
+    std::filesystem::create_directories(path);
     baseConfig = R"CONF(
         rule
         {
@@ -85,7 +85,7 @@
 
   ~ValidationPolicyConfigFixture()
   {
-    boost::filesystem::remove_all(path);
+    std::filesystem::remove_all(path);
   }
 
 private:
@@ -101,7 +101,7 @@
   }
 
 protected:
-  const boost::filesystem::path path{boost::filesystem::path(UNIT_TESTS_TMPDIR) / "security" / "validation-policy-config"};
+  const std::filesystem::path path{std::filesystem::path(UNIT_TESTS_TMPDIR) / "security" / "validation-policy-config"};
   std::string baseConfig;
 
   using Packet = PacketType;
@@ -115,14 +115,14 @@
   {
     BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
 
-    this->saveIdentityCert(this->identity, (this->path / "identity.ndncert").string());
+    this->saveIdentityCert(this->identity, this->path / "identity.ndncert");
     this->policy.load(this->baseConfig + R"CONF(
         trust-anchor
         {
           type file
           file-name "trust-anchor.ndncert"
         }
-      )CONF", (this->path / "test-config").string());
+      )CONF", this->path / "test-config");
 
     BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
     BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
@@ -135,7 +135,7 @@
 public:
   LoadFileWithFileAnchor()
   {
-    std::string configFile = (this->path / "config.conf").string();
+    auto configFile = this->path / "config.conf";
     {
       std::ofstream config(configFile);
       config << this->baseConfig << R"CONF(
@@ -147,7 +147,7 @@
         )CONF";
     }
 
-    this->saveIdentityCert(this->identity, (this->path / "identity.ndncert").string());
+    this->saveIdentityCert(this->identity, this->path / "identity.ndncert");
 
     BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
 
@@ -164,7 +164,7 @@
 public:
   LoadFileWithMultipleFileAnchors()
   {
-    std::string configFile = (this->path / "config.conf").string();
+    auto configFile = this->path / "config.conf";
     {
       std::ofstream config(configFile);
       config << this->baseConfig << R"CONF(
@@ -181,7 +181,7 @@
         )CONF";
     }
 
-    this->saveIdentityCert(this->identity, (this->path / "identity.ndncert").string());
+    this->saveIdentityCert(this->identity, this->path / "identity.ndncert");
 
     BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
 
@@ -206,11 +206,11 @@
         }
       )CONF");
 
-    this->saveIdentityCert(this->identity, (this->path / "identity.ndncert").string());
+    this->saveIdentityCert(this->identity, this->path / "identity.ndncert");
 
     BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
 
-    this->policy.load(section, (this->path / "test-config").string());
+    this->policy.load(section, this->path / "test-config");
 
     BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
     BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
@@ -238,7 +238,7 @@
           type base64
           base64-string ")CONF" + os.str() + R"CONF("
         }
-      )CONF", (this->path / "test-config").string());
+      )CONF", this->path / "test-config");
 
     BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
     BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
@@ -307,8 +307,8 @@
   {
     BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
 
-    boost::filesystem::create_directories(this->path / "keys");
-    this->saveIdentityCert(this->identity, (this->path / "keys" / "identity.ndncert").string());
+    std::filesystem::create_directories(this->path / "keys");
+    this->saveIdentityCert(this->identity, this->path / "keys" / "identity.ndncert");
 
     this->policy.load(this->baseConfig + R"CONF(
         trust-anchor
@@ -317,7 +317,7 @@
           dir keys
           )CONF" + Refresh::getRefreshString() + R"CONF(
         }
-      )CONF", (this->path / "test-config").string());
+      )CONF", this->path / "test-config");
 
     BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
     BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
@@ -713,7 +713,7 @@
   using Packet = Data;
   Packet unsignedPacket("/Security/ValidatorFixture/Sub1/Sub2/Packet");
 
-  boost::filesystem::remove(this->path / "keys" / "identity.ndncert");
+  std::filesystem::remove(this->path / "keys" / "identity.ndncert");
   this->advanceClocks(Refresh::getRefreshTime(), 3);
 
   Packet packet = unsignedPacket;
diff --git a/tests/unit/security/validator-config.t.cpp b/tests/unit/security/validator-config.t.cpp
index 3cfb122..8ca4245 100644
--- a/tests/unit/security/validator-config.t.cpp
+++ b/tests/unit/security/validator-config.t.cpp
@@ -51,28 +51,27 @@
 {
 public:
   ValidatorConfigFixture()
-    : path(boost::filesystem::path(UNIT_TESTS_TMPDIR) / "security" / "validator-config")
+    : path(std::filesystem::path(UNIT_TESTS_TMPDIR) / "security" / "validator-config")
     , validator(make_unique<security::CertificateFetcherOffline>())
   {
-    boost::filesystem::create_directories(path);
+    std::filesystem::create_directories(path);
     config = R"CONF(
         trust-anchor
         {
           type any
         }
       )CONF";
-    configFile = (this->path / "config.conf").string();
-    std::ofstream f(configFile.c_str());
-    f << config;
+    configFile = path / "config.conf";
+    std::ofstream(configFile) << config;
   }
 
   ~ValidatorConfigFixture()
   {
-    boost::filesystem::remove_all(path);
+    std::filesystem::remove_all(path);
   }
 
 public:
-  const boost::filesystem::path path;
+  const std::filesystem::path path;
   std::string config;
   std::string configFile;
   ValidatorConfig validator;
diff --git a/tests/unit/util/config-file.t.cpp b/tests/unit/util/config-file.t.cpp
index ab8f212..eac5f21 100644
--- a/tests/unit/util/config-file.t.cpp
+++ b/tests/unit/util/config-file.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2023 Regents of the University of California.
+ * Copyright (c) 2013-2024 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -24,7 +24,6 @@
 #include "tests/boost-test.hpp"
 #include "tests/unit/test-home-env-saver.hpp"
 
-#include <boost/filesystem/operations.hpp>
 #include <cstdlib>
 
 namespace ndn::tests {
@@ -34,15 +33,13 @@
 
 BOOST_AUTO_TEST_CASE(Parse)
 {
-  namespace fs = boost::filesystem;
-
   setenv("TEST_HOME", "tests/unit/util/config-file-home", 1);
 
-  fs::path homePath(fs::absolute(std::getenv("TEST_HOME")));
+  auto homePath = std::filesystem::absolute(std::getenv("TEST_HOME"));
   homePath /= ".ndn/client.conf";
 
   ConfigFile config;
-  BOOST_REQUIRE_EQUAL(config.getPath(), homePath);
+  BOOST_CHECK_EQUAL(config.getPath(), homePath);
 
   const ConfigFile::Parsed& parsed = config.getParsedConfiguration();
   BOOST_CHECK_EQUAL(parsed.get<std::string>("a"), "/path/to/nowhere");
@@ -53,14 +50,14 @@
 {
   setenv("TEST_HOME", "tests/unit/util/does/not/exist", 1);
 
-  BOOST_CHECK_NO_THROW(ConfigFile config);
+  BOOST_CHECK_NO_THROW(ConfigFile{});
 }
 
 BOOST_AUTO_TEST_CASE(ParseMalformed)
 {
   setenv("TEST_HOME", "tests/unit/util/config-file-malformed-home", 1);
 
-  BOOST_CHECK_THROW(ConfigFile config, ConfigFile::Error);
+  BOOST_CHECK_THROW(ConfigFile{}, ConfigFile::Error);
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestConfigFile
diff --git a/tests/unit/util/io.t.cpp b/tests/unit/util/io.t.cpp
index 2dc303d..350abf6 100644
--- a/tests/unit/util/io.t.cpp
+++ b/tests/unit/util/io.t.cpp
@@ -24,9 +24,11 @@
 #include "tests/boost-test.hpp"
 #include "tests/key-chain-fixture.hpp"
 
-#include <boost/filesystem.hpp>
 #include <boost/mp11/list.hpp>
 
+#include <filesystem>
+#include <system_error>
+
 namespace ndn::tests {
 
 BOOST_AUTO_TEST_SUITE(Util)
@@ -100,25 +102,23 @@
 {
 protected:
   FileIoFixture()
-    : filepath(boost::filesystem::path(UNIT_TESTS_TMPDIR) / "TestIo")
-    , filename(filepath.string())
   {
-    boost::filesystem::create_directories(filepath.parent_path());
+    std::filesystem::create_directories(filename.parent_path());
   }
 
   ~FileIoFixture()
   {
-    boost::system::error_code ec;
-    boost::filesystem::remove(filepath, ec); // ignore error
+    std::error_code ec;
+    std::filesystem::remove(filename, ec); // ignore error
   }
 
   /**
-   * \brief Create a directory at `filepath`, so that it's neither readable nor writable as a file.
+   * \brief Create a directory at `filename`, so that it's neither readable nor writable as a file.
    */
   void
   mkdir() const
   {
-    boost::filesystem::create_directory(filepath);
+    std::filesystem::create_directory(filename);
   }
 
   template<typename Container>
@@ -148,8 +148,7 @@
   }
 
 protected:
-  const boost::filesystem::path filepath;
-  const std::string filename;
+  const std::filesystem::path filename{std::filesystem::path(UNIT_TESTS_TMPDIR) / "TestIo"};
 };
 
 BOOST_FIXTURE_TEST_SUITE(FileIo, FileIoFixture)
diff --git a/tests/unit/util/sqlite3-statement.t.cpp b/tests/unit/util/sqlite3-statement.t.cpp
index b3d32d5..fe114c9 100644
--- a/tests/unit/util/sqlite3-statement.t.cpp
+++ b/tests/unit/util/sqlite3-statement.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2023 Regents of the University of California.
+ * Copyright (c) 2013-2024 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -23,8 +23,8 @@
 
 #include "tests/boost-test.hpp"
 
-#include <boost/filesystem.hpp>
 #include <cstring>
+#include <filesystem>
 #include <sqlite3.h>
 
 namespace ndn::tests {
@@ -36,9 +36,9 @@
 public:
   Sqlite3DbFixture()
   {
-    boost::filesystem::create_directories(m_path);
+    std::filesystem::create_directories(m_path);
 
-    int result = sqlite3_open_v2((m_path / "sqlite3-statement.db").string().c_str(), &db,
+    int result = sqlite3_open_v2((m_path / "sqlite3-statement.db").c_str(), &db,
                                  SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
 #ifdef NDN_CXX_DISABLE_SQLITE3_FS_LOCKING
                                  "unix-dotfile"
@@ -48,21 +48,21 @@
                                  );
 
     if (result != SQLITE_OK) {
-      BOOST_FAIL("Sqlite3 database cannot be opened/created: " + m_path.string());
+      BOOST_FAIL("Sqlite3 database cannot be opened/created: " + m_path.native());
     }
   }
 
   ~Sqlite3DbFixture()
   {
     sqlite3_close(db);
-    boost::filesystem::remove_all(m_path);
+    std::filesystem::remove_all(m_path);
   }
 
 protected:
   sqlite3* db = nullptr;
 
 private:
-  const boost::filesystem::path m_path{UNIT_TESTS_TMPDIR};
+  const std::filesystem::path m_path{UNIT_TESTS_TMPDIR};
 };
 
 BOOST_AUTO_TEST_SUITE(Util)
diff --git a/tools/ndnsec/main.cpp b/tools/ndnsec/main.cpp
index a9a055b..efdc526 100644
--- a/tools/ndnsec/main.cpp
+++ b/tools/ndnsec/main.cpp
@@ -26,7 +26,7 @@
 #include "ndn-cxx/version.hpp"
 
 #include <boost/exception/diagnostic_information.hpp>
-#include <boost/filesystem/path.hpp>
+#include <filesystem>
 #include <iostream>
 
 NDN_LOG_INIT(ndnsec);
@@ -54,8 +54,7 @@
 int
 main(int argc, char* argv[])
 {
-  boost::filesystem::path p(argv[0]);
-  std::string basename(p.filename().string());
+  std::string basename = std::filesystem::path(argv[0]).filename();
   std::string command;
   if (basename.rfind("ndnsec-", 0) == 0) {
     command = basename.substr(std::strlen("ndnsec-"));
diff --git a/wscript b/wscript
index 1e18344..e0fd3af 100644
--- a/wscript
+++ b/wscript
@@ -119,7 +119,7 @@
                    'For more information, see https://redmine.named-data.net/projects/nfd/wiki/Boost')
 
     # Boost.Log requires Boost.Thread
-    boost_libs = ['chrono', 'filesystem', 'log', 'thread']
+    boost_libs = ['chrono', 'log', 'thread']
 
     # Boost.Date_Time is header-only since 1.73
     if conf.env.BOOST_VERSION_NUMBER < 107300: