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
224 // Returns first path between |first_dir| and |second_dir| that correspond to a
225 // existing directory. Returns error if neither |first_dir| nor |second_dir|
226 // correspond to an existing directory.
FindFirstExistingDirectory(const std::string & first_dir,const std::string & second_dir)227 inline android::base::Result<std::string> FindFirstExistingDirectory(
228 const std::string& first_dir, const std::string& second_dir) {
229 struct stat stat_buf;
230 if (stat(first_dir.c_str(), &stat_buf) != 0) {
231 PLOG(WARNING) << "Failed to stat " << first_dir;
232 if (stat(second_dir.c_str(), &stat_buf) != 0) {
233 return android::base::ErrnoError() << "Failed to stat " << second_dir;
234 }
235 if (!S_ISDIR(stat_buf.st_mode)) {
236 return android::base::Error() << second_dir << " is not a directory";
237 }
238 return second_dir;
239 }
240
241 if (S_ISDIR(stat_buf.st_mode)) {
242 return first_dir;
243 }
244 LOG(WARNING) << first_dir << " is not a directory";
245
246 if (stat(second_dir.c_str(), &stat_buf) != 0) {
247 return android::base::ErrnoError() << "Failed to stat " << second_dir;
248 }
249 if (!S_ISDIR(stat_buf.st_mode)) {
250 return android::base::Error() << second_dir << " is not a directory";
251 }
252 return second_dir;
253 }
254
255 // Copies all entries under |from| directory to |to| directory, and then them.
256 // Leaving |from| empty.
MoveDir(const std::string & from,const std::string & to)257 inline android::base::Result<void> MoveDir(const std::string& from,
258 const std::string& to) {
259 struct stat stat_buf;
260 if (stat(to.c_str(), &stat_buf) != 0) {
261 return android::base::ErrnoError() << "Failed to stat " << to;
262 }
263 if (!S_ISDIR(stat_buf.st_mode)) {
264 return android::base::Error() << to << " is not a directory";
265 }
266
267 namespace fs = std::filesystem;
268 std::error_code ec;
269 auto it = fs::directory_iterator(from, ec);
270 if (ec) {
271 return android::base::Error()
272 << "Can't read " << from << " : " << ec.message();
273 }
274
275 for (const auto& end = fs::directory_iterator(); it != end;) {
276 auto from_path = it->path();
277 it.increment(ec);
278 if (ec) {
279 return android::base::Error()
280 << "Can't read " << from << " : " << ec.message();
281 }
282 auto to_path = to / from_path.filename();
283 fs::copy(from_path, to_path, fs::copy_options::recursive, ec);
284 if (ec) {
285 return android::base::Error() << "Failed to copy " << from_path << " to "
286 << to_path << " : " << ec.message();
287 }
288 fs::remove_all(from_path, ec);
289 if (ec) {
290 return android::base::Error()
291 << "Failed to delete " << from_path << " : " << ec.message();
292 }
293 }
294 return {};
295 }
296
GetFileSize(const std::string & file_path)297 inline android::base::Result<uintmax_t> GetFileSize(
298 const std::string& file_path) {
299 std::error_code ec;
300 auto value = std::filesystem::file_size(file_path, ec);
301 if (ec) {
302 return android::base::Error() << "Failed to get file size of " << file_path
303 << " : " << ec.message();
304 }
305
306 return value;
307 }
308
RestoreconPath(const std::string & path)309 inline android::base::Result<void> RestoreconPath(const std::string& path) {
310 unsigned int seflags = SELINUX_ANDROID_RESTORECON_RECURSE;
311 if (selinux_android_restorecon(path.c_str(), seflags) < 0) {
312 return android::base::ErrnoError() << "Failed to restorecon " << path;
313 }
314 return {};
315 }
316
GetfileconPath(const std::string & path)317 inline android::base::Result<std::string> GetfileconPath(
318 const std::string& path) {
319 char* ctx;
320 if (getfilecon(path.c_str(), &ctx) < 0) {
321 return android::base::ErrnoError() << "Failed to getfilecon " << path;
322 }
323 std::string ret(ctx);
324 freecon(ctx);
325 return ret;
326 }
327
328 } // namespace apex
329 } // namespace android
330
331 #endif // ANDROID_APEXD_APEXD_UTILS_H_
332