blob: 5babdabbc87960c04113e96595b968152e4e6f55 [file] [log] [blame]
/*
* 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