blob: 2759f9aaa747ef451e75073469770c2eae5436fd [file] [log] [blame]
Andreas Gampee1a40392018-11-30 09:47:17 -08001/*
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
Jiyong Parkd8a82ce2019-02-25 02:21:18 +090020#include <chrono>
Nikita Ioffea82b0a82019-02-15 18:59:47 +000021#include <filesystem>
Andreas Gampee1a40392018-11-30 09:47:17 -080022#include <string>
Jiyong Parkd8a82ce2019-02-25 02:21:18 +090023#include <thread>
Andreas Gampee1a40392018-11-30 09:47:17 -080024#include <vector>
25
Martijn Coenen610909b2019-01-18 13:49:38 +010026#include <dirent.h>
Nikita Ioffea8453da2019-01-30 21:29:13 +000027#include <sys/stat.h>
Andreas Gampee1a40392018-11-30 09:47:17 -080028#include <sys/types.h>
29#include <sys/wait.h>
30
Jiyong Parkd8a82ce2019-02-25 02:21:18 +090031#include <android-base/chrono_utils.h>
Andreas Gampee1a40392018-11-30 09:47:17 -080032#include <android-base/logging.h>
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +010033#include <android-base/result.h>
Oli Lan013148f2020-01-08 13:36:03 +000034#include <android-base/scopeguard.h>
Nikita Ioffe264c4212019-09-13 16:30:17 +010035#include <android-base/strings.h>
Nikita Ioffe31dc34d2019-02-23 15:59:04 +000036#include <cutils/android_reboot.h>
Andreas Gampee1a40392018-11-30 09:47:17 -080037
Oli Lan042fbcf2020-01-17 11:14:16 +000038#include "apex_constants.h"
Andreas Gampee1a40392018-11-30 09:47:17 -080039#include "string_log.h"
40
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +010041using android::base::ErrnoError;
42using android::base::Error;
43using android::base::Result;
44
Andreas Gampee1a40392018-11-30 09:47:17 -080045namespace android {
46namespace apex {
47
48inline int WaitChild(pid_t pid) {
49 int status;
50 pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
51
52 if (got_pid != pid) {
53 PLOG(WARNING) << "waitpid failed: wanted " << pid << ", got " << got_pid;
54 return 1;
55 }
56
57 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
58 return 0;
59 } else {
60 return status;
61 }
62}
63
Nikita Ioffe264c4212019-09-13 16:30:17 +010064// TODO(ioffe): change to Result<void>?
Andreas Gampee1a40392018-11-30 09:47:17 -080065inline int ForkAndRun(const std::vector<std::string>& args,
66 std::string* error_msg) {
Nikita Ioffe264c4212019-09-13 16:30:17 +010067 LOG(DEBUG) << "Forking : " << android::base::Join(args, " ");
Andreas Gampee1a40392018-11-30 09:47:17 -080068 std::vector<const char*> argv;
69 argv.resize(args.size() + 1, nullptr);
70 std::transform(args.begin(), args.end(), argv.begin(),
71 [](const std::string& in) { return in.c_str(); });
72
73 // 3) Fork.
74 pid_t pid = fork();
75 if (pid == -1) {
76 // Fork failed.
77 *error_msg = PStringLog() << "Unable to fork";
78 return -1;
79 }
80
81 if (pid == 0) {
82 execv(argv[0], const_cast<char**>(argv.data()));
83 PLOG(ERROR) << "execv failed";
84 _exit(1);
85 }
86
87 int rc = WaitChild(pid);
88 if (rc != 0) {
89 *error_msg = StringLog() << "Failed run: status=" << rc;
90 }
91 return rc;
92}
93
Jooyung Han80d8c862019-05-03 00:42:31 +090094template <typename Fn>
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +010095Result<void> WalkDir(const std::string& path, Fn fn) {
Mohammad Samiul Islamd428a182019-03-19 14:36:24 +000096 namespace fs = std::filesystem;
Jooyung Han50011d52019-04-24 10:11:56 +090097 std::error_code ec;
Jooyung Han50011d52019-04-24 10:11:56 +090098 auto it = fs::directory_iterator(path, ec);
99 auto end = fs::directory_iterator();
100 while (!ec && it != end) {
Jooyung Han80d8c862019-05-03 00:42:31 +0900101 fn(*it);
Jooyung Han50011d52019-04-24 10:11:56 +0900102 it.increment(ec);
103 }
Martijn Coenen320baff2019-05-01 09:20:42 +0200104 if (ec) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100105 return Error() << "Can't open " << path
106 << " for reading : " << ec.message();
Martijn Coenen320baff2019-05-01 09:20:42 +0200107 }
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100108 return {};
Jooyung Han80d8c862019-05-03 00:42:31 +0900109}
110
111template <typename FilterFn>
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100112Result<std::vector<std::string>> ReadDir(const std::string& path, FilterFn fn) {
Jooyung Han80d8c862019-05-03 00:42:31 +0900113 namespace fs = std::filesystem;
Jooyung Han80d8c862019-05-03 00:42:31 +0900114
115 std::vector<std::string> ret;
116 auto status = WalkDir(path, [&](const fs::directory_entry& entry) {
117 if (fn(entry)) {
118 ret.push_back(entry.path());
119 }
120 });
Bernie Innocentid04d5d02020-02-06 22:01:51 +0900121 if (!status.ok()) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100122 return status.error();
Jooyung Han80d8c862019-05-03 00:42:31 +0900123 }
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100124 return ret;
Jooyung Han80d8c862019-05-03 00:42:31 +0900125}
Martijn Coenen610909b2019-01-18 13:49:38 +0100126
Jiyong Park715e23d2019-02-22 22:14:37 +0900127inline bool IsEmptyDirectory(const std::string& path) {
Mohammad Samiul Islamd428a182019-03-19 14:36:24 +0000128 auto res = ReadDir(path, [](auto _) { return true; });
Bernie Innocentid04d5d02020-02-06 22:01:51 +0900129 return res.ok() && res->empty();
Jiyong Park715e23d2019-02-22 22:14:37 +0900130}
131
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100132inline Result<void> createDirIfNeeded(const std::string& path, mode_t mode) {
Nikita Ioffea8453da2019-01-30 21:29:13 +0000133 struct stat stat_data;
134
135 if (stat(path.c_str(), &stat_data) != 0) {
136 if (errno == ENOENT) {
137 if (mkdir(path.c_str(), mode) != 0) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100138 return ErrnoError() << "Could not mkdir " << path;
Nikita Ioffea8453da2019-01-30 21:29:13 +0000139 }
140 } else {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100141 return ErrnoError() << "Could not stat " << path;
Nikita Ioffea8453da2019-01-30 21:29:13 +0000142 }
143 } else {
144 if (!S_ISDIR(stat_data.st_mode)) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100145 return Error() << path << " exists and is not a directory.";
Nikita Ioffea8453da2019-01-30 21:29:13 +0000146 }
147 }
148
149 // Need to manually call chmod because mkdir will create a folder with
150 // permissions mode & ~umask.
151 if (chmod(path.c_str(), mode) != 0) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100152 return ErrnoError() << "Could not chmod " << path;
Nikita Ioffea8453da2019-01-30 21:29:13 +0000153 }
154
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100155 return {};
Nikita Ioffea8453da2019-01-30 21:29:13 +0000156}
157
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100158inline Result<void> DeleteDirContent(const std::string& path) {
Mohammad Samiul Islamd428a182019-03-19 14:36:24 +0000159 auto files = ReadDir(path, [](auto _) { return true; });
Bernie Innocentid04d5d02020-02-06 22:01:51 +0900160 if (!files.ok()) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100161 return Error() << "Failed to delete " << path << " : " << files.error();
Nikita Ioffea82b0a82019-02-15 18:59:47 +0000162 }
163 for (const std::string& file : *files) {
164 if (unlink(file.c_str()) != 0) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100165 return ErrnoError() << "Failed to delete " << file;
Nikita Ioffea82b0a82019-02-15 18:59:47 +0000166 }
167 }
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100168 return {};
Nikita Ioffea82b0a82019-02-15 18:59:47 +0000169}
170
Oli Lanf2083e32020-01-30 10:25:49 +0000171inline Result<void> DeleteDir(const std::string& path) {
172 namespace fs = std::filesystem;
173 std::error_code ec;
174 fs::remove_all(path, ec);
175 if (ec) {
176 return Error() << "Failed to delete path " << path << " : " << ec.message();
177 }
178 return {};
179}
180
Oli Lan2d59dfa2020-01-14 20:25:09 +0000181inline Result<ino_t> get_path_inode(const std::string& path) {
182 struct stat buf;
183 memset(&buf, 0, sizeof(buf));
184 if (stat(path.c_str(), &buf) != 0) {
185 return ErrnoError() << "Failed to stat " << path;
186 } else {
187 return buf.st_ino;
188 }
189}
190
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100191inline Result<bool> PathExists(const std::string& path) {
Nikita Ioffe9ae986a2019-02-18 22:39:27 +0000192 namespace fs = std::filesystem;
193
194 std::error_code ec;
195 if (!fs::exists(fs::path(path), ec)) {
196 if (ec) {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100197 return Error() << "Failed to access " << path << " : " << ec.message();
Nikita Ioffe9ae986a2019-02-18 22:39:27 +0000198 } else {
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100199 return false;
Nikita Ioffe9ae986a2019-02-18 22:39:27 +0000200 }
201 }
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100202 return true;
Nikita Ioffe9ae986a2019-02-18 22:39:27 +0000203}
204
Nikita Ioffe31dc34d2019-02-23 15:59:04 +0000205inline void Reboot() {
206 LOG(INFO) << "Rebooting device";
207 if (android_reboot(ANDROID_RB_RESTART2, 0, nullptr) != 0) {
208 LOG(ERROR) << "Failed to reboot device";
209 }
210}
211
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100212inline Result<void> WaitForFile(const std::string& path,
213 std::chrono::nanoseconds timeout) {
Jiyong Parkd8a82ce2019-02-25 02:21:18 +0900214 android::base::Timer t;
215 bool has_slept = false;
216 while (t.duration() < timeout) {
217 struct stat sb;
218 if (stat(path.c_str(), &sb) != -1) {
219 if (has_slept) {
220 LOG(INFO) << "wait for '" << path << "' took " << t;
221 }
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100222 return {};
Jiyong Parkd8a82ce2019-02-25 02:21:18 +0900223 }
224 std::this_thread::sleep_for(5ms);
225 has_slept = true;
226 }
Mohammad Samiul Islambd6ab0f2019-06-20 15:55:27 +0100227 return ErrnoError() << "wait for '" << path << "' timed out and took " << t;
Jiyong Parkd8a82ce2019-02-25 02:21:18 +0900228}
229
Oli Lan2993ccc2020-03-06 18:06:40 +0000230inline Result<std::vector<std::string>> GetSubdirs(const std::string& path) {
Oli Lan042fbcf2020-01-17 11:14:16 +0000231 namespace fs = std::filesystem;
232 auto filter_fn = [](const std::filesystem::directory_entry& entry) {
233 std::error_code ec;
234 bool result = entry.is_directory(ec);
235 if (ec) {
236 LOG(ERROR) << "Failed to check is_directory : " << ec.message();
237 return false;
238 }
239 return result;
240 };
Oli Lan2993ccc2020-03-06 18:06:40 +0000241 return ReadDir(path, filter_fn);
242}
243
244inline Result<std::vector<std::string>> GetDeUserDirs() {
245 return GetSubdirs(kDeNDataDir);
Oli Lan042fbcf2020-01-17 11:14:16 +0000246}
247
Andreas Gampee1a40392018-11-30 09:47:17 -0800248} // namespace apex
249} // namespace android
250
251#endif // ANDROID_APEXD_APEXD_UTILS_H_