• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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