1*da0073e9SAndroid Build Coastguard Worker #include <c10/util/Exception.h>
2*da0073e9SAndroid Build Coastguard Worker #include <c10/util/tempfile.h>
3*da0073e9SAndroid Build Coastguard Worker #include <fmt/format.h>
4*da0073e9SAndroid Build Coastguard Worker
5*da0073e9SAndroid Build Coastguard Worker #if !defined(_WIN32)
6*da0073e9SAndroid Build Coastguard Worker #include <unistd.h>
7*da0073e9SAndroid Build Coastguard Worker #include <cerrno>
8*da0073e9SAndroid Build Coastguard Worker #else // defined(_WIN32)
9*da0073e9SAndroid Build Coastguard Worker #include <Windows.h>
10*da0073e9SAndroid Build Coastguard Worker #include <fcntl.h>
11*da0073e9SAndroid Build Coastguard Worker #include <fileapi.h>
12*da0073e9SAndroid Build Coastguard Worker #include <io.h>
13*da0073e9SAndroid Build Coastguard Worker #endif // defined(_WIN32)
14*da0073e9SAndroid Build Coastguard Worker
15*da0073e9SAndroid Build Coastguard Worker // Creates the filename pattern passed to and completed by `mkstemp`.
16*da0073e9SAndroid Build Coastguard Worker #if !defined(_WIN32)
make_filename(std::string_view name_prefix)17*da0073e9SAndroid Build Coastguard Worker static std::string make_filename(std::string_view name_prefix) {
18*da0073e9SAndroid Build Coastguard Worker // The filename argument to `mkstemp` needs "XXXXXX" at the end according to
19*da0073e9SAndroid Build Coastguard Worker // http://pubs.opengroup.org/onlinepubs/009695399/functions/mkstemp.html
20*da0073e9SAndroid Build Coastguard Worker constexpr const char* kRandomPattern = "XXXXXX";
21*da0073e9SAndroid Build Coastguard Worker
22*da0073e9SAndroid Build Coastguard Worker // We see if any of these environment variables is set and use their value, or
23*da0073e9SAndroid Build Coastguard Worker // else default the temporary directory to `/tmp`.
24*da0073e9SAndroid Build Coastguard Worker
25*da0073e9SAndroid Build Coastguard Worker const char* tmp_directory = "/tmp";
26*da0073e9SAndroid Build Coastguard Worker for (const char* variable : {"TMPDIR", "TMP", "TEMP", "TEMPDIR"}) {
27*da0073e9SAndroid Build Coastguard Worker if (const char* path = getenv(variable)) {
28*da0073e9SAndroid Build Coastguard Worker tmp_directory = path;
29*da0073e9SAndroid Build Coastguard Worker break;
30*da0073e9SAndroid Build Coastguard Worker }
31*da0073e9SAndroid Build Coastguard Worker }
32*da0073e9SAndroid Build Coastguard Worker return fmt::format("{}/{}{}", tmp_directory, name_prefix, kRandomPattern);
33*da0073e9SAndroid Build Coastguard Worker }
34*da0073e9SAndroid Build Coastguard Worker #else
make_filename()35*da0073e9SAndroid Build Coastguard Worker static std::string make_filename() {
36*da0073e9SAndroid Build Coastguard Worker char name[L_tmpnam_s]{};
37*da0073e9SAndroid Build Coastguard Worker auto res = tmpnam_s(name, L_tmpnam_s);
38*da0073e9SAndroid Build Coastguard Worker if (res != 0) {
39*da0073e9SAndroid Build Coastguard Worker TORCH_WARN("Error generating temporary file");
40*da0073e9SAndroid Build Coastguard Worker return "";
41*da0073e9SAndroid Build Coastguard Worker }
42*da0073e9SAndroid Build Coastguard Worker return name;
43*da0073e9SAndroid Build Coastguard Worker }
44*da0073e9SAndroid Build Coastguard Worker #endif // !defined(_WIN32)
45*da0073e9SAndroid Build Coastguard Worker
46*da0073e9SAndroid Build Coastguard Worker namespace c10 {
47*da0073e9SAndroid Build Coastguard Worker /// Attempts to return a temporary file or returns `nullopt` if an error
48*da0073e9SAndroid Build Coastguard Worker /// occurred.
try_make_tempfile(std::string_view name_prefix)49*da0073e9SAndroid Build Coastguard Worker std::optional<TempFile> try_make_tempfile(std::string_view name_prefix) {
50*da0073e9SAndroid Build Coastguard Worker #if defined(_WIN32)
51*da0073e9SAndroid Build Coastguard Worker auto filename = make_filename();
52*da0073e9SAndroid Build Coastguard Worker #else
53*da0073e9SAndroid Build Coastguard Worker auto filename = make_filename(name_prefix);
54*da0073e9SAndroid Build Coastguard Worker #endif
55*da0073e9SAndroid Build Coastguard Worker if (filename.empty()) {
56*da0073e9SAndroid Build Coastguard Worker return std::nullopt;
57*da0073e9SAndroid Build Coastguard Worker }
58*da0073e9SAndroid Build Coastguard Worker #if defined(_WIN32)
59*da0073e9SAndroid Build Coastguard Worker return TempFile(std::move(filename));
60*da0073e9SAndroid Build Coastguard Worker #else
61*da0073e9SAndroid Build Coastguard Worker const int fd = mkstemp(filename.data());
62*da0073e9SAndroid Build Coastguard Worker if (fd == -1) {
63*da0073e9SAndroid Build Coastguard Worker return std::nullopt;
64*da0073e9SAndroid Build Coastguard Worker }
65*da0073e9SAndroid Build Coastguard Worker return TempFile(std::move(filename), fd);
66*da0073e9SAndroid Build Coastguard Worker #endif // defined(_WIN32)
67*da0073e9SAndroid Build Coastguard Worker }
68*da0073e9SAndroid Build Coastguard Worker
69*da0073e9SAndroid Build Coastguard Worker /// Like `try_make_tempfile`, but throws an exception if a temporary file could
70*da0073e9SAndroid Build Coastguard Worker /// not be returned.
make_tempfile(std::string_view name_prefix)71*da0073e9SAndroid Build Coastguard Worker TempFile make_tempfile(std::string_view name_prefix) {
72*da0073e9SAndroid Build Coastguard Worker if (auto tempfile = try_make_tempfile(name_prefix)) {
73*da0073e9SAndroid Build Coastguard Worker return std::move(*tempfile);
74*da0073e9SAndroid Build Coastguard Worker }
75*da0073e9SAndroid Build Coastguard Worker TORCH_CHECK(false, "Error generating temporary file: ", std::strerror(errno));
76*da0073e9SAndroid Build Coastguard Worker }
77*da0073e9SAndroid Build Coastguard Worker
78*da0073e9SAndroid Build Coastguard Worker /// Attempts to return a temporary directory or returns `nullopt` if an error
79*da0073e9SAndroid Build Coastguard Worker /// occurred.
try_make_tempdir(std::string_view name_prefix)80*da0073e9SAndroid Build Coastguard Worker std::optional<TempDir> try_make_tempdir(std::string_view name_prefix) {
81*da0073e9SAndroid Build Coastguard Worker #if defined(_WIN32)
82*da0073e9SAndroid Build Coastguard Worker for (int i = 0; i < 10; i++) {
83*da0073e9SAndroid Build Coastguard Worker auto dirname = make_filename();
84*da0073e9SAndroid Build Coastguard Worker if (dirname.empty()) {
85*da0073e9SAndroid Build Coastguard Worker return std::nullopt;
86*da0073e9SAndroid Build Coastguard Worker }
87*da0073e9SAndroid Build Coastguard Worker if (CreateDirectoryA(dirname.c_str(), nullptr)) {
88*da0073e9SAndroid Build Coastguard Worker return TempDir(dirname);
89*da0073e9SAndroid Build Coastguard Worker }
90*da0073e9SAndroid Build Coastguard Worker if (GetLastError() == ERROR_SUCCESS) {
91*da0073e9SAndroid Build Coastguard Worker return std::nullopt;
92*da0073e9SAndroid Build Coastguard Worker }
93*da0073e9SAndroid Build Coastguard Worker }
94*da0073e9SAndroid Build Coastguard Worker return std::nullopt;
95*da0073e9SAndroid Build Coastguard Worker #else
96*da0073e9SAndroid Build Coastguard Worker auto filename = make_filename(name_prefix);
97*da0073e9SAndroid Build Coastguard Worker const char* dirname = mkdtemp(filename.data());
98*da0073e9SAndroid Build Coastguard Worker if (!dirname) {
99*da0073e9SAndroid Build Coastguard Worker return std::nullopt;
100*da0073e9SAndroid Build Coastguard Worker }
101*da0073e9SAndroid Build Coastguard Worker return TempDir(dirname);
102*da0073e9SAndroid Build Coastguard Worker #endif // defined(_WIN32)
103*da0073e9SAndroid Build Coastguard Worker }
104*da0073e9SAndroid Build Coastguard Worker
105*da0073e9SAndroid Build Coastguard Worker #if defined(_WIN32)
open()106*da0073e9SAndroid Build Coastguard Worker bool TempFile::open() {
107*da0073e9SAndroid Build Coastguard Worker if (fd != -1) {
108*da0073e9SAndroid Build Coastguard Worker return false;
109*da0073e9SAndroid Build Coastguard Worker }
110*da0073e9SAndroid Build Coastguard Worker auto err = _sopen_s(
111*da0073e9SAndroid Build Coastguard Worker &fd,
112*da0073e9SAndroid Build Coastguard Worker name.c_str(),
113*da0073e9SAndroid Build Coastguard Worker _O_CREAT | _O_TEMPORARY | _O_EXCL | _O_BINARY | _O_RDWR,
114*da0073e9SAndroid Build Coastguard Worker _SH_DENYNO,
115*da0073e9SAndroid Build Coastguard Worker _S_IREAD | _S_IWRITE);
116*da0073e9SAndroid Build Coastguard Worker if (err != 0) {
117*da0073e9SAndroid Build Coastguard Worker fd = -1;
118*da0073e9SAndroid Build Coastguard Worker return false;
119*da0073e9SAndroid Build Coastguard Worker }
120*da0073e9SAndroid Build Coastguard Worker return true;
121*da0073e9SAndroid Build Coastguard Worker }
122*da0073e9SAndroid Build Coastguard Worker #endif
123*da0073e9SAndroid Build Coastguard Worker
~TempFile()124*da0073e9SAndroid Build Coastguard Worker TempFile::~TempFile() {
125*da0073e9SAndroid Build Coastguard Worker if (!name.empty()) {
126*da0073e9SAndroid Build Coastguard Worker #if !defined(_WIN32)
127*da0073e9SAndroid Build Coastguard Worker if (fd >= 0) {
128*da0073e9SAndroid Build Coastguard Worker unlink(name.c_str());
129*da0073e9SAndroid Build Coastguard Worker close(fd);
130*da0073e9SAndroid Build Coastguard Worker }
131*da0073e9SAndroid Build Coastguard Worker #else
132*da0073e9SAndroid Build Coastguard Worker if (fd >= 0) {
133*da0073e9SAndroid Build Coastguard Worker _close(fd);
134*da0073e9SAndroid Build Coastguard Worker }
135*da0073e9SAndroid Build Coastguard Worker #endif
136*da0073e9SAndroid Build Coastguard Worker }
137*da0073e9SAndroid Build Coastguard Worker }
138*da0073e9SAndroid Build Coastguard Worker
~TempDir()139*da0073e9SAndroid Build Coastguard Worker TempDir::~TempDir() {
140*da0073e9SAndroid Build Coastguard Worker if (!name.empty()) {
141*da0073e9SAndroid Build Coastguard Worker #if !defined(_WIN32)
142*da0073e9SAndroid Build Coastguard Worker rmdir(name.c_str());
143*da0073e9SAndroid Build Coastguard Worker #else // defined(_WIN32)
144*da0073e9SAndroid Build Coastguard Worker RemoveDirectoryA(name.c_str());
145*da0073e9SAndroid Build Coastguard Worker #endif // defined(_WIN32)
146*da0073e9SAndroid Build Coastguard Worker }
147*da0073e9SAndroid Build Coastguard Worker }
148*da0073e9SAndroid Build Coastguard Worker
149*da0073e9SAndroid Build Coastguard Worker /// Like `try_make_tempdir`, but throws an exception if a temporary directory
150*da0073e9SAndroid Build Coastguard Worker /// could not be returned.
make_tempdir(std::string_view name_prefix)151*da0073e9SAndroid Build Coastguard Worker TempDir make_tempdir(std::string_view name_prefix) {
152*da0073e9SAndroid Build Coastguard Worker if (auto tempdir = try_make_tempdir(name_prefix)) {
153*da0073e9SAndroid Build Coastguard Worker return std::move(*tempdir);
154*da0073e9SAndroid Build Coastguard Worker }
155*da0073e9SAndroid Build Coastguard Worker #if !defined(_WIN32)
156*da0073e9SAndroid Build Coastguard Worker TORCH_CHECK(
157*da0073e9SAndroid Build Coastguard Worker false, "Error generating temporary directory: ", std::strerror(errno));
158*da0073e9SAndroid Build Coastguard Worker #else // defined(_WIN32)
159*da0073e9SAndroid Build Coastguard Worker TORCH_CHECK(false, "Error generating temporary directory");
160*da0073e9SAndroid Build Coastguard Worker #endif // defined(_WIN32)
161*da0073e9SAndroid Build Coastguard Worker }
162*da0073e9SAndroid Build Coastguard Worker } // namespace c10
163