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 <vector>
25 
26 #include <dirent.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <sys/wait.h>
30 
31 #include <android-base/chrono_utils.h>
32 #include <android-base/logging.h>
33 #include <android-base/result.h>
34 #include <android-base/scopeguard.h>
35 #include <android-base/strings.h>
36 #include <cutils/android_reboot.h>
37 
38 #include "apex_constants.h"
39 #include "string_log.h"
40 
41 using android::base::ErrnoError;
42 using android::base::Error;
43 using android::base::Result;
44 
45 namespace android {
46 namespace apex {
47 
WaitChild(pid_t pid)48 inline int WaitChild(pid_t pid) {
49   int status;
50   pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
51 
52   if (got_pid != pid) {
53     PLOG(WARNING) << "waitpid failed: wanted " << pid << ", got " << got_pid;
54     return 1;
55   }
56 
57   if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
58     return 0;
59   } else {
60     return status;
61   }
62 }
63 
ForkAndRun(const std::vector<std::string> & args)64 inline Result<void> ForkAndRun(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 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 Error() << "Failed run: status=" << rc;
86   }
87   return {};
88 }
89 
90 template <typename Fn>
WalkDir(const std::string & path,Fn fn)91 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 Error() << "Can't open " << path
102                    << " for reading : " << ec.message();
103   }
104   return {};
105 }
106 
107 template <typename FilterFn>
ReadDir(const std::string & path,FilterFn fn)108 Result<std::vector<std::string>> ReadDir(const std::string& path, FilterFn fn) {
109   namespace fs = std::filesystem;
110 
111   std::vector<std::string> ret;
112   auto status = WalkDir(path, [&](const fs::directory_entry& entry) {
113     if (fn(entry)) {
114       ret.push_back(entry.path());
115     }
116   });
117   if (!status.ok()) {
118     return status.error();
119   }
120   return ret;
121 }
122 
IsEmptyDirectory(const std::string & path)123 inline bool IsEmptyDirectory(const std::string& path) {
124   auto res = ReadDir(path, [](auto _) { return true; });
125   return res.ok() && res->empty();
126 }
127 
createDirIfNeeded(const std::string & path,mode_t mode)128 inline Result<void> createDirIfNeeded(const std::string& path, mode_t mode) {
129   struct stat stat_data;
130 
131   if (stat(path.c_str(), &stat_data) != 0) {
132     if (errno == ENOENT) {
133       if (mkdir(path.c_str(), mode) != 0) {
134         return ErrnoError() << "Could not mkdir " << path;
135       }
136     } else {
137       return ErrnoError() << "Could not stat " << path;
138     }
139   } else {
140     if (!S_ISDIR(stat_data.st_mode)) {
141       return Error() << path << " exists and is not a directory.";
142     }
143   }
144 
145   // Need to manually call chmod because mkdir will create a folder with
146   // permissions mode & ~umask.
147   if (chmod(path.c_str(), mode) != 0) {
148     return ErrnoError() << "Could not chmod " << path;
149   }
150 
151   return {};
152 }
153 
DeleteDirContent(const std::string & path)154 inline Result<void> DeleteDirContent(const std::string& path) {
155   auto files = ReadDir(path, [](auto _) { return true; });
156   if (!files.ok()) {
157     return Error() << "Failed to delete " << path << " : " << files.error();
158   }
159   for (const std::string& file : *files) {
160     if (unlink(file.c_str()) != 0) {
161       return ErrnoError() << "Failed to delete " << file;
162     }
163   }
164   return {};
165 }
166 
DeleteDir(const std::string & path)167 inline Result<void> DeleteDir(const std::string& path) {
168   namespace fs = std::filesystem;
169   std::error_code ec;
170   fs::remove_all(path, ec);
171   if (ec) {
172     return Error() << "Failed to delete path " << path << " : " << ec.message();
173   }
174   return {};
175 }
176 
get_path_inode(const std::string & path)177 inline Result<ino_t> get_path_inode(const std::string& path) {
178   struct stat buf;
179   memset(&buf, 0, sizeof(buf));
180   if (stat(path.c_str(), &buf) != 0) {
181     return ErrnoError() << "Failed to stat " << path;
182   } else {
183     return buf.st_ino;
184   }
185 }
186 
PathExists(const std::string & path)187 inline Result<bool> PathExists(const std::string& path) {
188   namespace fs = std::filesystem;
189 
190   std::error_code ec;
191   if (!fs::exists(fs::path(path), ec)) {
192     if (ec) {
193       return Error() << "Failed to access " << path << " : " << ec.message();
194     } else {
195       return false;
196     }
197   }
198   return true;
199 }
200 
Reboot()201 inline void Reboot() {
202   LOG(INFO) << "Rebooting device";
203   if (android_reboot(ANDROID_RB_RESTART2, 0, nullptr) != 0) {
204     LOG(ERROR) << "Failed to reboot device";
205   }
206 }
207 
WaitForFile(const std::string & path,std::chrono::nanoseconds timeout)208 inline Result<void> WaitForFile(const std::string& path,
209                                 std::chrono::nanoseconds timeout) {
210   android::base::Timer t;
211   bool has_slept = false;
212   while (t.duration() < timeout) {
213     struct stat sb;
214     if (stat(path.c_str(), &sb) != -1) {
215       if (has_slept) {
216         LOG(INFO) << "wait for '" << path << "' took " << t;
217       }
218       return {};
219     }
220     std::this_thread::sleep_for(5ms);
221     has_slept = true;
222   }
223   return ErrnoError() << "wait for '" << path << "' timed out and took " << t;
224 }
225 
GetSubdirs(const std::string & path)226 inline Result<std::vector<std::string>> GetSubdirs(const std::string& path) {
227   namespace fs = std::filesystem;
228   auto filter_fn = [](const std::filesystem::directory_entry& entry) {
229     std::error_code ec;
230     bool result = entry.is_directory(ec);
231     if (ec) {
232       LOG(ERROR) << "Failed to check is_directory : " << ec.message();
233       return false;
234     }
235     return result;
236   };
237   return ReadDir(path, filter_fn);
238 }
239 
GetDeUserDirs()240 inline Result<std::vector<std::string>> GetDeUserDirs() {
241   return GetSubdirs(kDeNDataDir);
242 }
243 
244 }  // namespace apex
245 }  // namespace android
246 
247 #endif  // ANDROID_APEXD_APEXD_UTILS_H_
248