1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef ANDROID_APEXD_APEXD_UTILS_H_ 18 #define ANDROID_APEXD_APEXD_UTILS_H_ 19 20 #include <chrono> 21 #include <cstdint> 22 #include <filesystem> 23 #include <string> 24 #include <thread> 25 #include <type_traits> 26 #include <vector> 27 28 #include <dirent.h> 29 #include <sys/stat.h> 30 #include <sys/types.h> 31 #include <sys/wait.h> 32 33 #include <android-base/chrono_utils.h> 34 #include <android-base/logging.h> 35 #include <android-base/properties.h> 36 #include <android-base/result.h> 37 #include <android-base/scopeguard.h> 38 #include <android-base/strings.h> 39 #include <cutils/android_reboot.h> 40 #include <selinux/android.h> 41 42 #include "apex_constants.h" 43 44 namespace android { 45 namespace apex { 46 47 inline int WaitChild(pid_t pid) { 48 int status; 49 pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); 50 51 if (got_pid != pid) { 52 PLOG(WARNING) << "waitpid failed: wanted " << pid << ", got " << got_pid; 53 return 1; 54 } 55 56 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 57 return 0; 58 } else { 59 return status; 60 } 61 } 62 63 inline android::base::Result<void> ForkAndRun( 64 const std::vector<std::string>& args) { 65 LOG(DEBUG) << "Forking : " << android::base::Join(args, " "); 66 std::vector<const char*> argv; 67 argv.resize(args.size() + 1, nullptr); 68 std::transform(args.begin(), args.end(), argv.begin(), 69 [](const std::string& in) { return in.c_str(); }); 70 71 pid_t pid = fork(); 72 if (pid == -1) { 73 // Fork failed. 74 return android::base::ErrnoError() << "Unable to fork"; 75 } 76 77 if (pid == 0) { 78 execv(argv[0], const_cast<char**>(argv.data())); 79 PLOG(ERROR) << "execv failed"; 80 _exit(1); 81 } 82 83 int rc = WaitChild(pid); 84 if (rc != 0) { 85 return android::base::Error() << "Failed run: status=" << rc; 86 } 87 return {}; 88 } 89 90 template <typename Fn> 91 android::base::Result<void> WalkDir(const std::string& path, Fn fn) { 92 namespace fs = std::filesystem; 93 std::error_code ec; 94 auto it = fs::directory_iterator(path, ec); 95 auto end = fs::directory_iterator(); 96 while (!ec && it != end) { 97 fn(*it); 98 it.increment(ec); 99 } 100 if (ec) { 101 return android::base::Error() 102 << "Can't open " << path << " for reading : " << ec.message(); 103 } 104 return {}; 105 } 106 107 template <typename FilterFn> 108 android::base::Result<std::vector<std::string>> ReadDir(const std::string& path, 109 FilterFn fn) { 110 namespace fs = std::filesystem; 111 112 std::vector<std::string> ret; 113 auto status = WalkDir(path, [&](const fs::directory_entry& entry) { 114 if (fn(entry)) { 115 ret.push_back(entry.path()); 116 } 117 }); 118 if (!status.ok()) { 119 return status.error(); 120 } 121 return ret; 122 } 123 124 inline bool IsEmptyDirectory(const std::string& path) { 125 auto res = ReadDir(path, [](auto _) { return true; }); 126 return res.ok() && res->empty(); 127 } 128 129 inline android::base::Result<void> CreateDirIfNeeded(const std::string& path, 130 mode_t mode) { 131 struct stat stat_data; 132 133 if (stat(path.c_str(), &stat_data) != 0) { 134 if (errno == ENOENT) { 135 if (mkdir(path.c_str(), mode) != 0) { 136 return android::base::ErrnoError() << "Could not mkdir " << path; 137 } 138 } else { 139 return android::base::ErrnoError() << "Could not stat " << path; 140 } 141 } else { 142 if (!S_ISDIR(stat_data.st_mode)) { 143 return android::base::Error() 144 << path << " exists and is not a directory."; 145 } 146 } 147 148 // Need to manually call chmod because mkdir will create a folder with 149 // permissions mode & ~umask. 150 if (chmod(path.c_str(), mode) != 0) { 151 return android::base::ErrnoError() << "Could not chmod " << path; 152 } 153 154 return {}; 155 } 156 157 inline android::base::Result<void> DeleteDirContent(const std::string& path) { 158 auto files = ReadDir(path, [](auto _) { return true; }); 159 if (!files.ok()) { 160 return android::base::Error() 161 << "Failed to delete " << path << " : " << files.error(); 162 } 163 for (const std::string& file : *files) { 164 std::error_code ec; 165 std::filesystem::remove_all(file, ec); 166 if (ec) { 167 return android::base::Error() 168 << "Failed to delete path " << file << " : " << ec.message(); 169 } 170 } 171 return {}; 172 } 173 174 inline android::base::Result<void> DeleteDir(const std::string& path) { 175 namespace fs = std::filesystem; 176 std::error_code ec; 177 fs::remove_all(path, ec); 178 if (ec) { 179 return android::base::Error() 180 << "Failed to delete path " << path << " : " << ec.message(); 181 } 182 return {}; 183 } 184 185 inline android::base::Result<bool> PathExists(const std::string& path) { 186 namespace fs = std::filesystem; 187 188 std::error_code ec; 189 if (!fs::exists(fs::path(path), ec)) { 190 if (ec) { 191 return android::base::Error() 192 << "Failed to access " << path << " : " << ec.message(); 193 } else { 194 return false; 195 } 196 } 197 return true; 198 } 199 200 inline void Reboot() { 201 LOG(INFO) << "Rebooting device"; 202 if (android_reboot(ANDROID_RB_RESTART2, 0, nullptr) != 0) { 203 LOG(ERROR) << "Failed to reboot device"; 204 } 205 } 206 207 inline android::base::Result<void> WaitForFile( 208 const std::string& path, std::chrono::nanoseconds timeout) { 209 android::base::Timer t; 210 bool has_slept = false; 211 while (t.duration() < timeout) { 212 struct stat sb; 213 if (stat(path.c_str(), &sb) != -1) { 214 if (has_slept) { 215 LOG(INFO) << "wait for '" << path << "' took " << t; 216 } 217 return {}; 218 } 219 std::this_thread::sleep_for(5ms); 220 has_slept = true; 221 } 222 return android::base::ErrnoError() 223 << "wait for '" << path << "' timed out and took " << t; 224 } 225 226 inline android::base::Result<std::vector<std::string>> GetSubdirs( 227 const std::string& path) { 228 namespace fs = std::filesystem; 229 auto filter_fn = [](const std::filesystem::directory_entry& entry) { 230 std::error_code ec; 231 bool result = entry.is_directory(ec); 232 if (ec) { 233 LOG(ERROR) << "Failed to check is_directory : " << ec.message(); 234 return false; 235 } 236 return result; 237 }; 238 return ReadDir(path, filter_fn); 239 } 240 241 inline android::base::Result<std::vector<std::string>> GetDeUserDirs() { 242 return GetSubdirs(kDeNDataDir); 243 } 244 245 inline android::base::Result<std::vector<std::string>> FindFilesBySuffix( 246 const std::string& path, const std::vector<std::string>& suffix_list) { 247 auto filter_fn = 248 [&suffix_list](const std::filesystem::directory_entry& entry) { 249 for (const std::string& suffix : suffix_list) { 250 std::error_code ec; 251 auto name = entry.path().filename().string(); 252 if (entry.is_regular_file(ec) && 253 android::base::EndsWith(name, suffix)) { 254 return true; // suffix matches, take. 255 } 256 } 257 return false; 258 }; 259 return ReadDir(path, filter_fn); 260 } 261 262 inline android::base::Result<std::vector<std::string>> FindApexes( 263 const std::vector<std::string>& paths) { 264 std::vector<std::string> result; 265 for (const auto& path : paths) { 266 auto exist = PathExists(path); 267 if (!exist.ok()) { 268 return exist.error(); 269 } 270 if (!*exist) continue; 271 272 const auto& apexes = FindFilesBySuffix(path, {kApexPackageSuffix}); 273 if (!apexes.ok()) { 274 return apexes; 275 } 276 277 result.insert(result.end(), apexes->begin(), apexes->end()); 278 } 279 return result; 280 } 281 282 // Returns first path between |first_dir| and |second_dir| that correspond to a 283 // existing directory. Returns error if neither |first_dir| nor |second_dir| 284 // correspond to an existing directory. 285 inline android::base::Result<std::string> FindFirstExistingDirectory( 286 const std::string& first_dir, const std::string& second_dir) { 287 struct stat stat_buf; 288 if (stat(first_dir.c_str(), &stat_buf) != 0) { 289 PLOG(WARNING) << "Failed to stat " << first_dir; 290 if (stat(second_dir.c_str(), &stat_buf) != 0) { 291 return android::base::ErrnoError() << "Failed to stat " << second_dir; 292 } 293 if (!S_ISDIR(stat_buf.st_mode)) { 294 return android::base::Error() << second_dir << " is not a directory"; 295 } 296 return second_dir; 297 } 298 299 if (S_ISDIR(stat_buf.st_mode)) { 300 return first_dir; 301 } 302 LOG(WARNING) << first_dir << " is not a directory"; 303 304 if (stat(second_dir.c_str(), &stat_buf) != 0) { 305 return android::base::ErrnoError() << "Failed to stat " << second_dir; 306 } 307 if (!S_ISDIR(stat_buf.st_mode)) { 308 return android::base::Error() << second_dir << " is not a directory"; 309 } 310 return second_dir; 311 } 312 313 // Copies all entries under |from| directory to |to| directory, and then them. 314 // Leaving |from| empty. 315 inline android::base::Result<void> MoveDir(const std::string& from, 316 const std::string& to) { 317 struct stat stat_buf; 318 if (stat(to.c_str(), &stat_buf) != 0) { 319 return android::base::ErrnoError() << "Failed to stat " << to; 320 } 321 if (!S_ISDIR(stat_buf.st_mode)) { 322 return android::base::Error() << to << " is not a directory"; 323 } 324 325 namespace fs = std::filesystem; 326 std::error_code ec; 327 auto it = fs::directory_iterator(from, ec); 328 if (ec) { 329 return android::base::Error() 330 << "Can't read " << from << " : " << ec.message(); 331 } 332 333 for (const auto& end = fs::directory_iterator(); it != end;) { 334 auto from_path = it->path(); 335 it.increment(ec); 336 if (ec) { 337 return android::base::Error() 338 << "Can't read " << from << " : " << ec.message(); 339 } 340 auto to_path = to / from_path.filename(); 341 fs::copy(from_path, to_path, fs::copy_options::recursive, ec); 342 if (ec) { 343 return android::base::Error() << "Failed to copy " << from_path << " to " 344 << to_path << " : " << ec.message(); 345 } 346 fs::remove_all(from_path, ec); 347 if (ec) { 348 return android::base::Error() 349 << "Failed to delete " << from_path << " : " << ec.message(); 350 } 351 } 352 return {}; 353 } 354 355 inline android::base::Result<uintmax_t> GetFileSize( 356 const std::string& file_path) { 357 std::error_code ec; 358 auto value = std::filesystem::file_size(file_path, ec); 359 if (ec) { 360 return android::base::Error() << "Failed to get file size of " << file_path 361 << " : " << ec.message(); 362 } 363 364 return value; 365 } 366 367 inline android::base::Result<void> RestoreconPath(const std::string& path) { 368 unsigned int seflags = SELINUX_ANDROID_RESTORECON_RECURSE; 369 if (selinux_android_restorecon(path.c_str(), seflags) < 0) { 370 return android::base::ErrnoError() << "Failed to restorecon " << path; 371 } 372 return {}; 373 } 374 375 } // namespace apex 376 } // namespace android 377 378 #endif // ANDROID_APEXD_APEXD_UTILS_H_ 379