• 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 #define LOG_TAG "apexd"
18 
19 #include "apexd_prepostinstall.h"
20 
21 #include <algorithm>
22 #include <vector>
23 
24 #include <fcntl.h>
25 #include <sys/mount.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #include <unistd.h>
30 
31 #include <android-base/logging.h>
32 #include <android-base/macros.h>
33 #include <android-base/scopeguard.h>
34 #include <android-base/strings.h>
35 
36 #include "apex_file.h"
37 #include "apexd.h"
38 #include "apexd_private.h"
39 #include "apexd_utils.h"
40 #include "string_log.h"
41 
42 namespace android {
43 namespace apex {
44 
45 namespace {
46 
CloseSTDDescriptors()47 void CloseSTDDescriptors() {
48   // exec()d process will reopen STD* file descriptors as
49   // /dev/null
50   close(STDIN_FILENO);
51   close(STDOUT_FILENO);
52   close(STDERR_FILENO);
53 }
54 
55 template <typename Fn>
StageFnInstall(const std::vector<ApexFile> & apexes,Fn fn,const char * arg,const char * name)56 Status StageFnInstall(const std::vector<ApexFile>& apexes, Fn fn,
57                       const char* arg, const char* name) {
58   // TODO: Support a session with more than one pre-install hook.
59   const ApexFile* hook_file = nullptr;
60   for (const ApexFile& f : apexes) {
61     if (!(f.GetManifest().*fn)().empty()) {
62       if (hook_file != nullptr) {
63         return Status::Fail(StringLog() << "Missing support for multiple "
64                                         << name << " hooks");
65       }
66       hook_file = &f;
67     }
68   }
69   CHECK(hook_file != nullptr);
70   LOG(VERBOSE) << name << " for " << hook_file->GetPath();
71 
72   std::vector<const ApexFile*> mounted_apexes;
73   std::vector<std::string> activation_dirs;
74   auto preinstall_guard = android::base::make_scope_guard([&]() {
75     for (const ApexFile* f : mounted_apexes) {
76       Status st = apexd_private::UnmountPackage(*f);
77       if (!st.Ok()) {
78         LOG(ERROR) << "Failed to unmount " << f->GetPath() << " after " << name
79                    << ": " << st.ErrorMessage();
80       }
81     }
82     for (const std::string& active_point : activation_dirs) {
83       if (0 != rmdir(active_point.c_str())) {
84         PLOG(ERROR) << "Could not delete temporary active point "
85                     << active_point;
86       }
87     }
88   });
89 
90   for (const ApexFile& apex : apexes) {
91     // 1) Mount the package, if necessary.
92     std::string mount_point =
93         apexd_private::GetPackageMountPoint(apex.GetManifest());
94 
95     if (!apexd_private::IsMounted(apex.GetManifest().name(), apex.GetPath())) {
96       Status mountStatus = apexd_private::MountPackage(apex, mount_point);
97       if (!mountStatus.Ok()) {
98         return mountStatus;
99       }
100       mounted_apexes.push_back(&apex);
101     }
102 
103     // 2) Ensure there is an activation point, and we will clean it up.
104     std::string active_point =
105         apexd_private::GetActiveMountPoint(apex.GetManifest());
106     if (0 == mkdir(active_point.c_str(), kMkdirMode)) {
107       activation_dirs.emplace_back(std::move(active_point));
108     } else {
109       int saved_errno = errno;
110       if (saved_errno != EEXIST) {
111         return Status::Fail(StringLog()
112                             << "Unable to create mount point" << active_point
113                             << ": " << strerror(saved_errno));
114       }
115     }
116   }
117 
118   // 3) Create invocation args.
119   std::vector<std::string> args{
120       "/system/bin/apexd", arg,
121       hook_file->GetPath(),  // Make the APEX with hook first.
122   };
123   for (const ApexFile& apex : apexes) {
124     if (&apex != hook_file) {
125       args.push_back(apex.GetPath());
126     }
127   }
128 
129   std::string error_msg;
130   int res = ForkAndRun(args, &error_msg);
131   return res == 0 ? Status::Success() : Status::Fail(error_msg);
132 }
133 
134 template <typename Fn>
RunFnInstall(char ** in_argv,Fn fn,const char * name)135 int RunFnInstall(char** in_argv, Fn fn, const char* name) {
136   // 1) Unshare.
137   if (unshare(CLONE_NEWNS) != 0) {
138     PLOG(ERROR) << "Failed to unshare() for apex " << name;
139     _exit(200);
140   }
141 
142   // 2) Make everything private, so that our (and hook's) changes do not
143   //    propagate.
144   if (mount(nullptr, "/", nullptr, MS_PRIVATE | MS_REC, nullptr) == -1) {
145     PLOG(ERROR) << "Failed to mount private.";
146     _exit(201);
147   }
148 
149   std::string hook_path;
150   {
151     auto bind_fn = [&fn, name](const std::string& apex) {
152       std::string hook;
153       std::string mount_point;
154       std::string active_point;
155       {
156         StatusOr<ApexFile> apex_file = ApexFile::Open(apex);
157         if (!apex_file.Ok()) {
158           LOG(ERROR) << "Could not open apex " << apex << " for " << name
159                      << ": " << apex_file.ErrorMessage();
160           _exit(202);
161         }
162         const ApexManifest& manifest = apex_file->GetManifest();
163         hook = (manifest.*fn)();
164         mount_point = apexd_private::GetPackageMountPoint(manifest);
165         active_point = apexd_private::GetActiveMountPoint(manifest);
166       }
167 
168       // 3) Activate the new apex.
169       Status bind_status = apexd_private::BindMount(active_point, mount_point);
170       if (!bind_status.Ok()) {
171         LOG(ERROR) << "Failed to bind-mount " << mount_point << " to "
172                    << active_point << ": " << bind_status.ErrorMessage();
173         _exit(203);
174       }
175 
176       return std::make_pair(active_point, hook);
177     };
178 
179     // First/main APEX.
180     auto [active_point, hook] = bind_fn(in_argv[2]);
181     hook_path = active_point + "/" + hook;
182 
183     for (size_t i = 3;; ++i) {
184       if (in_argv[i] == nullptr) {
185         break;
186       }
187       bind_fn(in_argv[i]);  // Ignore result, hook will be empty.
188     }
189   }
190 
191   // 4) Run the hook.
192 
193   // For now, just run sh. But this probably needs to run the new linker.
194   std::vector<std::string> args{
195       hook_path,
196   };
197   std::vector<const char*> argv;
198   argv.resize(args.size() + 1, nullptr);
199   std::transform(args.begin(), args.end(), argv.begin(),
200                  [](const std::string& in) { return in.c_str(); });
201 
202   LOG(ERROR) << "execv of " << android::base::Join(args, " ");
203 
204   // Close all file descriptors. They are coming from the caller, we do not
205   // want to pass them on across our fork/exec into a different domain.
206   CloseSTDDescriptors();
207 
208   execv(argv[0], const_cast<char**>(argv.data()));
209   PLOG(ERROR) << "execv of " << android::base::Join(args, " ") << " failed";
210   _exit(204);
211 }
212 
213 }  // namespace
214 
StagePreInstall(const std::vector<ApexFile> & apexes)215 Status StagePreInstall(const std::vector<ApexFile>& apexes) {
216   return StageFnInstall(apexes, &ApexManifest::preinstallhook, "--pre-install",
217                         "pre-install");
218 }
219 
RunPreInstall(char ** in_argv)220 int RunPreInstall(char** in_argv) {
221   return RunFnInstall(in_argv, &ApexManifest::preinstallhook, "pre-install");
222 }
223 
StagePostInstall(const std::vector<ApexFile> & apexes)224 Status StagePostInstall(const std::vector<ApexFile>& apexes) {
225   return StageFnInstall(apexes, &ApexManifest::postinstallhook,
226                         "--post-install", "post-install");
227 }
228 
RunPostInstall(char ** in_argv)229 int RunPostInstall(char** in_argv) {
230   return RunFnInstall(in_argv, &ApexManifest::postinstallhook, "post-install");
231 }
232 
233 }  // namespace apex
234 }  // namespace android
235