blob: 305da076c71a4d2b90899a5e0b7f473a5dee2e7e [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/lib/files/scoped_temp_dir.h"
#include <fcntl.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include "src/lib/files/directory.h"
#include "src/lib/files/eintr_wrapper.h"
#include "src/lib/files/file.h"
#include "src/lib/files/path.h"
#include "src/lib/files/unique_fd.h"
#include "src/lib/fxl/build_config.h"
#include "src/lib/fxl/logging.h"
namespace files {
namespace {
fxl::StringView GetGlobalTmpDir() {
const char* env_var = getenv("TMPDIR");
return fxl::StringView(env_var ? env_var : "/tmp");
}
// Fills the first 6 bytes of |tp| with random characters suitable for the file
// system. The implementation is taken from __randname.c in //zircon
void GenerateRandName(char* tp) {
FX_DCHECK(strlen(tp) >= 6);
struct timespec ts;
unsigned long r;
clock_gettime(CLOCK_REALTIME, &ts);
r = ts.tv_nsec * 65537 ^
(reinterpret_cast<uintptr_t>(&ts) / 16 + reinterpret_cast<uintptr_t>(tp));
for (size_t i = 0; i < 6; i++, r >>= 5) {
tp[i] = 'A' + (r & 15) + (r & 16) * 2;
}
}
// Creates a unique temporary file under |root_fd| from template |tp|.
fbl::unique_fd MksTempAt(int root_fd, char* tp, size_t tp_length) {
FX_DCHECK(strlen(tp) == tp_length);
FX_DCHECK(tp_length >= 6);
FX_DCHECK(memcmp(tp + tp_length - 6, "XXXXXX", 6) == 0);
int retries = 100;
do {
GenerateRandName(tp + tp_length - 6);
fbl::unique_fd result(HANDLE_EINTR(openat(root_fd, tp, O_CREAT | O_EXCL, 0700)));
if (result.is_valid()) {
return result;
}
} while (--retries && errno == EEXIST);
memcpy(tp + tp_length - 6, "XXXXXX", 6);
return fbl::unique_fd();
}
// Creates a unique temporary directory under |root_fd| from template |tp|.
char* MkdTempAt(int root_fd, char* tp, size_t tp_length) {
FX_DCHECK(strlen(tp) == tp_length);
FX_DCHECK(tp_length >= 6);
FX_DCHECK(memcmp(tp + tp_length - 6, "XXXXXX", 6) == 0);
int retries = 100;
do {
GenerateRandName(tp + tp_length - 6);
if (mkdirat(root_fd, tp, 0700) == 0) {
return tp;
}
} while (--retries && errno == EEXIST);
memcpy(tp + tp_length - 6, "XXXXXX", 6);
return nullptr;
}
} // namespace
ScopedTempDirAt::ScopedTempDirAt(int root_fd) : ScopedTempDirAt(root_fd, ".") {}
ScopedTempDirAt::ScopedTempDirAt(int root_fd, fxl::StringView parent_path) : root_fd_(root_fd) {
const std::string parent_path_str = parent_path.ToString();
// MkdTempAt replaces "XXXXXX" so that the resulting directory path is unique.
directory_path_ = parent_path_str + "/temp_dir_XXXXXX";
if (!CreateDirectoryAt(root_fd_, parent_path_str) ||
!MkdTempAt(root_fd, &directory_path_[0], directory_path_.size())) {
directory_path_ = "";
}
}
ScopedTempDirAt::~ScopedTempDirAt() {
if (directory_path_.size()) {
if (!DeletePathAt(root_fd_, directory_path_, true)) {
FX_LOGS(WARNING) << "Unable to delete: " << directory_path_;
}
}
}
const std::string& ScopedTempDirAt::path() { return directory_path_; }
int ScopedTempDirAt::root_fd() { return root_fd_; }
bool ScopedTempDirAt::NewTempFile(std::string* output) {
// MksTempAt replaces "XXXXXX" so that the resulting file path is unique.
std::string file_path = directory_path_ + "/XXXXXX";
fbl::unique_fd fd = MksTempAt(root_fd_, &file_path[0], file_path.size());
if (!fd.is_valid()) {
return false;
}
output->swap(file_path);
return true;
}
bool ScopedTempDirAt::NewTempFileWithData(const std::string& data, std::string* output) {
if (!NewTempFile(output)) {
return false;
}
return WriteFile(*output, data.data(), data.size());
}
bool ScopedTempDirAt::NewTempDir(std::string* output) {
std::string dir_path = directory_path_ + "/XXXXXX";
char* out_path = MkdTempAt(root_fd_, &dir_path[0], dir_path.size());
if (out_path == nullptr) {
return false;
}
output->swap(dir_path);
return true;
}
ScopedTempDir::ScopedTempDir() : ScopedTempDir("") {}
ScopedTempDir::ScopedTempDir(fxl::StringView parent_path)
: base_(AT_FDCWD, parent_path.empty() ? GetGlobalTmpDir() : parent_path) {}
ScopedTempDir::~ScopedTempDir() {}
const std::string& ScopedTempDir::path() { return base_.path(); }
bool ScopedTempDir::NewTempFile(std::string* output) { return base_.NewTempFile(output); }
bool ScopedTempDir::NewTempFileWithData(const std::string& data, std::string* output) {
return base_.NewTempFileWithData(data, output);
}
bool ScopedTempDir::NewTempDir(std::string* path) { return base_.NewTempDir(path); }
} // namespace files