1 /* 2 ** Copyright 2016, 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 #include <fcntl.h> 18 #include <linux/unistd.h> 19 #include <sys/mount.h> 20 #include <sys/stat.h> 21 #include <sys/wait.h> 22 23 #include <sstream> 24 25 #include <android-base/logging.h> 26 #include <android-base/macros.h> 27 #include <android-base/stringprintf.h> 28 #include <libdm/dm.h> 29 #include <selinux/android.h> 30 31 #include <apexd.h> 32 33 #include "installd_constants.h" 34 #include "otapreopt_utils.h" 35 36 #ifndef LOG_TAG 37 #define LOG_TAG "otapreopt" 38 #endif 39 40 using android::base::StringPrintf; 41 42 namespace android { 43 namespace installd { 44 45 static void CloseDescriptor(int fd) { 46 if (fd >= 0) { 47 int result = close(fd); 48 UNUSED(result); // Ignore result. Printing to logcat will open a new descriptor 49 // that we do *not* want. 50 } 51 } 52 53 static void CloseDescriptor(const char* descriptor_string) { 54 int fd = -1; 55 std::istringstream stream(descriptor_string); 56 stream >> fd; 57 if (!stream.fail()) { 58 CloseDescriptor(fd); 59 } 60 } 61 62 static std::vector<apex::ApexFile> ActivateApexPackages() { 63 // The logic here is (partially) copied and adapted from 64 // system/apex/apexd/apexd_main.cpp. 65 // 66 // Only scan the APEX directory under /system (within the chroot dir). 67 apex::scanPackagesDirAndActivate(apex::kApexPackageSystemDir); 68 return apex::getActivePackages(); 69 } 70 71 static void DeactivateApexPackages(const std::vector<apex::ApexFile>& active_packages) { 72 for (const apex::ApexFile& apex_file : active_packages) { 73 const std::string& package_path = apex_file.GetPath(); 74 apex::Status status = apex::deactivatePackage(package_path); 75 if (!status.Ok()) { 76 LOG(ERROR) << "Failed to deactivate " << package_path << ": " << status.ErrorMessage(); 77 } 78 } 79 } 80 81 static void TryExtraMount(const char* name, const char* slot, const char* target) { 82 std::string partition_name = StringPrintf("%s%s", name, slot); 83 84 // See whether update_engine mounted a logical partition. 85 { 86 auto& dm = dm::DeviceMapper::Instance(); 87 if (dm.GetState(partition_name) != dm::DmDeviceState::INVALID) { 88 std::string path; 89 if (dm.GetDmDevicePathByName(partition_name, &path)) { 90 int mount_result = mount(path.c_str(), 91 target, 92 "ext4", 93 MS_RDONLY, 94 /* data */ nullptr); 95 if (mount_result == 0) { 96 return; 97 } 98 } 99 } 100 } 101 102 // Fall back and attempt a direct mount. 103 std::string block_device = StringPrintf("/dev/block/by-name/%s", partition_name.c_str()); 104 int mount_result = mount(block_device.c_str(), 105 target, 106 "ext4", 107 MS_RDONLY, 108 /* data */ nullptr); 109 UNUSED(mount_result); 110 } 111 112 // Entry for otapreopt_chroot. Expected parameters are: 113 // [cmd] [status-fd] [target-slot] "dexopt" [dexopt-params] 114 // The file descriptor denoted by status-fd will be closed. The rest of the parameters will 115 // be passed on to otapreopt in the chroot. 116 static int otapreopt_chroot(const int argc, char **arg) { 117 // Validate arguments 118 // We need the command, status channel and target slot, at a minimum. 119 if(argc < 3) { 120 PLOG(ERROR) << "Not enough arguments."; 121 exit(208); 122 } 123 // Close all file descriptors. They are coming from the caller, we do not want to pass them 124 // on across our fork/exec into a different domain. 125 // 1) Default descriptors. 126 CloseDescriptor(STDIN_FILENO); 127 CloseDescriptor(STDOUT_FILENO); 128 CloseDescriptor(STDERR_FILENO); 129 // 2) The status channel. 130 CloseDescriptor(arg[1]); 131 132 // We need to run the otapreopt tool from the postinstall partition. As such, set up a 133 // mount namespace and change root. 134 135 // Create our own mount namespace. 136 if (unshare(CLONE_NEWNS) != 0) { 137 PLOG(ERROR) << "Failed to unshare() for otapreopt."; 138 exit(200); 139 } 140 141 // Make postinstall private, so that our changes don't propagate. 142 if (mount("", "/postinstall", nullptr, MS_PRIVATE, nullptr) != 0) { 143 PLOG(ERROR) << "Failed to mount private."; 144 exit(201); 145 } 146 147 // Bind mount necessary directories. 148 constexpr const char* kBindMounts[] = { 149 "/data", "/dev", "/proc", "/sys" 150 }; 151 for (size_t i = 0; i < arraysize(kBindMounts); ++i) { 152 std::string trg = StringPrintf("/postinstall%s", kBindMounts[i]); 153 if (mount(kBindMounts[i], trg.c_str(), nullptr, MS_BIND, nullptr) != 0) { 154 PLOG(ERROR) << "Failed to bind-mount " << kBindMounts[i]; 155 exit(202); 156 } 157 } 158 159 // Try to mount the vendor partition. update_engine doesn't do this for us, but we 160 // want it for vendor APKs. 161 // Notes: 162 // 1) We pretty much guess a name here and hope to find the partition by name. 163 // It is just as complicated and brittle to scan /proc/mounts. But this requires 164 // validating the target-slot so as not to try to mount some totally random path. 165 // 2) We're in a mount namespace here, so when we die, this will be cleaned up. 166 // 3) Ignore errors. Printing anything at this stage will open a file descriptor 167 // for logging. 168 if (!ValidateTargetSlotSuffix(arg[2])) { 169 LOG(ERROR) << "Target slot suffix not legal: " << arg[2]; 170 exit(207); 171 } 172 TryExtraMount("vendor", arg[2], "/postinstall/vendor"); 173 174 // Try to mount the product partition. update_engine doesn't do this for us, but we 175 // want it for product APKs. Same notes as vendor above. 176 TryExtraMount("product", arg[2], "/postinstall/product"); 177 178 // Setup APEX mount point and its security context. 179 static constexpr const char* kPostinstallApexDir = "/postinstall/apex"; 180 // The following logic is similar to the one in system/core/rootdir/init.rc: 181 // 182 // mount tmpfs tmpfs /apex nodev noexec nosuid 183 // chmod 0755 /apex 184 // chown root root /apex 185 // restorecon /apex 186 // 187 // except we perform the `restorecon` step just after mounting the tmpfs 188 // filesystem in /postinstall/apex, so that this directory is correctly 189 // labeled (with type `postinstall_apex_mnt_dir`) and may be manipulated in 190 // following operations (`chmod`, `chown`, etc.) following policies 191 // restricted to `postinstall_apex_mnt_dir`: 192 // 193 // mount tmpfs tmpfs /postinstall/apex nodev noexec nosuid 194 // restorecon /postinstall/apex 195 // chmod 0755 /postinstall/apex 196 // chown root root /postinstall/apex 197 // 198 if (mount("tmpfs", kPostinstallApexDir, "tmpfs", MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr) 199 != 0) { 200 PLOG(ERROR) << "Failed to mount tmpfs in " << kPostinstallApexDir; 201 exit(209); 202 } 203 if (selinux_android_restorecon(kPostinstallApexDir, 0) < 0) { 204 PLOG(ERROR) << "Failed to restorecon " << kPostinstallApexDir; 205 exit(214); 206 } 207 if (chmod(kPostinstallApexDir, 0755) != 0) { 208 PLOG(ERROR) << "Failed to chmod " << kPostinstallApexDir << " to 0755"; 209 exit(210); 210 } 211 if (chown(kPostinstallApexDir, 0, 0) != 0) { 212 PLOG(ERROR) << "Failed to chown " << kPostinstallApexDir << " to root:root"; 213 exit(211); 214 } 215 216 // Chdir into /postinstall. 217 if (chdir("/postinstall") != 0) { 218 PLOG(ERROR) << "Unable to chdir into /postinstall."; 219 exit(203); 220 } 221 222 // Make /postinstall the root in our mount namespace. 223 if (chroot(".") != 0) { 224 PLOG(ERROR) << "Failed to chroot"; 225 exit(204); 226 } 227 228 if (chdir("/") != 0) { 229 PLOG(ERROR) << "Unable to chdir into /."; 230 exit(205); 231 } 232 233 // Try to mount APEX packages in "/apex" in the chroot dir. We need at least 234 // the Android Runtime APEX, as it is required by otapreopt to run dex2oat. 235 std::vector<apex::ApexFile> active_packages = ActivateApexPackages(); 236 237 // Now go on and run otapreopt. 238 239 // Incoming: cmd + status-fd + target-slot + cmd... | Incoming | = argc 240 // Outgoing: cmd + target-slot + cmd... | Outgoing | = argc - 1 241 std::vector<std::string> cmd; 242 cmd.reserve(argc); 243 cmd.push_back("/system/bin/otapreopt"); 244 245 // The first parameter is the status file descriptor, skip. 246 for (size_t i = 2; i < static_cast<size_t>(argc); ++i) { 247 cmd.push_back(arg[i]); 248 } 249 250 // Fork and execute otapreopt in its own process. 251 std::string error_msg; 252 bool exec_result = Exec(cmd, &error_msg); 253 if (!exec_result) { 254 LOG(ERROR) << "Running otapreopt failed: " << error_msg; 255 } 256 257 // Tear down the work down by the apexd logic. (i.e. deactivate packages). 258 DeactivateApexPackages(active_packages); 259 260 if (!exec_result) { 261 exit(213); 262 } 263 264 return 0; 265 } 266 267 } // namespace installd 268 } // namespace android 269 270 int main(const int argc, char *argv[]) { 271 return android::installd::otapreopt_chroot(argc, argv); 272 } 273