// ---------------------------------------------------------------------- // File: WalkDirTree // Author: Abhishek Lekshmanan - CERN // ---------------------------------------------------------------------- /************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2022 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ #pragma once #include "fst/Namespace.hh" #include "common/Logging.hh" #include "common/StringUtils.hh" #include #include #include #include #include EOSFSTNAMESPACE_BEGIN static constexpr std::string_view XSMAP_EXT = "xsmap"; inline bool exclude_xs_map(std::string_view filename) { return common::endsWith(filename, XSMAP_EXT); } // A function to walk the dir tree and apply a function with arguments // It is necessary that the function's first argument is a const char* path // This function uses FTS to walk through directory entries and // doesn't follow symlinks and only operates on regular files atm template uint64_t WalkDirTree(std::vector&& paths, ExcludeFn exclude_fn, PathOp path_op, std::error_code& ec) { FTS* tree = fts_open(paths.data(), FTS_NOCHDIR, 0); if (!tree) { eos_static_err("msg=\"fts_open failed\" errno=%d", errno); ec = std::make_error_code(static_cast(errno)); return 0; } uint64_t cnt {0}; FTSENT* node; while ((node = fts_read(tree))) { if (node->fts_level > 0 && node->fts_name[0] == '.') { fts_set(tree, node, FTS_SKIP); } else { if (node->fts_info == FTS_F) { if (!exclude_fn(node->fts_accpath)) { path_op(node->fts_path); ++cnt; } } } } if (fts_close(tree)) { eos_static_err("msg=\"fts_close failed\" errno=%d", errno); ec = std::make_error_code(static_cast(errno)); } return cnt; } // A function useful for walking FST trees, where xsmap files are usually excluded // This variant expects a member function to be applied across the tree template uint64_t WalkFSTree(std::string path, UnaryOp&& op, std::error_code& ec) { return WalkDirTree({path.data(), nullptr}, exclude_xs_map, std::forward(op), ec); } //------------------------------------------------------------------------------ //! Method to travers the subtree and check the file if they satisfy a certain //! condition. The files are counted and only the ones with the index matching //! the given ones are checked. //! //! @param path path of the sub-tree to check //! @param check_fn operation to be applied to individual files //! @param exclude_fn operator that should skip check the file if it returns //! true //! @param match_indexes set of indexes to check inside the subtree //------------------------------------------------------------------------------ template bool WalkFsTreeCheckCond(std::vector&& paths, CheckFn check_fn, ExcludeFn exclude_fn, const std::set& match_indexes) { FTS* tree = fts_open(paths.data(), FTS_NOCHDIR, 0); if (!tree) { eos_static_err("msg=\"fts_open failed\" path=\"%s\" errno=%d", paths.data(), errno); return false; } std::set checked_indexes; uint64_t cnt {0}; FTSENT* node {nullptr}; while ((node = fts_read(tree))) { if (node->fts_level > 0 && node->fts_name[0] == '.') { fts_set(tree, node, FTS_SKIP); } else { if (node->fts_info == FTS_F) { if (!exclude_fn(node->fts_name)) { ++cnt; if (match_indexes.find(cnt) != match_indexes.end()) { if (!check_fn(node->fts_path)) { eos_static_crit("msg=\"file not matching condition\" fn=\"%s\" " "index=%llu", node->fts_path, cnt); (void) fts_close(tree); return false; } else { checked_indexes.insert(cnt); if (checked_indexes.size() == match_indexes.size()) { break; } } } } } } } if (fts_close(tree)) { eos_static_err("msg=\"fts_close failed\" errno=%d", errno); return false; } return true; } EOSFSTNAMESPACE_END