• 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 "aemu/base/Compiler.h"
18 #include "aemu/base/StringFormat.h"
19 #include "aemu/base/files/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 #if defined(_MSC_VER) || defined(_WIN32)
43 #include <rpc.h>
44 #endif
45 
46 namespace android {
47 namespace base {
48 
49 // A class used to model a temporary directory used during testing.
50 // Usage is simple:
51 //
52 //      {
53 //        TestTempDir myDir("my_test");   // creates new temp directory.
54 //        ASSERT_TRUE(myDir.path());      // NULL if error during creation.
55 //        ... write files into directory path myDir->path()
56 //        ... do your test
57 //      }   // destructor removes temp directory and all files under it.
58 
59 #ifdef _MSC_VER
60 namespace uuid {
generate_uuid_v4()61     static inline std::string generate_uuid_v4() {
62         UUID uuid{};
63         UuidCreate(&uuid);
64         RPC_CSTR str{};
65         UuidToStringA(&uuid, &str);
66         std::string ret = reinterpret_cast<char*>(str);
67         RpcStringFreeA(&str);
68         return ret;
69     }
70 }
71 #endif
72 
73 class TestTempDir {
74 public:
75     // Create new instance. This also tries to create a new temporary
76     // directory. |debugPrefix| is an optional name prefix and can be NULL.
TestTempDir(const std::string & debugName)77     TestTempDir(const std::string& debugName) {
78         std::string temp_dir = getTempPath();
79         if (!debugName.empty()) {
80             temp_dir += debugName;
81             temp_dir += ".";
82         }
83 
84 #if defined(_MSC_VER) || defined(_WIN32)
85         temp_dir += uuid::generate_uuid_v4();
86         if (android_mkdir(temp_dir.c_str(), 0755) != 0) {
87             fprintf(stderr, "Unable to create %s, falling back to tmp dir",
88                     temp_dir.c_str());
89             temp_dir = getTempPath();
90         }
91         auto parts = PathUtils::decompose(temp_dir);
92         mPath = PathUtils::recompose(parts);
93 #else
94         temp_dir += "XXXXXX";
95         if (mkdtemp(&temp_dir[0])) {
96             // Fix any Win32/Linux naming issues
97             auto parts = PathUtils::decompose(temp_dir);
98             mPath = PathUtils::recompose(parts);
99         } else {
100             fprintf(stderr, "%s: mkdtemp failed!\n", __func__);
101         }
102 #endif
103     }
104 
105     // Return the path to the temporary directory, or NULL if it could not
106     // be created for some reason.
path()107     const char* path() const { return mPath.size() ? mPath.c_str() : NULL; }
108 
109     // Return the path as a string. It will be empty if the directory could
110     // not be created for some reason.
pathString()111     const std::string& pathString() const { return mPath; }
112 
113     // Destroy instance, and removes the temporary directory and all files
114     // inside it.
~TestTempDir()115     ~TestTempDir() {
116         if (mPath.size()) {
117             DeleteRecursive(mPath);
118         }
119     }
120 
121     // Create the path of a directory entry under the temporary directory.
makeSubPath(const std::string & subpath)122     std::string makeSubPath(const std::string& subpath) {
123         return StringFormat("%s/%s", mPath.c_str(), subpath.c_str());
124     }
125 
126     // Create an empty directory under the temporary directory.
makeSubDir(const std::string & subdir)127     bool makeSubDir(const std::string& subdir) {
128         std::string path = makeSubPath(subdir);
129         if (android_mkdir(path.c_str(), 0755) < 0) {
130             // PLOG(ERROR) << "Can't create " << path.c_str() << ": ";
131             return false;
132         }
133         return true;
134     }
135 
136     // Create an empty file under the temporary directory.
makeSubFile(const std::string & file)137     bool makeSubFile(const std::string& file) {
138         std::string path = makeSubPath(file);
139         int fd = ::android_open(path.c_str(), O_WRONLY | O_CREAT, 0744);
140         if (fd < 0) {
141             // PLOG(ERROR) << "Can't create " << path.c_str() << ": ";
142             return false;
143         }
144         ::close(fd);
145         return true;
146     }
147 
148 private:
149     DISALLOW_COPY_AND_ASSIGN(TestTempDir);
150 
DeleteRecursive(const std::string & path)151     void DeleteRecursive(const std::string& path) {
152 #ifdef _WIN32
153         std::filesystem::remove_all(path);
154 #else
155         // First remove any files in the dir
156         DIR* dir = opendir(path.c_str());
157         if (!dir) {
158             return;
159         }
160 
161         dirent* entry;
162         while ((entry = readdir(dir)) != NULL) {
163             if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
164                 continue;
165             }
166             std::string entry_path = StringFormat("%s/%s", path, entry->d_name);
167 #ifdef _WIN32
168             struct _stati64 stats;
169             android_lstat(entry_path.c_str(),
170                           reinterpret_cast<struct stat*>(&stats));
171 #else
172             struct stat stats;
173             android_lstat(entry_path.c_str(), &stats);
174 #endif
175 
176             if (S_ISDIR(stats.st_mode)) {
177                 DeleteRecursive(entry_path);
178             } else {
179                 android_unlink(entry_path.c_str());
180             }
181         }
182         closedir(dir);
183         android_rmdir(path.c_str());
184 #endif
185     }
186 
187 #ifdef _WIN32
getTempPath()188     std::string getTempPath() {
189         return std::filesystem::temp_directory_path().string();
190     }
191 
mkdtemp(char * path)192     char* mkdtemp(char* path) {
193         char tmpnamBuf[2048];
194         if (!std::tmpnam(tmpnamBuf)) return nullptr;
195 
196         char* path_end = tmpnamBuf + ::strlen(path);
197 
198         // Loop. On each iteration, replace the XXXXXX suffix with a random
199         // number.
200         const size_t kSuffixLen = 6U;
201         for (int tries = 128; tries > 0; tries--) {
202             int random = rand() % 1000000;
203 
204             snprintf(path_end - kSuffixLen, kSuffixLen + 1, "%0d", random);
205             if (android_mkdir(path, 0755) == 0) {
206                 return path;  // Success
207             }
208             if (errno != EEXIST) {
209                 return NULL;
210             }
211         }
212         return NULL;
213     }
214 #else  // !_WIN32
getTempPath()215     std::string getTempPath() {
216         std::string result;
217         // Only check TMPDIR if we're not root.
218         if (getuid() != 0 && getgid() != 0) {
219             const char* tmpdir = ::getenv("TMPDIR");
220             if (tmpdir && tmpdir[0]) {
221                 result = tmpdir;
222             }
223         }
224         // Otherwise use P_tmpdir, which defaults to /tmp
225         if (result.empty()) {
226 #ifndef P_tmpdir
227 #define P_tmpdir "/tmp"
228 #endif
229             result = P_tmpdir;
230         }
231         // Check that it exists and is a directory.
232         struct stat st;
233         int ret = android_stat(result.c_str(), &st);
234         if (ret < 0 || !S_ISDIR(st.st_mode)) {
235             fprintf(stderr, "%s: Can't find temp path %s\n", __func__,
236                     result.c_str());
237             abort();
238             // LOG(FATAL) << "Can't find temporary path: [" << result.c_str()
239             //            << "]";
240         }
241         // Ensure there is a trailing directory separator.
242         if (result.size() && result[result.size() - 1] != '/') {
243             result += '/';
244         }
245         return result;
246     }
247 #endif  // !_WIN32
248 
249     std::string mPath;
250 };
251 
252 }  // namespace base
253 }  // namespace android
254