Parse postinstall parameters from the payload metadata.
Payload v2 includes a description of the post-install command it should
run, while in payload v1 we use the default values. This patch mounts
the partition on the new top-level directory called /postinstall that
should already be created.
Bug: 27177071
TEST=FEATURES=test emerge-link update_engine
Change-Id: Iaedf3b01e5e1ad57c68bd316b4b6e79cbab35bb6
diff --git a/common/constants.cc b/common/constants.cc
index 6b1d416..fc6df37 100644
--- a/common/constants.cc
+++ b/common/constants.cc
@@ -29,6 +29,8 @@
const char kStatefulPartition[] = "/mnt/stateful_partition";
+const char kPostinstallDefaultScript[] = "postinst";
+
// Constants defining keys for the persisted state of update engine.
const char kPrefsAttemptInProgress[] = "attempt-in-progress";
const char kPrefsBackoffExpiryTime[] = "backoff-expiry-time";
diff --git a/common/constants.h b/common/constants.h
index f9a43c6..25d587b 100644
--- a/common/constants.h
+++ b/common/constants.h
@@ -32,6 +32,9 @@
// The location where we store the AU preferences (state etc).
extern const char kPrefsSubDirectory[];
+// Path to the post install command, relative to the partition.
+extern const char kPostinstallDefaultScript[];
+
// Path to the stateful partition on the root filesystem.
extern const char kStatefulPartition[];
diff --git a/common/test_utils.cc b/common/test_utils.cc
index c09096b..a574863 100644
--- a/common/test_utils.cc
+++ b/common/test_utils.cc
@@ -260,7 +260,7 @@
string loop_dev;
loop_binder_.reset(new ScopedLoopbackDeviceBinder(file_path, &loop_dev));
- EXPECT_TRUE(utils::MountFilesystem(loop_dev, *mnt_path, flags));
+ EXPECT_TRUE(utils::MountFilesystem(loop_dev, *mnt_path, flags, ""));
unmounter_.reset(new ScopedFilesystemUnmounter(*mnt_path));
}
diff --git a/common/utils.cc b/common/utils.cc
index 91dcfc8..b4956e7 100644
--- a/common/utils.cc
+++ b/common/utils.cc
@@ -613,9 +613,14 @@
bool MountFilesystem(const string& device,
const string& mountpoint,
- unsigned long mountflags) { // NOLINT(runtime/int)
- // TODO(sosa): Remove "ext3" once crbug.com/208022 is resolved.
- const vector<const char*> fstypes{"ext2", "ext3", "ext4", "squashfs"};
+ unsigned long mountflags, // NOLINT(runtime/int)
+ const string& type) {
+ vector<const char*> fstypes;
+ if (type.empty()) {
+ fstypes = {"ext2", "ext3", "ext4", "squashfs"};
+ } else {
+ fstypes = {type.c_str()};
+ }
for (const char* fstype : fstypes) {
int rc = mount(device.c_str(), mountpoint.c_str(), fstype, mountflags,
nullptr);
@@ -625,7 +630,9 @@
PLOG(WARNING) << "Unable to mount destination device " << device
<< " on " << mountpoint << " as " << fstype;
}
- LOG(ERROR) << "Unable to mount " << device << " with any supported type";
+ if (!type.empty()) {
+ LOG(ERROR) << "Unable to mount " << device << " with any supported type";
+ }
return false;
}
diff --git a/common/utils.h b/common/utils.h
index 5bf1422..8da0726 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -171,10 +171,13 @@
std::string MakePartitionNameForMount(const std::string& part_name);
// Synchronously mount or unmount a filesystem. Return true on success.
-// When mounting, it will attempt to mount the the device as "ext3", "ext2" and
-// "squashfs", with the passed |flags| options.
-bool MountFilesystem(const std::string& device, const std::string& mountpoint,
- unsigned long flags); // NOLINT(runtime/int)
+// When mounting, it will attempt to mount the device as the passed filesystem
+// type |type|, with the passed |flags| options. If |type| is empty, "ext2",
+// "ext3", "ext4" and "squashfs" will be tried.
+bool MountFilesystem(const std::string& device,
+ const std::string& mountpoint,
+ unsigned long flags, // NOLINT(runtime/int)
+ const std::string& type);
bool UnmountFilesystem(const std::string& mountpoint);
// Returns the block count and the block byte size of the file system on
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index f95679c..f490c08 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -790,6 +790,12 @@
install_part.name = partition.partition_name();
install_part.run_postinstall =
partition.has_run_postinstall() && partition.run_postinstall();
+ if (install_part.run_postinstall) {
+ install_part.postinstall_path =
+ (partition.has_postinstall_path() ? partition.postinstall_path()
+ : kPostinstallDefaultScript);
+ install_part.filesystem_type = partition.filesystem_type();
+ }
if (partition.has_old_partition_info()) {
const PartitionInfo& info = partition.old_partition_info();
diff --git a/payload_consumer/install_plan.cc b/payload_consumer/install_plan.cc
index 572ff41..51e85b3 100644
--- a/payload_consumer/install_plan.cc
+++ b/payload_consumer/install_plan.cc
@@ -59,9 +59,13 @@
void InstallPlan::Dump() const {
string partitions_str;
for (const auto& partition : partitions) {
- partitions_str += base::StringPrintf(
- ", part: %s (source_size: %" PRIu64 ", target_size %" PRIu64 ")",
- partition.name.c_str(), partition.source_size, partition.target_size);
+ partitions_str +=
+ base::StringPrintf(", part: %s (source_size: %" PRIu64
+ ", target_size %" PRIu64 ", postinst:%s)",
+ partition.name.c_str(),
+ partition.source_size,
+ partition.target_size,
+ utils::ToString(partition.run_postinstall).c_str());
}
LOG(INFO) << "InstallPlan: "
@@ -109,7 +113,9 @@
target_path == that.target_path &&
target_size == that.target_size &&
target_hash == that.target_hash &&
- run_postinstall == that.run_postinstall);
+ run_postinstall == that.run_postinstall &&
+ postinstall_path == that.postinstall_path &&
+ filesystem_type == that.filesystem_type);
}
} // namespace chromeos_update_engine
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index d2f15fa..454dd78 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -90,8 +90,11 @@
uint64_t target_size{0};
brillo::Blob target_hash;
- // Whether we should run the postinstall script from this partition.
+ // Whether we should run the postinstall script from this partition and the
+ // postinstall parameters.
bool run_postinstall{false};
+ std::string postinstall_path;
+ std::string filesystem_type;
};
std::vector<Partition> partitions;
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index 84ca398..fe468cc 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -23,6 +23,7 @@
#include <base/bind.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
#include "update_engine/common/action_processor.h"
#include "update_engine/common/boot_control_interface.h"
@@ -34,16 +35,6 @@
using std::string;
using std::vector;
-namespace {
-// The absolute path to the post install command.
-const char kPostinstallScript[] = "/postinst";
-
-// Path to the binary file used by kPostinstallScript. Used to get and log the
-// file format of the binary to debug issues when the ELF format on the update
-// doesn't match the one on the current system. This path is not executed.
-const char kDebugPostinstallBinaryPath[] = "/usr/bin/cros_installer";
-}
-
void PostinstallRunnerAction::PerformAction() {
CHECK(HasInputObject());
install_plan_ = GetInputObject();
@@ -60,6 +51,11 @@
}
void PostinstallRunnerAction::PerformPartitionPostinstall() {
+ if (install_plan_.download_url.empty()) {
+ LOG(INFO) << "Skipping post-install during rollback";
+ return CompletePostinstall(ErrorCode::kSuccess);
+ }
+
// Skip all the partitions that don't have a post-install step.
while (current_partition_ < install_plan_.partitions.size() &&
!install_plan_.partitions[current_partition_].run_postinstall) {
@@ -83,38 +79,44 @@
// Perform post-install for the current_partition_ partition. At this point we
// need to call CompletePartitionPostinstall to complete the operation and
// cleanup.
+#ifdef __ANDROID__
+ fs_mount_dir_ = "/postinstall";
+#else // __ANDROID__
TEST_AND_RETURN(
- utils::MakeTempDirectory("au_postint_mount.XXXXXX", &temp_rootfs_dir_));
+ utils::MakeTempDirectory("au_postint_mount.XXXXXX", &fs_mount_dir_));
+#endif // __ANDROID__
- if (!utils::MountFilesystem(mountable_device, temp_rootfs_dir_, MS_RDONLY)) {
+ string abs_path = base::FilePath(fs_mount_dir_)
+ .AppendASCII(partition.postinstall_path)
+ .value();
+ if (!base::StartsWith(
+ abs_path, fs_mount_dir_, base::CompareCase::SENSITIVE)) {
+ LOG(ERROR) << "Invalid relative postinstall path: "
+ << partition.postinstall_path;
+ return CompletePostinstall(ErrorCode::kPostinstallRunnerError);
+ }
+
+ if (!utils::MountFilesystem(mountable_device,
+ fs_mount_dir_,
+ MS_RDONLY,
+ partition.filesystem_type)) {
return CompletePartitionPostinstall(
1, "Error mounting the device " + mountable_device);
}
- LOG(INFO) << "Performing postinst (" << kPostinstallScript
- << ") installed on device " << partition.target_path
+ LOG(INFO) << "Performing postinst (" << partition.postinstall_path << " at "
+ << abs_path << ") installed on device " << partition.target_path
<< " and mountable device " << mountable_device;
// Logs the file format of the postinstall script we are about to run. This
// will help debug when the postinstall script doesn't match the architecture
// of our build.
- LOG(INFO) << "Format file for new " << kPostinstallScript << " is: "
- << utils::GetFileFormat(temp_rootfs_dir_ + kPostinstallScript);
- LOG(INFO) << "Format file for new " << kDebugPostinstallBinaryPath << " is: "
- << utils::GetFileFormat(
- temp_rootfs_dir_ + kDebugPostinstallBinaryPath);
+ LOG(INFO) << "Format file for new " << partition.postinstall_path
+ << " is: " << utils::GetFileFormat(abs_path);
// Runs the postinstall script asynchronously to free up the main loop while
// it's running.
- vector<string> command;
- if (!install_plan_.download_url.empty()) {
- command.push_back(temp_rootfs_dir_ + kPostinstallScript);
- } else {
- // TODO(sosa): crbug.com/366207.
- // If we're doing a rollback, just run our own postinstall.
- command.push_back(kPostinstallScript);
- }
- command.push_back(partition.target_path);
+ vector<string> command = {abs_path, partition.target_path};
if (!Subprocess::Get().Exec(
command,
base::Bind(
@@ -127,11 +129,13 @@
void PostinstallRunnerAction::CompletePartitionPostinstall(
int return_code,
const string& output) {
- utils::UnmountFilesystem(temp_rootfs_dir_);
- if (!base::DeleteFile(base::FilePath(temp_rootfs_dir_), false)) {
- PLOG(WARNING) << "Not removing mountpoint " << temp_rootfs_dir_;
+ utils::UnmountFilesystem(fs_mount_dir_);
+#ifndef ANDROID
+ if (!base::DeleteFile(base::FilePath(fs_mount_dir_), false)) {
+ PLOG(WARNING) << "Not removing temporary mountpoint " << fs_mount_dir_;
}
- temp_rootfs_dir_.clear();
+#endif // !ANDROID
+ fs_mount_dir_.clear();
if (return_code != 0) {
LOG(ERROR) << "Postinst command failed with code: " << return_code;
diff --git a/payload_consumer/postinstall_runner_action.h b/payload_consumer/postinstall_runner_action.h
index ab267b8..b4defae 100644
--- a/payload_consumer/postinstall_runner_action.h
+++ b/payload_consumer/postinstall_runner_action.h
@@ -63,7 +63,9 @@
void CompletePostinstall(ErrorCode error_code);
InstallPlan install_plan_;
- std::string temp_rootfs_dir_;
+
+ // The path where the filesystem will be mounted during post-install.
+ std::string fs_mount_dir_;
// The partition being processed on the list of partitions specified in the
// InstallPlan.
diff --git a/payload_consumer/postinstall_runner_action_unittest.cc b/payload_consumer/postinstall_runner_action_unittest.cc
index c4c68b1..85535d7 100644
--- a/payload_consumer/postinstall_runner_action_unittest.cc
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -191,6 +191,7 @@
part.name = "part";
part.target_path = dev;
part.run_postinstall = true;
+ part.postinstall_path = kPostinstallDefaultScript;
InstallPlan install_plan;
install_plan.partitions = {part};
install_plan.download_url = "http://devserver:8080/update";
@@ -215,7 +216,7 @@
EXPECT_TRUE(delegate.code_set_);
EXPECT_EQ(should_succeed, delegate.code_ == ErrorCode::kSuccess);
if (should_succeed)
- EXPECT_TRUE(install_plan == collector_action.object());
+ EXPECT_EQ(install_plan, collector_action.object());
const base::FilePath kPowerwashMarkerPath(powerwash_marker_file);
string actual_cmd;