diff options
author | Alex Deymo <deymo@google.com> | 2016-02-18 11:00:40 -0800 |
---|---|---|
committer | Alex Deymo <deymo@google.com> | 2016-02-29 18:04:36 -0800 |
commit | 390efedcb7e17587da765b6d682077cb7fa46ee1 (patch) | |
tree | 8a8016f57fbcc5c5848ef95536b653171e05cb2d | |
parent | 14fd1ec41d1da4e849b724b762ca111a30c6628c (diff) |
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
-rw-r--r-- | common/constants.cc | 2 | ||||
-rw-r--r-- | common/constants.h | 3 | ||||
-rw-r--r-- | common/test_utils.cc | 2 | ||||
-rw-r--r-- | common/utils.cc | 15 | ||||
-rw-r--r-- | common/utils.h | 11 | ||||
-rw-r--r-- | payload_consumer/delta_performer.cc | 6 | ||||
-rw-r--r-- | payload_consumer/install_plan.cc | 14 | ||||
-rw-r--r-- | payload_consumer/install_plan.h | 5 | ||||
-rw-r--r-- | payload_consumer/postinstall_runner_action.cc | 68 | ||||
-rw-r--r-- | payload_consumer/postinstall_runner_action.h | 4 | ||||
-rw-r--r-- | payload_consumer/postinstall_runner_action_unittest.cc | 3 |
11 files changed, 85 insertions, 48 deletions
diff --git a/common/constants.cc b/common/constants.cc index 6b1d4160..fc6df37d 100644 --- a/common/constants.cc +++ b/common/constants.cc @@ -29,6 +29,8 @@ const char kPrefsSubDirectory[] = "prefs"; 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 f9a43c65..25d587bf 100644 --- a/common/constants.h +++ b/common/constants.h @@ -32,6 +32,9 @@ extern const char kPowerwashSafePrefsSubDirectory[]; // 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 c09096bb..a574863f 100644 --- a/common/test_utils.cc +++ b/common/test_utils.cc @@ -260,7 +260,7 @@ ScopedLoopMounter::ScopedLoopMounter(const string& file_path, 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 91dcfc83..b4956e78 100644 --- a/common/utils.cc +++ b/common/utils.cc @@ -613,9 +613,14 @@ bool MakeTempDirectory(const string& base_dirname_template, 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 @@ bool MountFilesystem(const string& device, 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 5bf14224..8da0726c 100644 --- a/common/utils.h +++ b/common/utils.h @@ -171,10 +171,13 @@ std::string MakePartitionName(const std::string& disk_name, 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 f95679c4..f490c08c 100644 --- a/payload_consumer/delta_performer.cc +++ b/payload_consumer/delta_performer.cc @@ -790,6 +790,12 @@ bool DeltaPerformer::ParseManifestPartitions(ErrorCode* error) { 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 572ff41f..51e85b35 100644 --- a/payload_consumer/install_plan.cc +++ b/payload_consumer/install_plan.cc @@ -59,9 +59,13 @@ bool InstallPlan::operator!=(const InstallPlan& that) const { 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 @@ bool InstallPlan::Partition::operator==( 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 d2f15faf..454dd78a 100644 --- a/payload_consumer/install_plan.h +++ b/payload_consumer/install_plan.h @@ -90,8 +90,11 @@ struct InstallPlan { 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 84ca398c..fe468cc6 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 @@ namespace chromeos_update_engine { 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::PerformAction() { } 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 @@ void PostinstallRunnerAction::PerformPartitionPostinstall() { // 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__ + + 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, temp_rootfs_dir_, MS_RDONLY)) { + 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::PerformPartitionPostinstall() { 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 ab267b82..b4defae4 100644 --- a/payload_consumer/postinstall_runner_action.h +++ b/payload_consumer/postinstall_runner_action.h @@ -63,7 +63,9 @@ class PostinstallRunnerAction : public InstallPlanAction { 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 c4c68b1f..85535d79 100644 --- a/payload_consumer/postinstall_runner_action_unittest.cc +++ b/payload_consumer/postinstall_runner_action_unittest.cc @@ -191,6 +191,7 @@ void PostinstallRunnerActionTest::DoTest( 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 @@ void PostinstallRunnerActionTest::DoTest( 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; |