| /* |
| * Copyright 2020 Google LLC |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef THIRD_PARTY_ECCLESIA_LIB_FILE_DIR_H_ |
| #define THIRD_PARTY_ECCLESIA_LIB_FILE_DIR_H_ |
| |
| #include <dirent.h> |
| |
| #include <cstddef> |
| #include <cstdlib> |
| #include <string> |
| #include <tuple> |
| |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/container/flat_hash_set.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/str_format.h" |
| #include "absl/strings/string_view.h" |
| |
| namespace ecclesia { |
| |
| // Get a system temporary directory path. Note that this doesn't create a |
| // temporary directory for you, it simply gets the path to a usable system |
| // temporary directory. This will make use of TEST_TMPDIR if it is set in order |
| // to be "bazel test"-friendly. It should never fail because as a last resort it |
| // will just return /tmp. |
| std::string GetSystemTempdirPath(); |
| |
| // Given a path, create a directory at it, including creating any intervening |
| // directories above it if they do not already exist. |
| // |
| // Returns a non-ok status if it is unable to create the directories for any |
| // reason. Returns OK if the directory already exists. |
| absl::Status MakeDirectories(absl::string_view dirname); |
| |
| // Iterates over a list of all filenames in directory, invoking output_func with |
| // each filename. The filename will be passed as a string_view whose underlying |
| // buffer will be released at the end of the WithEachFileInDirectory call. |
| // |
| // The paths passed to output_func will be directory entries, not full paths |
| // (e.g. a file "/tmp/myfile.txt" in dirname "/tmp" will be passed as |
| // "myfile.txt"). If no files were found or there are any errors, output_func |
| // will not be called. |
| template <typename F> |
| absl::Status WithEachFileInDirectory(absl::string_view dirname, F output_func) { |
| // Constants for the names of the pseudo directory entries. |
| static constexpr absl::string_view kCurrentDir = "."; |
| static constexpr absl::string_view kParentDir = ".."; |
| |
| class ScandirCloser { |
| public: |
| ScandirCloser(struct dirent **namelist, int n) |
| : namelist_(namelist), n_(n) {} |
| ~ScandirCloser() { |
| int i = n_; |
| while (i--) free(namelist_[i]); |
| free(namelist_); |
| } |
| |
| private: |
| struct dirent **namelist_; |
| int n_; |
| }; |
| |
| struct dirent **namelist; |
| std::string c_dirname(dirname); // Needed to get a NUL terminator. |
| if (int n = scandir(c_dirname.c_str(), &namelist, nullptr, nullptr); n >= 0) { |
| ScandirCloser closer(namelist, n); |
| while (n--) { |
| absl::string_view directory_entry = namelist[n]->d_name; |
| // Skip the entries which don't correspond to real entries. |
| if (directory_entry == kCurrentDir || directory_entry == kParentDir) { |
| continue; |
| } |
| // Call the provided output function with the name. |
| output_func(directory_entry); |
| } |
| return absl::OkStatus(); |
| } else { |
| return absl::InternalError( |
| absl::StrFormat("scandir() failed on directory %s", dirname)); |
| } |
| } |
| |
| // Helper class that tracks the usage of a directory-based data store. This |
| // mostly just represents a way to pass around a standard directory for storing |
| // data files, but in a way that also provides some tracking of what files are |
| // getting created and used. |
| // |
| // The use case for this is primarily for in daemons/services where you have |
| // some local storage available and you want to make that storage available to |
| // subcomponents that have some fixed set of files they want to use. |
| // |
| // Note that "using" a file in the directory is not intended to correspond to |
| // "creating" a file. When used in daemons this object is often going to be used |
| // to track files which where already created by prior runs of the daemon, in |
| // fact that's usually the point of using a filesystem directory at all. |
| // Declaring that a file is being used is instead just a signal to the object |
| // that it should provide stats on that filename, and that it should return a |
| // failure if other code tries to use the same file. |
| class DataStoreDirectory { |
| public: |
| // Construct a instance referencing the directory specified by path. The |
| // directory is expected to already exist in a useable state, it will not |
| // attempt to create it or initialize it in any way. |
| explicit DataStoreDirectory(std::string path); |
| |
| DataStoreDirectory(const DataStoreDirectory &other) = delete; |
| DataStoreDirectory &operator=(const DataStoreDirectory &other) = delete; |
| |
| // Allocate a filename for use. By default this does not do anything with the |
| // actual file (e.g. create it, or delete it if it already exists). If the |
| // file is already in use from a prior call to UseFile, this will fail. On |
| // success it returns the full path to the file. |
| struct UseFileOptions {}; |
| absl::StatusOr<std::string> UseFile(absl::string_view filename, |
| const UseFileOptions &options); |
| |
| // Collect usage stats on either a single file, or all files. |
| // |
| // Note that both of these lookups key off of the in use files, not the actual |
| // contents of the directory. You can't use this to look up stats on a file |
| // that hasn't be registered with UseFile(). |
| struct Stats { |
| // Does the file exist? |
| // If this is false, the other stats will be left at their default values. |
| bool exists = false; |
| // The size of the file in bytes. |
| size_t size = 0; |
| |
| // Equality comparisons to make it easy to compare stats. |
| bool operator==(const Stats &other) const { |
| return std::tie(exists, size) == std::tie(other.exists, other.size); |
| } |
| bool operator!=(const Stats &other) const { return !(*this == other); } |
| }; |
| absl::StatusOr<Stats> GetFileStats(absl::string_view filename) const; |
| absl::flat_hash_map<std::string, Stats> GetAllFileStats() const; |
| |
| private: |
| // The path to the directory. |
| std::string path_; |
| // Track the files that are in use. Stores filenames, not full paths. |
| absl::flat_hash_set<std::string> used_files_; |
| }; |
| |
| } // namespace ecclesia |
| |
| #endif // THIRD_PARTY_ECCLESIA_LIB_FILE_DIR_H_ |