You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
302 lines
9.0 KiB
302 lines
9.0 KiB
// |
|
// Copyright (C) 2012 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 "update_engine/payload_consumer/filesystem_verifier_action.h" |
|
|
|
#include <fcntl.h> |
|
|
|
#include <set> |
|
#include <string> |
|
#include <vector> |
|
|
|
#include <base/bind.h> |
|
#include <base/posix/eintr_wrapper.h> |
|
#include <base/strings/string_util.h> |
|
#include <base/strings/stringprintf.h> |
|
#include <brillo/bind_lambda.h> |
|
#include <brillo/message_loops/fake_message_loop.h> |
|
#include <brillo/message_loops/message_loop_utils.h> |
|
#include <gmock/gmock.h> |
|
#include <gtest/gtest.h> |
|
|
|
#include "update_engine/common/hash_calculator.h" |
|
#include "update_engine/common/test_utils.h" |
|
#include "update_engine/common/utils.h" |
|
#include "update_engine/payload_consumer/payload_constants.h" |
|
|
|
using brillo::MessageLoop; |
|
using std::set; |
|
using std::string; |
|
using std::vector; |
|
|
|
namespace chromeos_update_engine { |
|
|
|
class FilesystemVerifierActionTest : public ::testing::Test { |
|
protected: |
|
void SetUp() override { |
|
loop_.SetAsCurrent(); |
|
} |
|
|
|
void TearDown() override { |
|
EXPECT_EQ(0, brillo::MessageLoopRunMaxIterations(&loop_, 1)); |
|
} |
|
|
|
// Returns true iff test has completed successfully. |
|
bool DoTest(bool terminate_early, bool hash_fail); |
|
|
|
brillo::FakeMessageLoop loop_{nullptr}; |
|
}; |
|
|
|
class FilesystemVerifierActionTestDelegate : public ActionProcessorDelegate { |
|
public: |
|
explicit FilesystemVerifierActionTestDelegate( |
|
FilesystemVerifierAction* action) |
|
: action_(action), ran_(false), code_(ErrorCode::kError) {} |
|
void ExitMainLoop() { |
|
// We need to wait for the Action to call Cleanup. |
|
if (action_->IsCleanupPending()) { |
|
LOG(INFO) << "Waiting for Cleanup() to be called."; |
|
MessageLoop::current()->PostDelayedTask( |
|
FROM_HERE, |
|
base::Bind(&FilesystemVerifierActionTestDelegate::ExitMainLoop, |
|
base::Unretained(this)), |
|
base::TimeDelta::FromMilliseconds(100)); |
|
} else { |
|
MessageLoop::current()->BreakLoop(); |
|
} |
|
} |
|
void ProcessingDone(const ActionProcessor* processor, ErrorCode code) { |
|
ExitMainLoop(); |
|
} |
|
void ProcessingStopped(const ActionProcessor* processor) { |
|
ExitMainLoop(); |
|
} |
|
void ActionCompleted(ActionProcessor* processor, |
|
AbstractAction* action, |
|
ErrorCode code) { |
|
if (action->Type() == FilesystemVerifierAction::StaticType()) { |
|
ran_ = true; |
|
code_ = code; |
|
} |
|
} |
|
bool ran() const { return ran_; } |
|
ErrorCode code() const { return code_; } |
|
|
|
private: |
|
FilesystemVerifierAction* action_; |
|
bool ran_; |
|
ErrorCode code_; |
|
}; |
|
|
|
void StartProcessorInRunLoop(ActionProcessor* processor, |
|
FilesystemVerifierAction* filesystem_copier_action, |
|
bool terminate_early) { |
|
processor->StartProcessing(); |
|
if (terminate_early) { |
|
EXPECT_NE(nullptr, filesystem_copier_action); |
|
processor->StopProcessing(); |
|
} |
|
} |
|
|
|
bool FilesystemVerifierActionTest::DoTest(bool terminate_early, |
|
bool hash_fail) { |
|
string a_loop_file; |
|
|
|
if (!(utils::MakeTempFile("a_loop_file.XXXXXX", &a_loop_file, nullptr))) { |
|
ADD_FAILURE(); |
|
return false; |
|
} |
|
ScopedPathUnlinker a_loop_file_unlinker(a_loop_file); |
|
|
|
// Make random data for a. |
|
const size_t kLoopFileSize = 10 * 1024 * 1024 + 512; |
|
brillo::Blob a_loop_data(kLoopFileSize); |
|
test_utils::FillWithData(&a_loop_data); |
|
|
|
// Write data to disk |
|
if (!(test_utils::WriteFileVector(a_loop_file, a_loop_data))) { |
|
ADD_FAILURE(); |
|
return false; |
|
} |
|
|
|
// Attach loop devices to the files |
|
string a_dev; |
|
test_utils::ScopedLoopbackDeviceBinder a_dev_releaser( |
|
a_loop_file, false, &a_dev); |
|
if (!(a_dev_releaser.is_bound())) { |
|
ADD_FAILURE(); |
|
return false; |
|
} |
|
|
|
LOG(INFO) << "verifying: " << a_loop_file << " (" << a_dev << ")"; |
|
|
|
bool success = true; |
|
|
|
// Set up the action objects |
|
InstallPlan install_plan; |
|
install_plan.source_slot = 0; |
|
install_plan.target_slot = 1; |
|
InstallPlan::Partition part; |
|
part.name = "part"; |
|
part.target_size = kLoopFileSize - (hash_fail ? 1 : 0); |
|
part.target_path = a_dev; |
|
if (!HashCalculator::RawHashOfData(a_loop_data, &part.target_hash)) { |
|
ADD_FAILURE(); |
|
success = false; |
|
} |
|
part.source_size = kLoopFileSize; |
|
part.source_path = a_dev; |
|
if (!HashCalculator::RawHashOfData(a_loop_data, &part.source_hash)) { |
|
ADD_FAILURE(); |
|
success = false; |
|
} |
|
install_plan.partitions = {part}; |
|
|
|
ActionProcessor processor; |
|
|
|
ObjectFeederAction<InstallPlan> feeder_action; |
|
FilesystemVerifierAction copier_action; |
|
ObjectCollectorAction<InstallPlan> collector_action; |
|
|
|
BondActions(&feeder_action, &copier_action); |
|
BondActions(&copier_action, &collector_action); |
|
|
|
FilesystemVerifierActionTestDelegate delegate(&copier_action); |
|
processor.set_delegate(&delegate); |
|
processor.EnqueueAction(&feeder_action); |
|
processor.EnqueueAction(&copier_action); |
|
processor.EnqueueAction(&collector_action); |
|
|
|
feeder_action.set_obj(install_plan); |
|
|
|
loop_.PostTask(FROM_HERE, base::Bind(&StartProcessorInRunLoop, |
|
&processor, |
|
&copier_action, |
|
terminate_early)); |
|
loop_.Run(); |
|
|
|
if (!terminate_early) { |
|
bool is_delegate_ran = delegate.ran(); |
|
EXPECT_TRUE(is_delegate_ran); |
|
success = success && is_delegate_ran; |
|
} else { |
|
EXPECT_EQ(ErrorCode::kError, delegate.code()); |
|
return (ErrorCode::kError == delegate.code()); |
|
} |
|
if (hash_fail) { |
|
ErrorCode expected_exit_code = ErrorCode::kNewRootfsVerificationError; |
|
EXPECT_EQ(expected_exit_code, delegate.code()); |
|
return (expected_exit_code == delegate.code()); |
|
} |
|
EXPECT_EQ(ErrorCode::kSuccess, delegate.code()); |
|
|
|
// Make sure everything in the out_image is there |
|
brillo::Blob a_out; |
|
if (!utils::ReadFile(a_dev, &a_out)) { |
|
ADD_FAILURE(); |
|
return false; |
|
} |
|
const bool is_a_file_reading_eq = |
|
test_utils::ExpectVectorsEq(a_loop_data, a_out); |
|
EXPECT_TRUE(is_a_file_reading_eq); |
|
success = success && is_a_file_reading_eq; |
|
|
|
bool is_install_plan_eq = (collector_action.object() == install_plan); |
|
EXPECT_TRUE(is_install_plan_eq); |
|
success = success && is_install_plan_eq; |
|
return success; |
|
} |
|
|
|
class FilesystemVerifierActionTest2Delegate : public ActionProcessorDelegate { |
|
public: |
|
void ActionCompleted(ActionProcessor* processor, |
|
AbstractAction* action, |
|
ErrorCode code) { |
|
if (action->Type() == FilesystemVerifierAction::StaticType()) { |
|
ran_ = true; |
|
code_ = code; |
|
} |
|
} |
|
bool ran_; |
|
ErrorCode code_; |
|
}; |
|
|
|
TEST_F(FilesystemVerifierActionTest, MissingInputObjectTest) { |
|
ActionProcessor processor; |
|
FilesystemVerifierActionTest2Delegate delegate; |
|
|
|
processor.set_delegate(&delegate); |
|
|
|
FilesystemVerifierAction copier_action; |
|
ObjectCollectorAction<InstallPlan> collector_action; |
|
|
|
BondActions(&copier_action, &collector_action); |
|
|
|
processor.EnqueueAction(&copier_action); |
|
processor.EnqueueAction(&collector_action); |
|
processor.StartProcessing(); |
|
EXPECT_FALSE(processor.IsRunning()); |
|
EXPECT_TRUE(delegate.ran_); |
|
EXPECT_EQ(ErrorCode::kError, delegate.code_); |
|
} |
|
|
|
TEST_F(FilesystemVerifierActionTest, NonExistentDriveTest) { |
|
ActionProcessor processor; |
|
FilesystemVerifierActionTest2Delegate delegate; |
|
|
|
processor.set_delegate(&delegate); |
|
|
|
ObjectFeederAction<InstallPlan> feeder_action; |
|
InstallPlan install_plan; |
|
InstallPlan::Partition part; |
|
part.name = "nope"; |
|
part.source_path = "/no/such/file"; |
|
part.target_path = "/no/such/file"; |
|
install_plan.partitions = {part}; |
|
|
|
feeder_action.set_obj(install_plan); |
|
FilesystemVerifierAction verifier_action; |
|
ObjectCollectorAction<InstallPlan> collector_action; |
|
|
|
BondActions(&verifier_action, &collector_action); |
|
|
|
processor.EnqueueAction(&feeder_action); |
|
processor.EnqueueAction(&verifier_action); |
|
processor.EnqueueAction(&collector_action); |
|
processor.StartProcessing(); |
|
EXPECT_FALSE(processor.IsRunning()); |
|
EXPECT_TRUE(delegate.ran_); |
|
EXPECT_EQ(ErrorCode::kError, delegate.code_); |
|
} |
|
|
|
TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashTest) { |
|
ASSERT_EQ(0U, getuid()); |
|
EXPECT_TRUE(DoTest(false, false)); |
|
} |
|
|
|
TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashFailTest) { |
|
ASSERT_EQ(0U, getuid()); |
|
EXPECT_TRUE(DoTest(false, true)); |
|
} |
|
|
|
TEST_F(FilesystemVerifierActionTest, RunAsRootTerminateEarlyTest) { |
|
ASSERT_EQ(0U, getuid()); |
|
EXPECT_TRUE(DoTest(true, false)); |
|
// TerminateEarlyTest may leak some null callbacks from the Stream class. |
|
while (loop_.RunOnce(false)) {} |
|
} |
|
|
|
} // namespace chromeos_update_engine
|
|
|