Otapreopt: Adapt for actual A/B
Add postinstall script for update_engine hook.
Add otapreopt_chroot as a gateway between installd and otapreopt.
Installd will fork and run otapreopt_chroot, which has the permission
to set up a chroot in /postinstall and run otapreopt from the B
partition.
Bug: 25612095
Change-Id: I4264598da00053ced87c849c738ddc0bc5437304
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
index 65bcf39..d35099a 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -80,6 +80,36 @@
LOCAL_CLANG := true
include $(BUILD_EXECUTABLE)
+# OTA chroot tool
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := otapreopt_chroot
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := $(common_cflags)
+
+LOCAL_SRC_FILES := otapreopt_chroot.cpp
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ liblog \
+
+LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+LOCAL_CLANG := true
+include $(BUILD_EXECUTABLE)
+
+# OTA postinstall script
+
+include $(CLEAR_VARS)
+LOCAL_MODULE:= otapreopt_script
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_SRC_FILES := otapreopt_script.sh
+
+# Let this depend on otapreopt and the chroot tool, so we just have to mention one in a
+# configuration.
+LOCAL_REQUIRED_MODULES := otapreopt otapreopt_chroot
+
+include $(BUILD_PREBUILT)
+
# Tests.
include $(LOCAL_PATH)/tests/Android.mk
\ No newline at end of file
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index 38290cc..2bf27a2 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -216,29 +216,40 @@
return destroy_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]));
}
-static int do_ota_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- // Time to fork and run otapreopt.
- pid_t pid = fork();
- if (pid == 0) {
- const char* argv[1 + 9 + 1];
- argv[0] = "/system/bin/otapreopt";
- for (size_t i = 1; i <= 9; ++i) {
- argv[i] = arg[i - 1];
- }
- argv[10] = nullptr;
+// We use otapreopt_chroot to get into the chroot.
+static constexpr const char* kOtaPreopt = "/system/bin/otapreopt_chroot";
- execv(argv[0], (char * const *)argv);
- ALOGE("execv(OTAPREOPT) failed: %s\n", strerror(errno));
- exit(99);
- } else {
- int res = wait_child(pid);
- if (res == 0) {
- ALOGV("DexInv: --- END OTAPREOPT (success) ---\n");
- } else {
- ALOGE("DexInv: --- END OTAPREOPT --- status=0x%04x, process failed\n", res);
+static int do_ota_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
+ // Time to fork and run otapreopt.
+
+ // Check that the tool exists.
+ struct stat s;
+ if (stat(kOtaPreopt, &s) != 0) {
+ LOG(ERROR) << "Otapreopt chroot tool not found.";
+ return -1;
}
- return res;
- }
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ const char* argv[1 + 9 + 1];
+ argv[0] = kOtaPreopt;
+ for (size_t i = 1; i <= 9; ++i) {
+ argv[i] = arg[i - 1];
+ }
+ argv[10] = nullptr;
+
+ execv(argv[0], (char * const *)argv);
+ PLOG(ERROR) << "execv(OTAPREOPT_CHROOT) failed";
+ exit(99);
+ } else {
+ int res = wait_child(pid);
+ if (res == 0) {
+ ALOGV("DexInv: --- END OTAPREOPT (success) ---\n");
+ } else {
+ ALOGE("DexInv: --- END OTAPREOPT --- status=0x%04x, process failed\n", res);
+ }
+ return res;
+ }
}
static int do_dexopt(char **arg, char reply[REPLY_MAX])
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
new file mode 100644
index 0000000..f7f69a9
--- /dev/null
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -0,0 +1,99 @@
+/*
+ ** Copyright 2016, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <linux/unistd.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+
+#ifndef LOG_TAG
+#define LOG_TAG "otapreopt"
+#endif
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+static int otapreopt_chroot(const int argc, char **arg) {
+ // We need to run the otapreopt tool from the postinstall partition. As such, set up a
+ // mount namespace and change root.
+
+ // Create our own mount namespace.
+ if (unshare(CLONE_NEWNS) != 0) {
+ PLOG(ERROR) << "Failed to unshare() for otapreopt.";
+ exit(200);
+ }
+
+ // Make postinstall private, so that our changes don't propagate.
+ if (mount("", "/postinstall", nullptr, MS_PRIVATE, nullptr) != 0) {
+ PLOG(ERROR) << "Failed to mount private.";
+ exit(201);
+ }
+
+ // Bind mount necessary directories.
+ constexpr const char* kBindMounts[] = {
+ "/data", "/dev", "/proc", "/sys"
+ };
+ for (size_t i = 0; i < arraysize(kBindMounts); ++i) {
+ std::string trg = StringPrintf("/postinstall%s", kBindMounts[i]);
+ if (mount(kBindMounts[i], trg.c_str(), nullptr, MS_BIND, nullptr) != 0) {
+ PLOG(ERROR) << "Failed to bind-mount " << kBindMounts[i];
+ exit(202);
+ }
+ }
+
+ // Chdir into /postinstall.
+ if (chdir("/postinstall") != 0) {
+ PLOG(ERROR) << "Unable to chdir into /postinstall.";
+ exit(203);
+ }
+
+ // Make /postinstall the root in our mount namespace.
+ if (chroot(".") != 0) {
+ PLOG(ERROR) << "Failed to chroot";
+ exit(204);
+ }
+
+ if (chdir("/") != 0) {
+ PLOG(ERROR) << "Unable to chdir into /.";
+ exit(205);
+ }
+
+ // Now go on and run otapreopt.
+
+ const char* argv[1 + 9 + 1];
+ CHECK_EQ(argc, 10);
+ argv[0] = "/system/bin/otapreopt";
+ for (size_t i = 1; i <= 9; ++i) {
+ argv[i] = arg[i];
+ }
+ argv[10] = nullptr;
+
+ execv(argv[0], (char * const *)argv);
+ PLOG(ERROR) << "execv(OTAPREOPT) failed.";
+ exit(99);
+}
+
+} // namespace installd
+} // namespace android
+
+int main(const int argc, char *argv[]) {
+ return android::installd::otapreopt_chroot(argc, argv);
+}
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
new file mode 100644
index 0000000..a31734a
--- /dev/null
+++ b/cmds/installd/otapreopt_script.sh
@@ -0,0 +1,50 @@
+#!/system/bin/sh
+
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# This script will run as a postinstall step to drive otapreopt.
+
+# Maximum number of packages/steps.
+MAXIMUM_PACKAGES=1000
+
+PREPARE=$(cmd otadexopt prepare)
+if [ "$PREPARE" != "Success" ] ; then
+ echo "Failed to prepare."
+ exit 1
+fi
+
+i=0
+while ((i<MAXIMUM_PACKAGES)) ; do
+ cmd otadexopt step
+ DONE=$(cmd otadexopt done)
+ if [ "$DONE" = "OTA complete." ] ; then
+ break
+ fi
+ sleep 1
+ i=$((i+1))
+done
+
+DONE=$(cmd otadexopt done)
+if [ "$DONE" = "OTA incomplete." ] ; then
+ echo "Incomplete."
+else
+ echo "Complete or error."
+fi
+
+cmd otadexopt cleanup
+
+exit 0