• 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 <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