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