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 <filesystem>
22 #include <string>
23 #include <thread>
24 #include <type_traits>
25 #include <vector>
26
27 #include <dirent.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/wait.h>
31
32 #include <android-base/chrono_utils.h>
33 #include <android-base/logging.h>
34 #include <android-base/result.h>
35 #include <android-base/scopeguard.h>
36 #include <android-base/strings.h>
37 #include <cutils/android_reboot.h>
38
39 #include "apex_constants.h"
40 #include "string_log.h"
41
42 using android::base::ErrnoError;
43 using android::base::Error;
44 using android::base::Result;
45
46 namespace android {
47 namespace apex {
48
WaitChild(pid_t pid)49 inline int WaitChild(pid_t pid) {
50 int status;
51 pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
52
53 if (got_pid != pid) {
54 PLOG(WARNING) << "waitpid failed: wanted " << pid << ", got " << got_pid;
55 return 1;
56 }
57
58 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
59 return 0;
60 } else {
61 return status;
62 }
63 }
64
65 // TODO(ioffe): change to Result<void>?
ForkAndRun(const std::vector<std::string> & args,std::string * error_msg)66 inline int ForkAndRun(const std::vector<std::string>& args,
67 std::string* error_msg) {
68 LOG(DEBUG) << "Forking : " << android::base::Join(args, " ");
69 std::vector<const char*> argv;
70 argv.resize(args.size() + 1, nullptr);
71 std::transform(args.begin(), args.end(), argv.begin(),
72 [](const std::string& in) { return in.c_str(); });
73
74 // 3) Fork.
75 pid_t pid = fork();
76 if (pid == -1) {
77 // Fork failed.
78 *error_msg = PStringLog() << "Unable to fork";
79 return -1;
80 }
81
82 if (pid == 0) {
83 execv(argv[0], const_cast<char**>(argv.data()));
84 PLOG(ERROR) << "execv failed";
85 _exit(1);
86 }
87
88 int rc = WaitChild(pid);
89 if (rc != 0) {
90 *error_msg = StringLog() << "Failed run: status=" << rc;
91 }
92 return rc;
93 }
94
95 template <typename Fn>
WalkDir(const std::string & path,Fn fn)96 Result<void> WalkDir(const std::string& path, Fn fn) {
97 namespace fs = std::filesystem;
98 std::error_code ec;
99 auto it = fs::directory_iterator(path, ec);
100 auto end = fs::directory_iterator();
101 while (!ec && it != end) {
102 fn(*it);
103 it.increment(ec);
104 }
105 if (ec) {
106 return Error() << "Can't open " << path
107 << " for reading : " << ec.message();
108 }
109 return {};
110 }
111
112 template <typename FilterFn>
ReadDir(const std::string & path,FilterFn fn)113 Result<std::vector<std::string>> ReadDir(const std::string& path, FilterFn fn) {
114 namespace fs = std::filesystem;
115
116 std::vector<std::string> ret;
117 auto status = WalkDir(path, [&](const fs::directory_entry& entry) {
118 if (fn(entry)) {
119 ret.push_back(entry.path());
120 }
121 });
122 if (!status.ok()) {
123 return status.error();
124 }
125 return ret;
126 }
127
IsEmptyDirectory(const std::string & path)128 inline bool IsEmptyDirectory(const std::string& path) {
129 auto res = ReadDir(path, [](auto _) { return true; });
130 return res.ok() && res->empty();
131 }
132
createDirIfNeeded(const std::string & path,mode_t mode)133 inline Result<void> createDirIfNeeded(const std::string& path, mode_t mode) {
134 struct stat stat_data;
135
136 if (stat(path.c_str(), &stat_data) != 0) {
137 if (errno == ENOENT) {
138 if (mkdir(path.c_str(), mode) != 0) {
139 return ErrnoError() << "Could not mkdir " << path;
140 }
141 } else {
142 return ErrnoError() << "Could not stat " << path;
143 }
144 } else {
145 if (!S_ISDIR(stat_data.st_mode)) {
146 return Error() << path << " exists and is not a directory.";
147 }
148 }
149
150 // Need to manually call chmod because mkdir will create a folder with
151 // permissions mode & ~umask.
152 if (chmod(path.c_str(), mode) != 0) {
153 return ErrnoError() << "Could not chmod " << path;
154 }
155
156 return {};
157 }
158
DeleteDirContent(const std::string & path)159 inline Result<void> DeleteDirContent(const std::string& path) {
160 auto files = ReadDir(path, [](auto _) { return true; });
161 if (!files.ok()) {
162 return Error() << "Failed to delete " << path << " : " << files.error();
163 }
164 for (const std::string& file : *files) {
165 if (unlink(file.c_str()) != 0) {
166 return ErrnoError() << "Failed to delete " << file;
167 }
168 }
169 return {};
170 }
171
DeleteDir(const std::string & path)172 inline Result<void> DeleteDir(const std::string& path) {
173 namespace fs = std::filesystem;
174 std::error_code ec;
175 fs::remove_all(path, ec);
176 if (ec) {
177 return Error() << "Failed to delete path " << path << " : " << ec.message();
178 }
179 return {};
180 }
181
get_path_inode(const std::string & path)182 inline Result<ino_t> get_path_inode(const std::string& path) {
183 struct stat buf;
184 memset(&buf, 0, sizeof(buf));
185 if (stat(path.c_str(), &buf) != 0) {
186 return ErrnoError() << "Failed to stat " << path;
187 } else {
188 return buf.st_ino;
189 }
190 }
191
PathExists(const std::string & path)192 inline Result<bool> PathExists(const std::string& path) {
193 namespace fs = std::filesystem;
194
195 std::error_code ec;
196 if (!fs::exists(fs::path(path), ec)) {
197 if (ec) {
198 return Error() << "Failed to access " << path << " : " << ec.message();
199 } else {
200 return false;
201 }
202 }
203 return true;
204 }
205
Reboot()206 inline void Reboot() {
207 LOG(INFO) << "Rebooting device";
208 if (android_reboot(ANDROID_RB_RESTART2, 0, nullptr) != 0) {
209 LOG(ERROR) << "Failed to reboot device";
210 }
211 }
212
WaitForFile(const std::string & path,std::chrono::nanoseconds timeout)213 inline Result<void> WaitForFile(const std::string& path,
214 std::chrono::nanoseconds timeout) {
215 android::base::Timer t;
216 bool has_slept = false;
217 while (t.duration() < timeout) {
218 struct stat sb;
219 if (stat(path.c_str(), &sb) != -1) {
220 if (has_slept) {
221 LOG(INFO) << "wait for '" << path << "' took " << t;
222 }
223 return {};
224 }
225 std::this_thread::sleep_for(5ms);
226 has_slept = true;
227 }
228 return ErrnoError() << "wait for '" << path << "' timed out and took " << t;
229 }
230
GetSubdirs(const std::string & path)231 inline Result<std::vector<std::string>> GetSubdirs(const std::string& path) {
232 namespace fs = std::filesystem;
233 auto filter_fn = [](const std::filesystem::directory_entry& entry) {
234 std::error_code ec;
235 bool result = entry.is_directory(ec);
236 if (ec) {
237 LOG(ERROR) << "Failed to check is_directory : " << ec.message();
238 return false;
239 }
240 return result;
241 };
242 return ReadDir(path, filter_fn);
243 }
244
GetDeUserDirs()245 inline Result<std::vector<std::string>> GetDeUserDirs() {
246 return GetSubdirs(kDeNDataDir);
247 }
248
249 // Returns first path between |first_dir| and |second_dir| that correspond to a
250 // existing directory. Returns error if neither |first_dir| nor |second_dir|
251 // correspond to an existing directory.
FindFirstExistingDirectory(const std::string & first_dir,const std::string & second_dir)252 inline Result<std::string> FindFirstExistingDirectory(
253 const std::string& first_dir, const std::string& second_dir) {
254 struct stat stat_buf;
255 if (stat(first_dir.c_str(), &stat_buf) != 0) {
256 PLOG(WARNING) << "Failed to stat " << first_dir;
257 if (stat(second_dir.c_str(), &stat_buf) != 0) {
258 return ErrnoError() << "Failed to stat " << second_dir;
259 }
260 if (!S_ISDIR(stat_buf.st_mode)) {
261 return Error() << second_dir << " is not a directory";
262 }
263 return second_dir;
264 }
265
266 if (S_ISDIR(stat_buf.st_mode)) {
267 return first_dir;
268 }
269 LOG(WARNING) << first_dir << " is not a directory";
270
271 if (stat(second_dir.c_str(), &stat_buf) != 0) {
272 return ErrnoError() << "Failed to stat " << second_dir;
273 }
274 if (!S_ISDIR(stat_buf.st_mode)) {
275 return Error() << second_dir << " is not a directory";
276 }
277 return second_dir;
278 }
279
280 // Copies all entries under |from| directory to |to| directory, and then them.
281 // Leaving |from| empty.
MoveDir(const std::string & from,const std::string & to)282 inline Result<void> MoveDir(const std::string& from, const std::string& to) {
283 struct stat stat_buf;
284 if (stat(to.c_str(), &stat_buf) != 0) {
285 return ErrnoError() << "Failed to stat " << to;
286 }
287 if (!S_ISDIR(stat_buf.st_mode)) {
288 return Error() << to << " is not a directory";
289 }
290
291 namespace fs = std::filesystem;
292 std::error_code ec;
293 auto it = fs::directory_iterator(from, ec);
294 if (ec) {
295 return Error() << "Can't read " << from << " : " << ec.message();
296 }
297
298 for (const auto& end = fs::directory_iterator(); it != end;) {
299 auto from_path = it->path();
300 it.increment(ec);
301 if (ec) {
302 return Error() << "Can't read " << from << " : " << ec.message();
303 }
304 auto to_path = to / from_path.filename();
305 fs::copy(from_path, to_path, fs::copy_options::recursive, ec);
306 if (ec) {
307 return Error() << "Failed to copy " << from_path << " to " << to_path
308 << " : " << ec.message();
309 }
310 fs::remove_all(from_path, ec);
311 if (ec) {
312 return Error() << "Failed to delete " << from_path << " : "
313 << ec.message();
314 }
315 }
316 return {};
317 }
318
319 } // namespace apex
320 } // namespace android
321
322 #endif // ANDROID_APEXD_APEXD_UTILS_H_
323