blob: 28c3c7f14fd06b38699bbe086bd5f16b53459019 [file] [log] [blame]
#include <c10/util/Exception.h>
#include <c10/util/tempfile.h>
#include <fmt/format.h>
#if !defined(_WIN32)
#include <unistd.h>
#include <cerrno>
#else // defined(_WIN32)
#include <Windows.h>
#include <fcntl.h>
#include <fileapi.h>
#include <io.h>
#endif // defined(_WIN32)
// Creates the filename pattern passed to and completed by `mkstemp`.
#if !defined(_WIN32)
static std::string make_filename(std::string_view name_prefix) {
// The filename argument to `mkstemp` needs "XXXXXX" at the end according to
// http://pubs.opengroup.org/onlinepubs/009695399/functions/mkstemp.html
constexpr const char* kRandomPattern = "XXXXXX";
// We see if any of these environment variables is set and use their value, or
// else default the temporary directory to `/tmp`.
const char* tmp_directory = "/tmp";
for (const char* variable : {"TMPDIR", "TMP", "TEMP", "TEMPDIR"}) {
if (const char* path = getenv(variable)) {
tmp_directory = path;
break;
}
}
return fmt::format("{}/{}{}", tmp_directory, name_prefix, kRandomPattern);
}
#else
static std::string make_filename() {
char name[L_tmpnam_s]{};
auto res = tmpnam_s(name, L_tmpnam_s);
if (res != 0) {
TORCH_WARN("Error generating temporary file");
return "";
}
return name;
}
#endif // !defined(_WIN32)
namespace c10 {
/// Attempts to return a temporary file or returns `nullopt` if an error
/// occurred.
std::optional<TempFile> try_make_tempfile(std::string_view name_prefix) {
#if defined(_WIN32)
auto filename = make_filename();
#else
auto filename = make_filename(name_prefix);
#endif
if (filename.empty()) {
return std::nullopt;
}
#if defined(_WIN32)
return TempFile(std::move(filename));
#else
const int fd = mkstemp(filename.data());
if (fd == -1) {
return std::nullopt;
}
return TempFile(std::move(filename), fd);
#endif // defined(_WIN32)
}
/// Like `try_make_tempfile`, but throws an exception if a temporary file could
/// not be returned.
TempFile make_tempfile(std::string_view name_prefix) {
if (auto tempfile = try_make_tempfile(name_prefix)) {
return std::move(*tempfile);
}
TORCH_CHECK(false, "Error generating temporary file: ", std::strerror(errno));
}
/// Attempts to return a temporary directory or returns `nullopt` if an error
/// occurred.
std::optional<TempDir> try_make_tempdir(std::string_view name_prefix) {
#if defined(_WIN32)
for (int i = 0; i < 10; i++) {
auto dirname = make_filename();
if (dirname.empty()) {
return std::nullopt;
}
if (CreateDirectoryA(dirname.c_str(), nullptr)) {
return TempDir(dirname);
}
if (GetLastError() == ERROR_SUCCESS) {
return std::nullopt;
}
}
return std::nullopt;
#else
auto filename = make_filename(name_prefix);
const char* dirname = mkdtemp(filename.data());
if (!dirname) {
return std::nullopt;
}
return TempDir(dirname);
#endif // defined(_WIN32)
}
#if defined(_WIN32)
bool TempFile::open() {
if (fd != -1) {
return false;
}
auto err = _sopen_s(
&fd,
name.c_str(),
_O_CREAT | _O_TEMPORARY | _O_EXCL | _O_BINARY | _O_RDWR,
_SH_DENYNO,
_S_IREAD | _S_IWRITE);
if (err != 0) {
fd = -1;
return false;
}
return true;
}
#endif
TempFile::~TempFile() {
if (!name.empty()) {
#if !defined(_WIN32)
if (fd >= 0) {
unlink(name.c_str());
close(fd);
}
#else
if (fd >= 0) {
_close(fd);
}
#endif
}
}
TempDir::~TempDir() {
if (!name.empty()) {
#if !defined(_WIN32)
rmdir(name.c_str());
#else // defined(_WIN32)
RemoveDirectoryA(name.c_str());
#endif // defined(_WIN32)
}
}
/// Like `try_make_tempdir`, but throws an exception if a temporary directory
/// could not be returned.
TempDir make_tempdir(std::string_view name_prefix) {
if (auto tempdir = try_make_tempdir(name_prefix)) {
return std::move(*tempdir);
}
#if !defined(_WIN32)
TORCH_CHECK(
false, "Error generating temporary directory: ", std::strerror(errno));
#else // defined(_WIN32)
TORCH_CHECK(false, "Error generating temporary directory");
#endif // defined(_WIN32)
}
} // namespace c10