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