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
WaitChild(pid_t pid)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
ForkAndRun(const std::vector<std::string> & args)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>
WalkDir(const std::string & path,Fn 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>
ReadDir(const std::string & path,FilterFn fn)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
IsEmptyDirectory(const std::string & path)124 inline bool IsEmptyDirectory(const std::string& path) {
125 auto res = ReadDir(path, [](auto _) { return true; });
126 return res.ok() && res->empty();
127 }
128
CreateDirIfNeeded(const std::string & path,mode_t mode)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
DeleteDirContent(const std::string & path)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
DeleteDir(const std::string & path)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
PathExists(const std::string & path)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
Reboot()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
WaitForFile(const std::string & path,std::chrono::nanoseconds timeout)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
GetSubdirs(const std::string & path)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
GetDeUserDirs()241 inline android::base::Result<std::vector<std::string>> GetDeUserDirs() {
242 return GetSubdirs(kDeNDataDir);
243 }
244
FindFilesBySuffix(const std::string & path,const std::vector<std::string> & suffix_list)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
FindApexes(const std::vector<std::string> & paths)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.
FindFirstExistingDirectory(const std::string & first_dir,const std::string & second_dir)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.
MoveDir(const std::string & from,const std::string & to)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
GetFileSize(const std::string & file_path)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
RestoreconPath(const std::string & path)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