]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Add pdns::visit_directory(), wrapping opendir/readdir/closedir
authorRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 13 Nov 2023 14:20:34 +0000 (15:20 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 14 Nov 2023 10:16:45 +0000 (11:16 +0100)
pdns/arguments.cc
pdns/dnsbackend.cc
pdns/dnsdist-lua.cc
pdns/ixfrdist.cc
pdns/ixfrutils.cc
pdns/misc.cc
pdns/misc.hh

index 3970d8cd51af75a84ea46fe861088d51f0889858..babaaa1ee17c00b39c56e58886daebafba95d5b4 100644 (file)
@@ -579,37 +579,38 @@ void ArgvMap::gatherIncludes(const std::string& directory, const std::string& su
     return; // nothing to do
   }
 
-  auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(directory.c_str()), closedir);
-  if (dir == nullptr) {
-    int err = errno;
-    string msg = directory + " is not accessible: " + stringerror(err);
-    SLOG(g_log << Logger::Error << msg << std::endl,
-         d_log->error(Logr::Error, err, "Directory is not accessible", "name", Logging::Loggable(directory)));
-    throw ArgException(msg);
-  }
-
   std::vector<std::string> vec;
-  struct dirent* ent = nullptr;
-  while ((ent = readdir(dir.get())) != nullptr) { // NOLINT(concurrency-mt-unsafe): see Linux man page
-    if (ent->d_name[0] == '.') {
-      continue; // skip any dots
+  auto directoryError = pdns::visit_directory(directory, [this, &directory, &suffix, &vec]([[maybe_unused]] ino_t inodeNumber, const std::string_view& name) {
+    (void)this;
+    if (boost::starts_with(name, ".")) {
+      return true; // skip any dots
     }
-    if (boost::ends_with(ent->d_name, suffix)) {
+    if (boost::ends_with(name, suffix)) {
       // build name
-      string name = directory + "/" + ent->d_name; // NOLINT: Posix API
+      string fullName = directory + "/" + std::string(name);
       // ensure it's readable file
       struct stat statInfo
       {
       };
-      if (stat(name.c_str(), &statInfo) != 0 || !S_ISREG(statInfo.st_mode)) {
-        string msg = name + " is not a regular file";
+      if (stat(fullName.c_str(), &statInfo) != 0 || !S_ISREG(statInfo.st_mode)) {
+        string msg = fullName + " is not a regular file";
         SLOG(g_log << Logger::Error << msg << std::endl,
-             d_log->info(Logr::Error, "Unable to open non-regular file", "name", Logging::Loggable(name)));
+             d_log->info(Logr::Error, "Unable to open non-regular file", "name", Logging::Loggable(fullName)));
         throw ArgException(msg);
       }
-      vec.emplace_back(name);
+      vec.emplace_back(fullName);
     }
+    return true;
+  });
+
+  if (directoryError) {
+    int err = errno;
+    string msg = directory + " is not accessible: " + stringerror(err);
+    SLOG(g_log << Logger::Error << msg << std::endl,
+         d_log->error(Logr::Error, err, "Directory is not accessible", "name", Logging::Loggable(directory)));
+    throw ArgException(msg);
   }
+
   std::sort(vec.begin(), vec.end(), CIStringComparePOSIX());
   extraConfigs.insert(extraConfigs.end(), vec.begin(), vec.end());
 }
index 7ef66280ac24ae0b0677fbc73a55014015220e01..fdc15369c9e89adf48ce1ccb97ef2ec57f9eca78 100644 (file)
@@ -113,21 +113,16 @@ vector<string> BackendMakerClass::getModules()
 void BackendMakerClass::load_all()
 {
   // TODO: Implement this?
-  auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(arg()["module-dir"].c_str()), closedir);
-  if (!dir) {
-    g_log<<Logger::Error<<"Unable to open module directory '"<<arg()["module-dir"]<<"'"<<endl;
-    return;
-  }
-  struct dirent* entry = nullptr;
-  // NOLINTNEXTLINE(concurrency-mt-unsafe): readdir is thread-safe nowadays and readdir_r is deprecated
-  while ((entry = readdir(dir.get())) != nullptr) {
-    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay): this is what dirent is
-    auto name = std::string_view(entry->d_name, strlen(entry->d_name));
+  auto directoryError = pdns::visit_directory(arg()["module-dir"], [this]([[maybe_unused]] ino_t inodeNumber, const std::string_view& name) {
     if (boost::starts_with(name, "lib") &&
         name.size() > 13 &&
         boost::ends_with(name, "backend.so")) {
-      load(entry->d_name);
+      load(std::string(name));
     }
+    return true;
+  });
+  if (directoryError) {
+    g_log<<Logger::Error<<"Unable to open module directory '"<<arg()["module-dir"]<<"': "<<*directoryError<<endl;
   }
 }
 
index 2b47f26007f7a4a65028d14e470830e62d5fa259..09d9c5a34431de79a202846f3d2a5588b0d37be6 100644 (file)
@@ -1862,34 +1862,27 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
     }
 
     std::vector<std::string> files;
-    {
-      auto dirHandle = std::unique_ptr<DIR, decltype(&closedir)>(opendir(dirname.c_str()), closedir);
-      if (!dirHandle) {
-        errlog("Error opening the included directory %s!", dirname.c_str());
-        g_outputBuffer = "Error opening the included directory " + dirname + "!";
-        return;
-      }
-
-      struct dirent* ent = nullptr;
-      // NOLINTNEXTLINE(concurrency-mt-unsafe): readdir is thread-safe nowadays and readdir_r is deprecated
-      while ((ent = readdir(dirHandle.get())) != nullptr) {
-        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay): this is what dirent is
-        if (ent->d_name[0] == '.') {
-          continue;
-        }
-
-        if (boost::ends_with(ent->d_name, ".conf")) {
-          std::ostringstream namebuf;
-          // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay): this is what dirent is
-          namebuf << dirname << "/" << ent->d_name;
-
-          if (stat(namebuf.str().c_str(), &st) != 0 || !S_ISREG(st.st_mode)) {
-            continue;
-          }
-
+    auto directoryError = pdns::visit_directory(dirname, [&dirname, &files]([[maybe_unused]] ino_t inodeNumber, const std::string_view& name) {
+      if (boost::starts_with(name, ".")) {
+        return true;
+      }
+      if (boost::ends_with(name, ".conf")) {
+        std::ostringstream namebuf;
+        namebuf << dirname << "/" << name;
+        struct stat fileStat
+        {
+        };
+        if (stat(namebuf.str().c_str(), &fileStat) == 0 && S_ISREG(fileStat.st_mode)) {
           files.push_back(namebuf.str());
         }
       }
+      return true;
+    });
+
+    if (directoryError) {
+      errlog("Error opening included directory: %s!", *directoryError);
+      g_outputBuffer = "Error opening included directory: " + *directoryError + "!";
+      return;
     }
 
     std::sort(files.begin(), files.end());
index b9daff9d9f94e40fdd253f9391e2f25a9f9d69d9..48ade6706dd236a09491f3e24bd3d78b5f3deb75 100644 (file)
@@ -203,22 +203,20 @@ static bool sortSOA(uint32_t i, uint32_t j) {
 static void cleanUpDomain(const DNSName& domain, const uint16_t& keep, const string& workdir) {
   string dir = workdir + "/" + domain.toString();
   vector<uint32_t> zoneVersions;
-  {
-    auto dirHandle = std::unique_ptr<DIR, decltype(&closedir)>(opendir(dir.c_str()), closedir);
-    if (!dirHandle) {
-      return;
-    }
-
-    struct dirent* entry = nullptr;
-    // NOLINTNEXTLINE(concurrency-mt-unsafe): readdir is thread-safe nowadays and readdir_r is deprecated
-    while ((entry = readdir(dirHandle.get())) != nullptr) {
-      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay): this is what dirent is
-      if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
-        continue;
+  auto directoryError = pdns::visit_directory(dir, [&zoneVersions]([[maybe_unused]] ino_t inodeNumber, const std::string_view& name) {
+    if (name != "." && name != "..") {
+      try {
+        auto version = pdns::checked_stoi<uint32_t>(std::string(name));
+        zoneVersions.push_back(version);
+      }
+      catch (...) {
       }
-      //  NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay): this is what dirent is
-      zoneVersions.push_back(std::stoi(entry->d_name));
     }
+    return true;
+  });
+
+  if (directoryError) {
+    return;
   }
 
   g_log<<Logger::Info<<"Found "<<zoneVersions.size()<<" versions of "<<domain<<", asked to keep "<<keep<<", ";
index 2fff8c580d085e1775acaa9d65c69e140791a770..7d0b58d2bd9a6e20e411cc08aa594d093b9c16a0 100644 (file)
@@ -78,20 +78,20 @@ uint32_t getSerialFromMaster(const ComboAddress& master, const DNSName& zone, sh
 uint32_t getSerialFromDir(const std::string& dir)
 {
   uint32_t ret = 0;
-  auto dirhdl = std::unique_ptr<DIR, decltype(&closedir)>(opendir(dir.c_str()), closedir);
-  if (!dirhdl) {
-    throw runtime_error("Could not open IXFR directory '" + dir + "': " + stringerror());
-  }
-
-  struct dirent* entry = nullptr;
-  // NOLINTNEXTLINE(concurrency-mt-unsafe): readdir is thread-safe nowadays and readdir_r is deprecated
-  while ((entry = readdir(dirhdl.get())) != nullptr) {
-    uint32_t num = atoi(entry->d_name);
-    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay): this is what dirent is
-    auto name = std::string_view(entry->d_name, strlen(entry->d_name));
-    if (std::to_string(num) == name) {
-      ret = max(num, ret);
+  auto directoryError = pdns::visit_directory(dir, [&ret]([[maybe_unused]] ino_t inodeNumber, const std::string_view& name) {
+    try {
+      auto version = pdns::checked_stoi<uint32_t>(std::string(name));
+      if (std::to_string(version) == name) {
+        ret = std::max(version, ret);
+      }
     }
+    catch (...) {
+    }
+    return true;
+  });
+
+  if (directoryError) {
+    throw runtime_error("Could not open IXFR directory '" + dir + "': " + *directoryError);
   }
 
   return ret;
index bb5464f286be964696a41fbc0cb09ca103af514c..b6ce1417406ce8c786449733161c27f1e250077b 100644 (file)
@@ -1377,34 +1377,29 @@ DNSName getTSIGAlgoName(TSIGHashEnum& algoEnum)
 uint64_t getOpenFileDescriptors(const std::string&)
 {
 #ifdef __linux__
-  auto dirHandle = std::unique_ptr<DIR, decltype(&closedir)>(opendir(("/proc/"+std::to_string(getpid())+"/fd/").c_str()), closedir);
-  if (!dirHandle) {
-    return 0;
-  }
-
-  int ret = 0;
-  struct dirent* entry = nullptr;
-  // NOLINTNEXTLINE(concurrency-mt-unsafe): readdir is thread-safe nowadays and readdir_r is deprecated
-  while ((entry = readdir(dirHandle.get())) != nullptr) {
+  uint64_t nbFileDescriptors = 0;
+  const auto dirName = "/proc/" + std::to_string(getpid()) + "/fd/";
+  auto directoryError = pdns::visit_directory(dirName, [&nbFileDescriptors]([[maybe_unused]] ino_t inodeNumber, const std::string_view& name) {
     uint32_t num;
     try {
-      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay): this is what dirent is
-      pdns::checked_stoi_into(num, entry->d_name);
+      pdns::checked_stoi_into(num, std::string(name));
+      if (std::to_string(num) == name) {
+        nbFileDescriptors++;
+      }
     } catch (...) {
-      continue; // was not a number.
-    }
-    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay): this is what dirent is
-    if (std::to_string(num) == entry->d_name) {
-      ret++;
+      // was not a number.
     }
+    return true;
+  });
+  if (directoryError) {
+    return 0U;
   }
-  return ret;
-
+  return nbFileDescriptors;
 #elif defined(__OpenBSD__)
   // FreeBSD also has this in libopenbsd, but I don't know if that's available always
   return getdtablecount();
 #else
-  return 0;
+  return 0U;
 #endif
 }
 
@@ -1745,3 +1740,26 @@ bool constantTimeStringEquals(const std::string& a, const std::string& b)
 #endif /* !HAVE_SODIUM_MEMCMP */
 #endif /* !HAVE_CRYPTO_MEMCMP */
 }
+
+namespace pdns
+{
+std::optional<std::string> visit_directory(const std::string& directory, const std::function<bool(ino_t inodeNumber, const std::string_view& name)>& visitor)
+{
+  auto dirHandle = std::unique_ptr<DIR, decltype(&closedir)>(opendir(directory.c_str()), closedir);
+  if (!dirHandle) {
+    auto err = errno;
+    return std::string("Error opening directory '" + directory + "': " + stringerror(err));
+  }
+
+  bool keepGoing = true;
+  struct dirent* ent = nullptr;
+  // NOLINTNEXTLINE(concurrency-mt-unsafe): readdir is thread-safe nowadays and readdir_r is deprecated
+  while (keepGoing && (ent = readdir(dirHandle.get())) != nullptr) {
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay: dirent API
+    auto name = std::string_view(ent->d_name, strlen(ent->d_name));
+    keepGoing = visitor(ent->d_ino, name);
+  }
+
+  return std::nullopt;
+}
+}
index 179c27b508c583174cdbe2fd85894417fde9894b..740c32b816d86cd23805b8fe86e296e2fe347dc0 100644 (file)
@@ -833,3 +833,8 @@ struct FDWrapper
 private:
   int d_fd{-1};
 };
+
+namespace pdns
+{
+[[nodiscard]] std::optional<std::string> visit_directory(const std::string& directory, const std::function<bool(ino_t inodeNumber, const std::string_view& name)>& visitor);
+}