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 template <typename Fn>
WalkDir(const std::string & path,Fn fn)48 android::base::Result<void> WalkDir(const std::string& path, Fn fn) {
49 namespace fs = std::filesystem;
50 std::error_code ec;
51 auto it = fs::directory_iterator(path, ec);
52 auto end = fs::directory_iterator();
53 while (!ec && it != end) {
54 fn(*it);
55 it.increment(ec);
56 }
57 if (ec) {
58 return android::base::Error()
59 << "Can't open " << path << " for reading : " << ec.message();
60 }
61 return {};
62 }
63
64 template <typename FilterFn>
ReadDir(const std::string & path,FilterFn fn)65 android::base::Result<std::vector<std::string>> ReadDir(const std::string& path,
66 FilterFn fn) {
67 namespace fs = std::filesystem;
68
69 std::vector<std::string> ret;
70 auto status = WalkDir(path, [&](const fs::directory_entry& entry) {
71 if (fn(entry)) {
72 ret.push_back(entry.path());
73 }
74 });
75 if (!status.ok()) {
76 return status.error();
77 }
78 return ret;
79 }
80
IsEmptyDirectory(const std::string & path)81 inline bool IsEmptyDirectory(const std::string& path) {
82 auto res = ReadDir(path, [](auto _) { return true; });
83 return res.ok() && res->empty();
84 }
85
CreateDirIfNeeded(const std::string & path,mode_t mode)86 inline android::base::Result<void> CreateDirIfNeeded(const std::string& path,
87 mode_t mode) {
88 struct stat stat_data;
89
90 if (stat(path.c_str(), &stat_data) != 0) {
91 if (errno == ENOENT) {
92 if (mkdir(path.c_str(), mode) != 0) {
93 return android::base::ErrnoError() << "Could not mkdir " << path;
94 }
95 } else {
96 return android::base::ErrnoError() << "Could not stat " << path;
97 }
98 } else {
99 if (!S_ISDIR(stat_data.st_mode)) {
100 return android::base::Error()
101 << path << " exists and is not a directory.";
102 }
103 }
104
105 // Need to manually call chmod because mkdir will create a folder with
106 // permissions mode & ~umask.
107 if (chmod(path.c_str(), mode) != 0) {
108 return android::base::ErrnoError() << "Could not chmod " << path;
109 }
110
111 return {};
112 }
113
DeleteDirContent(const std::string & path)114 inline android::base::Result<void> DeleteDirContent(const std::string& path) {
115 auto files = ReadDir(path, [](auto _) { return true; });
116 if (!files.ok()) {
117 return android::base::Error()
118 << "Failed to delete " << path << " : " << files.error();
119 }
120 for (const std::string& file : *files) {
121 std::error_code ec;
122 std::filesystem::remove_all(file, ec);
123 if (ec) {
124 return android::base::Error()
125 << "Failed to delete path " << file << " : " << ec.message();
126 }
127 }
128 return {};
129 }
130
DeleteDir(const std::string & path)131 inline android::base::Result<void> DeleteDir(const std::string& path) {
132 namespace fs = std::filesystem;
133 std::error_code ec;
134 fs::remove_all(path, ec);
135 if (ec) {
136 return android::base::Error()
137 << "Failed to delete path " << path << " : " << ec.message();
138 }
139 return {};
140 }
141
PathExists(const std::string & path)142 inline android::base::Result<bool> PathExists(const std::string& path) {
143 namespace fs = std::filesystem;
144
145 std::error_code ec;
146 if (!fs::exists(fs::path(path), ec)) {
147 if (ec) {
148 return android::base::Error()
149 << "Failed to access " << path << " : " << ec.message();
150 } else {
151 return false;
152 }
153 }
154 return true;
155 }
156
Reboot()157 inline void Reboot() {
158 LOG(INFO) << "Rebooting device";
159 if (android_reboot(ANDROID_RB_RESTART2, 0, nullptr) != 0) {
160 LOG(ERROR) << "Failed to reboot device";
161 }
162 // Wait for reboot to complete as we expect this to be a terminal
163 // command. Crash apexd if reboot does not complete even after
164 // waiting an arbitrary significant amount of time.
165 std::this_thread::sleep_for(std::chrono::seconds(120));
166 LOG(FATAL) << "Device did not reboot within 120 seconds";
167 }
168
WaitForFile(const std::string & path,std::chrono::nanoseconds timeout)169 inline android::base::Result<void> WaitForFile(
170 const std::string& path, std::chrono::nanoseconds timeout) {
171 android::base::Timer t;
172 bool has_slept = false;
173 while (t.duration() < timeout) {
174 struct stat sb;
175 if (stat(path.c_str(), &sb) != -1) {
176 if (has_slept) {
177 LOG(INFO) << "wait for '" << path << "' took " << t;
178 }
179 return {};
180 }
181 std::this_thread::sleep_for(5ms);
182 has_slept = true;
183 }
184 return android::base::ErrnoError()
185 << "wait for '" << path << "' timed out and took " << t;
186 }
187
GetSubdirs(const std::string & path)188 inline android::base::Result<std::vector<std::string>> GetSubdirs(
189 const std::string& path) {
190 namespace fs = std::filesystem;
191 auto filter_fn = [](const std::filesystem::directory_entry& entry) {
192 std::error_code ec;
193 bool result = entry.is_directory(ec);
194 if (ec) {
195 LOG(ERROR) << "Failed to check is_directory : " << ec.message();
196 return false;
197 }
198 return result;
199 };
200 return ReadDir(path, filter_fn);
201 }
202
GetDeUserDirs()203 inline android::base::Result<std::vector<std::string>> GetDeUserDirs() {
204 return GetSubdirs(kDeNDataDir);
205 }
206
FindFilesBySuffix(const std::string & path,const std::vector<std::string> & suffix_list)207 inline android::base::Result<std::vector<std::string>> FindFilesBySuffix(
208 const std::string& path, const std::vector<std::string>& suffix_list) {
209 auto filter_fn =
210 [&suffix_list](const std::filesystem::directory_entry& entry) {
211 for (const std::string& suffix : suffix_list) {
212 std::error_code ec;
213 auto name = entry.path().filename().string();
214 if (entry.is_regular_file(ec) &&
215 android::base::EndsWith(name, suffix)) {
216 return true; // suffix matches, take.
217 }
218 }
219 return false;
220 };
221 return ReadDir(path, filter_fn);
222 }
223
FindApexes(const std::vector<std::string> & paths)224 inline android::base::Result<std::vector<std::string>> FindApexes(
225 const std::vector<std::string>& paths) {
226 std::vector<std::string> result;
227 for (const auto& path : paths) {
228 auto exist = PathExists(path);
229 if (!exist.ok()) {
230 return exist.error();
231 }
232 if (!*exist) continue;
233
234 const auto& apexes = FindFilesBySuffix(path, {kApexPackageSuffix});
235 if (!apexes.ok()) {
236 return apexes;
237 }
238
239 result.insert(result.end(), apexes->begin(), apexes->end());
240 }
241 return result;
242 }
243
244 // Returns first path between |first_dir| and |second_dir| that correspond to a
245 // existing directory. Returns error if neither |first_dir| nor |second_dir|
246 // correspond to an existing directory.
FindFirstExistingDirectory(const std::string & first_dir,const std::string & second_dir)247 inline android::base::Result<std::string> FindFirstExistingDirectory(
248 const std::string& first_dir, const std::string& second_dir) {
249 struct stat stat_buf;
250 if (stat(first_dir.c_str(), &stat_buf) != 0) {
251 PLOG(WARNING) << "Failed to stat " << first_dir;
252 if (stat(second_dir.c_str(), &stat_buf) != 0) {
253 return android::base::ErrnoError() << "Failed to stat " << second_dir;
254 }
255 if (!S_ISDIR(stat_buf.st_mode)) {
256 return android::base::Error() << second_dir << " is not a directory";
257 }
258 return second_dir;
259 }
260
261 if (S_ISDIR(stat_buf.st_mode)) {
262 return first_dir;
263 }
264 LOG(WARNING) << first_dir << " is not a directory";
265
266 if (stat(second_dir.c_str(), &stat_buf) != 0) {
267 return android::base::ErrnoError() << "Failed to stat " << second_dir;
268 }
269 if (!S_ISDIR(stat_buf.st_mode)) {
270 return android::base::Error() << second_dir << " is not a directory";
271 }
272 return second_dir;
273 }
274
275 // Copies all entries under |from| directory to |to| directory, and then them.
276 // Leaving |from| empty.
MoveDir(const std::string & from,const std::string & to)277 inline android::base::Result<void> MoveDir(const std::string& from,
278 const std::string& to) {
279 struct stat stat_buf;
280 if (stat(to.c_str(), &stat_buf) != 0) {
281 return android::base::ErrnoError() << "Failed to stat " << to;
282 }
283 if (!S_ISDIR(stat_buf.st_mode)) {
284 return android::base::Error() << to << " is not a directory";
285 }
286
287 namespace fs = std::filesystem;
288 std::error_code ec;
289 auto it = fs::directory_iterator(from, ec);
290 if (ec) {
291 return android::base::Error()
292 << "Can't read " << from << " : " << ec.message();
293 }
294
295 for (const auto& end = fs::directory_iterator(); it != end;) {
296 auto from_path = it->path();
297 it.increment(ec);
298 if (ec) {
299 return android::base::Error()
300 << "Can't read " << from << " : " << ec.message();
301 }
302 auto to_path = to / from_path.filename();
303 fs::copy(from_path, to_path, fs::copy_options::recursive, ec);
304 if (ec) {
305 return android::base::Error() << "Failed to copy " << from_path << " to "
306 << to_path << " : " << ec.message();
307 }
308 fs::remove_all(from_path, ec);
309 if (ec) {
310 return android::base::Error()
311 << "Failed to delete " << from_path << " : " << ec.message();
312 }
313 }
314 return {};
315 }
316
GetFileSize(const std::string & file_path)317 inline android::base::Result<uintmax_t> GetFileSize(
318 const std::string& file_path) {
319 std::error_code ec;
320 auto value = std::filesystem::file_size(file_path, ec);
321 if (ec) {
322 return android::base::Error() << "Failed to get file size of " << file_path
323 << " : " << ec.message();
324 }
325
326 return value;
327 }
328
RestoreconPath(const std::string & path)329 inline android::base::Result<void> RestoreconPath(const std::string& path) {
330 unsigned int seflags = SELINUX_ANDROID_RESTORECON_RECURSE;
331 if (selinux_android_restorecon(path.c_str(), seflags) < 0) {
332 return android::base::ErrnoError() << "Failed to restorecon " << path;
333 }
334 return {};
335 }
336
GetfileconPath(const std::string & path)337 inline android::base::Result<std::string> GetfileconPath(
338 const std::string& path) {
339 char* ctx;
340 if (getfilecon(path.c_str(), &ctx) < 0) {
341 return android::base::ErrnoError() << "Failed to getfilecon " << path;
342 }
343 std::string ret(ctx);
344 freecon(ctx);
345 return ret;
346 }
347
348 } // namespace apex
349 } // namespace android
350
351 #endif // ANDROID_APEXD_APEXD_UTILS_H_
352