• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2015 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #pragma once
16 
17 #include "base/Compiler.h"
18 #include "base/StringFormat.h"
19 #include "base/PathUtils.h"
20 
21 #include "file_io.h"
22 
23 #ifdef _WIN32
24 #include <windows.h>
25 #include <random>
26 #include <sstream>
27 #include <filesystem>
28 #undef ERROR
29 #include <errno.h>
30 #include <stdio.h>
31 #endif
32 
33 #include <fcntl.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/stat.h>
37 #ifndef _MSC_VER
38 #include <dirent.h>
39 #include <unistd.h>
40 #endif
41 
42 namespace android {
43 namespace base {
44 
45 // A class used to model a temporary directory used during testing.
46 // Usage is simple:
47 //
48 //      {
49 //        TestTempDir myDir("my_test");   // creates new temp directory.
50 //        ASSERT_TRUE(myDir.path());      // NULL if error during creation.
51 //        ... write files into directory path myDir->path()
52 //        ... do your test
53 //      }   // destructor removes temp directory and all files under it.
54 
55 #ifdef _MSC_VER
56 // From https://stackoverflow.com/questions/24365331/how-can-i-generate-uuid-in-c-without-using-boost-library
57 namespace uuid {
58     static std::random_device              rd;
59     static std::mt19937                    gen(rd());
60     static std::uniform_int_distribution<> dis(0, 15);
61     static std::uniform_int_distribution<> dis2(8, 11);
62 
generate_uuid_v4()63     static inline std::string generate_uuid_v4() {
64         std::stringstream ss;
65         int i;
66         ss << std::hex;
67         for (i = 0; i < 8; i++) {
68             ss << dis(gen);
69         }
70         ss << "-";
71         for (i = 0; i < 4; i++) {
72             ss << dis(gen);
73         }
74         ss << "-4";
75         for (i = 0; i < 3; i++) {
76             ss << dis(gen);
77         }
78         ss << "-";
79         ss << dis2(gen);
80         for (i = 0; i < 3; i++) {
81             ss << dis(gen);
82         }
83         ss << "-";
84         for (i = 0; i < 12; i++) {
85             ss << dis(gen);
86         };
87         return ss.str();
88     }
89 }
90 #endif
91 
92 class TestTempDir {
93 public:
94     // Create new instance. This also tries to create a new temporary
95     // directory. |debugPrefix| is an optional name prefix and can be NULL.
TestTempDir(const std::string & debugName)96     TestTempDir(const std::string& debugName) {
97         std::string temp_dir = getTempPath();
98         if (!debugName.empty()) {
99             temp_dir += debugName;
100             temp_dir += ".";
101         }
102 
103 #if defined(_MSC_VER) || defined(_WIN32)
104         temp_dir += uuid::generate_uuid_v4();
105         if (android_mkdir(temp_dir.c_str(), 0755) != 0) {
106             fprintf(stderr, "Unable to create %s, falling back to tmp dir",
107                     temp_dir.c_str());
108             temp_dir = getTempPath();
109         }
110         auto parts = PathUtils::decompose(temp_dir);
111         mPath = PathUtils::recompose(parts);
112 #else
113         temp_dir += "XXXXXX";
114         if (mkdtemp(&temp_dir[0])) {
115             // Fix any Win32/Linux naming issues
116             auto parts = PathUtils::decompose(temp_dir);
117             mPath = PathUtils::recompose(parts);
118         } else {
119             fprintf(stderr, "%s: mkdtemp failed!\n", __func__);
120         }
121 #endif
122     }
123 
124     // Return the path to the temporary directory, or NULL if it could not
125     // be created for some reason.
path()126     const char* path() const { return mPath.size() ? mPath.c_str() : NULL; }
127 
128     // Return the path as a string. It will be empty if the directory could
129     // not be created for some reason.
pathString()130     const std::string& pathString() const { return mPath; }
131 
132     // Destroy instance, and removes the temporary directory and all files
133     // inside it.
~TestTempDir()134     ~TestTempDir() {
135         if (mPath.size()) {
136             DeleteRecursive(mPath);
137         }
138     }
139 
140     // Create the path of a directory entry under the temporary directory.
makeSubPath(const std::string & subpath)141     std::string makeSubPath(const std::string& subpath) {
142         return StringFormat("%s/%s", mPath.c_str(), subpath.c_str());
143     }
144 
145     // Create an empty directory under the temporary directory.
makeSubDir(const std::string & subdir)146     bool makeSubDir(const std::string& subdir) {
147         std::string path = makeSubPath(subdir);
148         if (android_mkdir(path.c_str(), 0755) < 0) {
149             // PLOG(ERROR) << "Can't create " << path.c_str() << ": ";
150             return false;
151         }
152         return true;
153     }
154 
155     // Create an empty file under the temporary directory.
makeSubFile(const std::string & file)156     bool makeSubFile(const std::string& file) {
157         std::string path = makeSubPath(file);
158         int fd = ::android_open(path.c_str(), O_WRONLY | O_CREAT, 0744);
159         if (fd < 0) {
160             // PLOG(ERROR) << "Can't create " << path.c_str() << ": ";
161             return false;
162         }
163         ::close(fd);
164         return true;
165     }
166 
167 private:
168     DISALLOW_COPY_AND_ASSIGN(TestTempDir);
169 
DeleteRecursive(const std::string & path)170     void DeleteRecursive(const std::string& path) {
171 #ifdef _WIN32
172         std::filesystem::remove_all(path);
173 #else
174         // First remove any files in the dir
175         DIR* dir = opendir(path.c_str());
176         if (!dir) {
177             return;
178         }
179 
180         dirent* entry;
181         while ((entry = readdir(dir)) != NULL) {
182             if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
183                 continue;
184             }
185             std::string entry_path = StringFormat("%s/%s", path, entry->d_name);
186 #ifdef _WIN32
187             struct _stati64 stats;
188             android_lstat(entry_path.c_str(),
189                           reinterpret_cast<struct stat*>(&stats));
190 #else
191             struct stat stats;
192             android_lstat(entry_path.c_str(), &stats);
193 #endif
194 
195             if (S_ISDIR(stats.st_mode)) {
196                 DeleteRecursive(entry_path);
197             } else {
198                 android_unlink(entry_path.c_str());
199             }
200         }
201         closedir(dir);
202         android_rmdir(path.c_str());
203 #endif
204     }
205 
206 #ifdef _WIN32
getTempPath()207     std::string getTempPath() {
208         return std::filesystem::temp_directory_path().string();
209     }
210 
mkdtemp(char * path)211     char* mkdtemp(char* path) {
212         char tmpnamBuf[2048];
213         if (!std::tmpnam(tmpnamBuf)) return nullptr;
214 
215         char* path_end = tmpnamBuf + ::strlen(path);
216 
217         // Loop. On each iteration, replace the XXXXXX suffix with a random
218         // number.
219         const size_t kSuffixLen = 6U;
220         for (int tries = 128; tries > 0; tries--) {
221             int random = rand() % 1000000;
222 
223             snprintf(path_end - kSuffixLen, kSuffixLen + 1, "%0d", random);
224             if (android_mkdir(path, 0755) == 0) {
225                 return path;  // Success
226             }
227             if (errno != EEXIST) {
228                 return NULL;
229             }
230         }
231         return NULL;
232     }
233 #else  // !_WIN32
getTempPath()234     std::string getTempPath() {
235         std::string result;
236         // Only check TMPDIR if we're not root.
237         if (getuid() != 0 && getgid() != 0) {
238             const char* tmpdir = ::getenv("TMPDIR");
239             if (tmpdir && tmpdir[0]) {
240                 result = tmpdir;
241             }
242         }
243         // Otherwise use P_tmpdir, which defaults to /tmp
244         if (result.empty()) {
245 #ifndef P_tmpdir
246 #define P_tmpdir "/tmp"
247 #endif
248             result = P_tmpdir;
249         }
250         // Check that it exists and is a directory.
251         struct stat st;
252         int ret = android_stat(result.c_str(), &st);
253         if (ret < 0 || !S_ISDIR(st.st_mode)) {
254             fprintf(stderr, "%s: Can't find temp path %s\n", __func__,
255                     result.c_str());
256             abort();
257             // LOG(FATAL) << "Can't find temporary path: [" << result.c_str()
258             //            << "]";
259         }
260         // Ensure there is a trailing directory separator.
261         if (result.size() && result[result.size() - 1] != '/') {
262             result += '/';
263         }
264         return result;
265     }
266 #endif  // !_WIN32
267 
268     std::string mPath;
269 };
270 
271 }  // namespace base
272 }  // namespace android
273