1// 2// Copyright (C) 2012 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 "update_engine/payload_consumer/filesystem_verifier_action.h" 18 19#include <fcntl.h> 20 21#include <set> 22#include <string> 23#include <vector> 24 25#include <base/bind.h> 26#include <base/posix/eintr_wrapper.h> 27#include <base/strings/string_util.h> 28#include <base/strings/stringprintf.h> 29#include <brillo/bind_lambda.h> 30#include <brillo/message_loops/fake_message_loop.h> 31#include <brillo/message_loops/message_loop_utils.h> 32#include <gmock/gmock.h> 33#include <gtest/gtest.h> 34 35#include "update_engine/common/hash_calculator.h" 36#include "update_engine/common/test_utils.h" 37#include "update_engine/common/utils.h" 38#include "update_engine/payload_consumer/payload_constants.h" 39 40using brillo::MessageLoop; 41using std::set; 42using std::string; 43using std::vector; 44 45namespace chromeos_update_engine { 46 47class FilesystemVerifierActionTest : public ::testing::Test { 48 protected: 49 void SetUp() override { 50 loop_.SetAsCurrent(); 51 } 52 53 void TearDown() override { 54 EXPECT_EQ(0, brillo::MessageLoopRunMaxIterations(&loop_, 1)); 55 } 56 57 // Returns true iff test has completed successfully. 58 bool DoTest(bool terminate_early, bool hash_fail); 59 60 brillo::FakeMessageLoop loop_{nullptr}; 61}; 62 63class FilesystemVerifierActionTestDelegate : public ActionProcessorDelegate { 64 public: 65 explicit FilesystemVerifierActionTestDelegate( 66 FilesystemVerifierAction* action) 67 : action_(action), ran_(false), code_(ErrorCode::kError) {} 68 void ExitMainLoop() { 69 // We need to wait for the Action to call Cleanup. 70 if (action_->IsCleanupPending()) { 71 LOG(INFO) << "Waiting for Cleanup() to be called."; 72 MessageLoop::current()->PostDelayedTask( 73 FROM_HERE, 74 base::Bind(&FilesystemVerifierActionTestDelegate::ExitMainLoop, 75 base::Unretained(this)), 76 base::TimeDelta::FromMilliseconds(100)); 77 } else { 78 MessageLoop::current()->BreakLoop(); 79 } 80 } 81 void ProcessingDone(const ActionProcessor* processor, ErrorCode code) { 82 ExitMainLoop(); 83 } 84 void ProcessingStopped(const ActionProcessor* processor) { 85 ExitMainLoop(); 86 } 87 void ActionCompleted(ActionProcessor* processor, 88 AbstractAction* action, 89 ErrorCode code) { 90 if (action->Type() == FilesystemVerifierAction::StaticType()) { 91 ran_ = true; 92 code_ = code; 93 } 94 } 95 bool ran() const { return ran_; } 96 ErrorCode code() const { return code_; } 97 98 private: 99 FilesystemVerifierAction* action_; 100 bool ran_; 101 ErrorCode code_; 102}; 103 104void StartProcessorInRunLoop(ActionProcessor* processor, 105 FilesystemVerifierAction* filesystem_copier_action, 106 bool terminate_early) { 107 processor->StartProcessing(); 108 if (terminate_early) { 109 EXPECT_NE(nullptr, filesystem_copier_action); 110 processor->StopProcessing(); 111 } 112} 113 114bool FilesystemVerifierActionTest::DoTest(bool terminate_early, 115 bool hash_fail) { 116 string a_loop_file; 117 118 if (!(utils::MakeTempFile("a_loop_file.XXXXXX", &a_loop_file, nullptr))) { 119 ADD_FAILURE(); 120 return false; 121 } 122 ScopedPathUnlinker a_loop_file_unlinker(a_loop_file); 123 124 // Make random data for a. 125 const size_t kLoopFileSize = 10 * 1024 * 1024 + 512; 126 brillo::Blob a_loop_data(kLoopFileSize); 127 test_utils::FillWithData(&a_loop_data); 128 129 // Write data to disk 130 if (!(test_utils::WriteFileVector(a_loop_file, a_loop_data))) { 131 ADD_FAILURE(); 132 return false; 133 } 134 135 // Attach loop devices to the files 136 string a_dev; 137 test_utils::ScopedLoopbackDeviceBinder a_dev_releaser( 138 a_loop_file, false, &a_dev); 139 if (!(a_dev_releaser.is_bound())) { 140 ADD_FAILURE(); 141 return false; 142 } 143 144 LOG(INFO) << "verifying: " << a_loop_file << " (" << a_dev << ")"; 145 146 bool success = true; 147 148 // Set up the action objects 149 InstallPlan install_plan; 150 install_plan.source_slot = 0; 151 install_plan.target_slot = 1; 152 InstallPlan::Partition part; 153 part.name = "part"; 154 part.target_size = kLoopFileSize - (hash_fail ? 1 : 0); 155 part.target_path = a_dev; 156 if (!HashCalculator::RawHashOfData(a_loop_data, &part.target_hash)) { 157 ADD_FAILURE(); 158 success = false; 159 } 160 part.source_size = kLoopFileSize; 161 part.source_path = a_dev; 162 if (!HashCalculator::RawHashOfData(a_loop_data, &part.source_hash)) { 163 ADD_FAILURE(); 164 success = false; 165 } 166 install_plan.partitions = {part}; 167 168 ActionProcessor processor; 169 170 ObjectFeederAction<InstallPlan> feeder_action; 171 FilesystemVerifierAction copier_action; 172 ObjectCollectorAction<InstallPlan> collector_action; 173 174 BondActions(&feeder_action, &copier_action); 175 BondActions(&copier_action, &collector_action); 176 177 FilesystemVerifierActionTestDelegate delegate(&copier_action); 178 processor.set_delegate(&delegate); 179 processor.EnqueueAction(&feeder_action); 180 processor.EnqueueAction(&copier_action); 181 processor.EnqueueAction(&collector_action); 182 183 feeder_action.set_obj(install_plan); 184 185 loop_.PostTask(FROM_HERE, base::Bind(&StartProcessorInRunLoop, 186 &processor, 187 &copier_action, 188 terminate_early)); 189 loop_.Run(); 190 191 if (!terminate_early) { 192 bool is_delegate_ran = delegate.ran(); 193 EXPECT_TRUE(is_delegate_ran); 194 success = success && is_delegate_ran; 195 } else { 196 EXPECT_EQ(ErrorCode::kError, delegate.code()); 197 return (ErrorCode::kError == delegate.code()); 198 } 199 if (hash_fail) { 200 ErrorCode expected_exit_code = ErrorCode::kNewRootfsVerificationError; 201 EXPECT_EQ(expected_exit_code, delegate.code()); 202 return (expected_exit_code == delegate.code()); 203 } 204 EXPECT_EQ(ErrorCode::kSuccess, delegate.code()); 205 206 // Make sure everything in the out_image is there 207 brillo::Blob a_out; 208 if (!utils::ReadFile(a_dev, &a_out)) { 209 ADD_FAILURE(); 210 return false; 211 } 212 const bool is_a_file_reading_eq = 213 test_utils::ExpectVectorsEq(a_loop_data, a_out); 214 EXPECT_TRUE(is_a_file_reading_eq); 215 success = success && is_a_file_reading_eq; 216 217 bool is_install_plan_eq = (collector_action.object() == install_plan); 218 EXPECT_TRUE(is_install_plan_eq); 219 success = success && is_install_plan_eq; 220 return success; 221} 222 223class FilesystemVerifierActionTest2Delegate : public ActionProcessorDelegate { 224 public: 225 void ActionCompleted(ActionProcessor* processor, 226 AbstractAction* action, 227 ErrorCode code) { 228 if (action->Type() == FilesystemVerifierAction::StaticType()) { 229 ran_ = true; 230 code_ = code; 231 } 232 } 233 bool ran_; 234 ErrorCode code_; 235}; 236 237TEST_F(FilesystemVerifierActionTest, MissingInputObjectTest) { 238 ActionProcessor processor; 239 FilesystemVerifierActionTest2Delegate delegate; 240 241 processor.set_delegate(&delegate); 242 243 FilesystemVerifierAction copier_action; 244 ObjectCollectorAction<InstallPlan> collector_action; 245 246 BondActions(&copier_action, &collector_action); 247 248 processor.EnqueueAction(&copier_action); 249 processor.EnqueueAction(&collector_action); 250 processor.StartProcessing(); 251 EXPECT_FALSE(processor.IsRunning()); 252 EXPECT_TRUE(delegate.ran_); 253 EXPECT_EQ(ErrorCode::kError, delegate.code_); 254} 255 256TEST_F(FilesystemVerifierActionTest, NonExistentDriveTest) { 257 ActionProcessor processor; 258 FilesystemVerifierActionTest2Delegate delegate; 259 260 processor.set_delegate(&delegate); 261 262 ObjectFeederAction<InstallPlan> feeder_action; 263 InstallPlan install_plan; 264 InstallPlan::Partition part; 265 part.name = "nope"; 266 part.source_path = "/no/such/file"; 267 part.target_path = "/no/such/file"; 268 install_plan.partitions = {part}; 269 270 feeder_action.set_obj(install_plan); 271 FilesystemVerifierAction verifier_action; 272 ObjectCollectorAction<InstallPlan> collector_action; 273 274 BondActions(&verifier_action, &collector_action); 275 276 processor.EnqueueAction(&feeder_action); 277 processor.EnqueueAction(&verifier_action); 278 processor.EnqueueAction(&collector_action); 279 processor.StartProcessing(); 280 EXPECT_FALSE(processor.IsRunning()); 281 EXPECT_TRUE(delegate.ran_); 282 EXPECT_EQ(ErrorCode::kError, delegate.code_); 283} 284 285TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashTest) { 286 ASSERT_EQ(0U, getuid()); 287 EXPECT_TRUE(DoTest(false, false)); 288} 289 290TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashFailTest) { 291 ASSERT_EQ(0U, getuid()); 292 EXPECT_TRUE(DoTest(false, true)); 293} 294 295TEST_F(FilesystemVerifierActionTest, RunAsRootTerminateEarlyTest) { 296 ASSERT_EQ(0U, getuid()); 297 EXPECT_TRUE(DoTest(true, false)); 298 // TerminateEarlyTest may leak some null callbacks from the Stream class. 299 while (loop_.RunOnce(false)) {} 300} 301 302} // namespace chromeos_update_engine 303