From: Remi Gacogne Date: Mon, 13 Nov 2023 14:20:34 +0000 (+0100) Subject: Add pdns::visit_directory(), wrapping opendir/readdir/closedir X-Git-Tag: rec-5.0.0-rc1~37^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d3190b7dad7e154445d9baa0b4723163701f5e82;p=thirdparty%2Fpdns.git Add pdns::visit_directory(), wrapping opendir/readdir/closedir --- diff --git a/pdns/arguments.cc b/pdns/arguments.cc index 3970d8cd51..babaaa1ee1 100644 --- a/pdns/arguments.cc +++ b/pdns/arguments.cc @@ -579,37 +579,38 @@ void ArgvMap::gatherIncludes(const std::string& directory, const std::string& su return; // nothing to do } - auto dir = std::unique_ptr(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 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()); } diff --git a/pdns/dnsbackend.cc b/pdns/dnsbackend.cc index 7ef66280ac..fdc15369c9 100644 --- a/pdns/dnsbackend.cc +++ b/pdns/dnsbackend.cc @@ -113,21 +113,16 @@ vector BackendMakerClass::getModules() void BackendMakerClass::load_all() { // TODO: Implement this? - auto dir = std::unique_ptr(opendir(arg()["module-dir"].c_str()), closedir); - if (!dir) { - g_log<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< files; - { - auto dirHandle = std::unique_ptr(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()); diff --git a/pdns/ixfrdist.cc b/pdns/ixfrdist.cc index b9daff9d9f..48ade6706d 100644 --- a/pdns/ixfrdist.cc +++ b/pdns/ixfrdist.cc @@ -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 zoneVersions; - { - auto dirHandle = std::unique_ptr(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(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<(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(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; diff --git a/pdns/misc.cc b/pdns/misc.cc index bb5464f286..b6ce141740 100644 --- a/pdns/misc.cc +++ b/pdns/misc.cc @@ -1377,34 +1377,29 @@ DNSName getTSIGAlgoName(TSIGHashEnum& algoEnum) uint64_t getOpenFileDescriptors(const std::string&) { #ifdef __linux__ - auto dirHandle = std::unique_ptr(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 visit_directory(const std::string& directory, const std::function& visitor) +{ + auto dirHandle = std::unique_ptr(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; +} +} diff --git a/pdns/misc.hh b/pdns/misc.hh index 179c27b508..740c32b816 100644 --- a/pdns/misc.hh +++ b/pdns/misc.hh @@ -833,3 +833,8 @@ struct FDWrapper private: int d_fd{-1}; }; + +namespace pdns +{ +[[nodiscard]] std::optional visit_directory(const std::string& directory, const std::function& visitor); +}