| /* |
| * 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. |
| */ |
| |
| #include "file/dir.h" |
| |
| #include <errno.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include <cstdlib> |
| #include <stack> |
| #include <string> |
| #include <utility> |
| |
| #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" |
| #include "file/path.h" |
| |
| namespace ecclesia { |
| namespace { |
| |
| // Implement DataStoreDirectory::GetFileStats for a single file, but unlike the |
| // actual method this operates on a full path and it does not do any kind of "is |
| // this a registered filename" check. |
| DataStoreDirectory::Stats GetSingleFileStats(const std::string &path) { |
| DataStoreDirectory::Stats stats; |
| struct stat st; |
| if (stat(path.c_str(), &st) == 0) { |
| // We basically treat any kind of stat failure as "it doesn't exist". While |
| // there are technically other ways that this could fail there's not much |
| // that we could meaningfully distinguish so "exists=false" is good enough. |
| stats.exists = true; |
| stats.size = st.st_size; |
| } |
| return stats; |
| } |
| |
| } // namespace |
| |
| std::string GetSystemTempdirPath() { |
| // If we're in a test environment, use the test temporary directory. |
| char *test_tmpdir = std::getenv("TEST_TMPDIR"); |
| if (test_tmpdir) { |
| return test_tmpdir; |
| } |
| // Fall back to /tmp if we don't have a more specific directory to return. |
| return "/tmp"; |
| } |
| |
| absl::Status MakeDirectories(absl::string_view dirname) { |
| std::stack<std::string> missing_dirs; |
| |
| // Keep walking up the tree until we hit the root. |
| while (dirname != "/" && !dirname.empty()) { |
| std::string path(dirname); |
| if (access(path.c_str(), F_OK) == 0) { |
| break; |
| } else { |
| missing_dirs.push(std::move(path)); |
| dirname = GetDirname(dirname); |
| } |
| } |
| |
| // Try and create all of the components that are missing. |
| while (!missing_dirs.empty()) { |
| std::string path = std::move(missing_dirs.top()); |
| missing_dirs.pop(); |
| if (mkdir(path.c_str(), 0700) < 0) { |
| return absl::UnknownError(absl::StrFormat( |
| "unable to create directory %s, mkdir returned errno=%d", path, |
| errno)); |
| } |
| } |
| |
| // If we get here then every directory was created. |
| return absl::OkStatus(); |
| } |
| |
| DataStoreDirectory::DataStoreDirectory(std::string path) |
| : path_(std::move(path)) {} |
| |
| absl::StatusOr<std::string> DataStoreDirectory::UseFile( |
| absl::string_view filename, const UseFileOptions &options) { |
| // Make sure the filename is actually a filename, not empty or a directory. |
| if (filename.empty()) { |
| return absl::InvalidArgumentError("filename must not be empty"); |
| } |
| if (filename.find('/') != filename.npos) { |
| return absl::InvalidArgumentError(absl::StrFormat( |
| "filename '%s' appears to be a path and not a filename", filename)); |
| } |
| // If the file is already in use then return an error. |
| if (used_files_.contains(filename)) { |
| return absl::FailedPreconditionError(absl::StrFormat( |
| "file '%s' in directory '%s' is already in use", filename, path_)); |
| } |
| // The file is not in use, do any setup required by the options and register |
| // it for use. |
| used_files_.emplace(filename); |
| return JoinFilePaths(path_, filename); |
| } |
| |
| absl::StatusOr<DataStoreDirectory::Stats> DataStoreDirectory::GetFileStats( |
| absl::string_view filename) const { |
| if (!used_files_.contains(filename)) { |
| return absl::FailedPreconditionError(absl::StrFormat( |
| "'%s' is not a known filename to '%s'", filename, path_)); |
| } |
| return GetSingleFileStats(JoinFilePaths(path_, filename)); |
| } |
| |
| absl::flat_hash_map<std::string, DataStoreDirectory::Stats> |
| DataStoreDirectory::GetAllFileStats() const { |
| absl::flat_hash_map<std::string, Stats> stats_map; |
| for (const std::string &filename : used_files_) { |
| stats_map[filename] = GetSingleFileStats(JoinFilePaths(path_, filename)); |
| } |
| return stats_map; |
| } |
| |
| } // namespace ecclesia |