filesystem_verifier_action_unittest.cc revision 39910dcd1d68987ccee7c3031dc269233a8490bb
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/fake_boot_control.h" 36#include "update_engine/common/hash_calculator.h" 37#include "update_engine/common/test_utils.h" 38#include "update_engine/common/utils.h" 39#include "update_engine/payload_consumer/payload_constants.h" 40 41using brillo::MessageLoop; 42using std::set; 43using std::string; 44using std::vector; 45 46namespace chromeos_update_engine { 47 48class FilesystemVerifierActionTest : public ::testing::Test { 49 protected: 50 void SetUp() override { 51 loop_.SetAsCurrent(); 52 } 53 54 void TearDown() override { 55 EXPECT_EQ(0, brillo::MessageLoopRunMaxIterations(&loop_, 1)); 56 } 57 58 // Returns true iff test has completed successfully. 59 bool DoTest(bool terminate_early, 60 bool hash_fail, 61 VerifierMode verifier_mode); 62 63 brillo::FakeMessageLoop loop_{nullptr}; 64 FakeBootControl fake_boot_control_; 65}; 66 67class FilesystemVerifierActionTestDelegate : public ActionProcessorDelegate { 68 public: 69 explicit FilesystemVerifierActionTestDelegate( 70 FilesystemVerifierAction* action) 71 : action_(action), ran_(false), code_(ErrorCode::kError) {} 72 void ExitMainLoop() { 73 // We need to wait for the Action to call Cleanup. 74 if (action_->IsCleanupPending()) { 75 LOG(INFO) << "Waiting for Cleanup() to be called."; 76 MessageLoop::current()->PostDelayedTask( 77 FROM_HERE, 78 base::Bind(&FilesystemVerifierActionTestDelegate::ExitMainLoop, 79 base::Unretained(this)), 80 base::TimeDelta::FromMilliseconds(100)); 81 } else { 82 MessageLoop::current()->BreakLoop(); 83 } 84 } 85 void ProcessingDone(const ActionProcessor* processor, ErrorCode code) { 86 ExitMainLoop(); 87 } 88 void ProcessingStopped(const ActionProcessor* processor) { 89 ExitMainLoop(); 90 } 91 void ActionCompleted(ActionProcessor* processor, 92 AbstractAction* action, 93 ErrorCode code) { 94 if (action->Type() == FilesystemVerifierAction::StaticType()) { 95 ran_ = true; 96 code_ = code; 97 } 98 } 99 bool ran() const { return ran_; } 100 ErrorCode code() const { return code_; } 101 102 private: 103 FilesystemVerifierAction* action_; 104 bool ran_; 105 ErrorCode code_; 106}; 107 108void StartProcessorInRunLoop(ActionProcessor* processor, 109 FilesystemVerifierAction* filesystem_copier_action, 110 bool terminate_early) { 111 processor->StartProcessing(); 112 if (terminate_early) { 113 EXPECT_NE(nullptr, filesystem_copier_action); 114 processor->StopProcessing(); 115 } 116} 117 118// TODO(garnold) Temporarily disabling this test, see chromium-os:31082 for 119// details; still trying to track down the root cause for these rare write 120// failures and whether or not they are due to the test setup or an inherent 121// issue with the chroot environment, library versions we use, etc. 122TEST_F(FilesystemVerifierActionTest, DISABLED_RunAsRootSimpleTest) { 123 ASSERT_EQ(0, getuid()); 124 bool test = DoTest(false, false, VerifierMode::kComputeSourceHash); 125 EXPECT_TRUE(test); 126 if (!test) 127 return; 128 test = DoTest(false, false, VerifierMode::kVerifyTargetHash); 129 EXPECT_TRUE(test); 130} 131 132bool FilesystemVerifierActionTest::DoTest(bool terminate_early, 133 bool hash_fail, 134 VerifierMode verifier_mode) { 135 string a_loop_file; 136 137 if (!(utils::MakeTempFile("a_loop_file.XXXXXX", &a_loop_file, nullptr))) { 138 ADD_FAILURE(); 139 return false; 140 } 141 ScopedPathUnlinker a_loop_file_unlinker(a_loop_file); 142 143 // Make random data for a. 144 const size_t kLoopFileSize = 10 * 1024 * 1024 + 512; 145 brillo::Blob a_loop_data(kLoopFileSize); 146 test_utils::FillWithData(&a_loop_data); 147 148 // Write data to disk 149 if (!(test_utils::WriteFileVector(a_loop_file, a_loop_data))) { 150 ADD_FAILURE(); 151 return false; 152 } 153 154 // Attach loop devices to the files 155 string a_dev; 156 test_utils::ScopedLoopbackDeviceBinder a_dev_releaser(a_loop_file, &a_dev); 157 if (!(a_dev_releaser.is_bound())) { 158 ADD_FAILURE(); 159 return false; 160 } 161 162 LOG(INFO) << "verifying: " << a_loop_file << " (" << a_dev << ")"; 163 164 bool success = true; 165 166 // Set up the action objects 167 InstallPlan install_plan; 168 install_plan.source_slot = 0; 169 install_plan.target_slot = 1; 170 InstallPlan::Partition part; 171 part.name = "part"; 172 switch (verifier_mode) { 173 case VerifierMode::kVerifyTargetHash: 174 part.target_size = kLoopFileSize - (hash_fail ? 1 : 0); 175 part.target_path = a_dev; 176 fake_boot_control_.SetPartitionDevice( 177 part.name, install_plan.target_slot, a_dev); 178 if (!HashCalculator::RawHashOfData(a_loop_data, &part.target_hash)) { 179 ADD_FAILURE(); 180 success = false; 181 } 182 break; 183 case VerifierMode::kComputeSourceHash: 184 part.source_size = kLoopFileSize; 185 part.source_path = a_dev; 186 fake_boot_control_.SetPartitionDevice( 187 part.name, install_plan.source_slot, a_dev); 188 if (!HashCalculator::RawHashOfData(a_loop_data, &part.source_hash)) { 189 ADD_FAILURE(); 190 success = false; 191 } 192 break; 193 } 194 install_plan.partitions = {part}; 195 196 ActionProcessor processor; 197 198 ObjectFeederAction<InstallPlan> feeder_action; 199 FilesystemVerifierAction copier_action(&fake_boot_control_, verifier_mode); 200 ObjectCollectorAction<InstallPlan> collector_action; 201 202 BondActions(&feeder_action, &copier_action); 203 BondActions(&copier_action, &collector_action); 204 205 FilesystemVerifierActionTestDelegate delegate(&copier_action); 206 processor.set_delegate(&delegate); 207 processor.EnqueueAction(&feeder_action); 208 processor.EnqueueAction(&copier_action); 209 processor.EnqueueAction(&collector_action); 210 211 feeder_action.set_obj(install_plan); 212 213 loop_.PostTask(FROM_HERE, base::Bind(&StartProcessorInRunLoop, 214 &processor, 215 &copier_action, 216 terminate_early)); 217 loop_.Run(); 218 219 if (!terminate_early) { 220 bool is_delegate_ran = delegate.ran(); 221 EXPECT_TRUE(is_delegate_ran); 222 success = success && is_delegate_ran; 223 } else { 224 EXPECT_EQ(ErrorCode::kError, delegate.code()); 225 return (ErrorCode::kError == delegate.code()); 226 } 227 if (hash_fail) { 228 ErrorCode expected_exit_code = ErrorCode::kNewRootfsVerificationError; 229 EXPECT_EQ(expected_exit_code, delegate.code()); 230 return (expected_exit_code == delegate.code()); 231 } 232 EXPECT_EQ(ErrorCode::kSuccess, delegate.code()); 233 234 // Make sure everything in the out_image is there 235 brillo::Blob a_out; 236 if (!utils::ReadFile(a_dev, &a_out)) { 237 ADD_FAILURE(); 238 return false; 239 } 240 const bool is_a_file_reading_eq = 241 test_utils::ExpectVectorsEq(a_loop_data, a_out); 242 EXPECT_TRUE(is_a_file_reading_eq); 243 success = success && is_a_file_reading_eq; 244 245 bool is_install_plan_eq = (collector_action.object() == install_plan); 246 EXPECT_TRUE(is_install_plan_eq); 247 success = success && is_install_plan_eq; 248 return success; 249} 250 251class FilesystemVerifierActionTest2Delegate : public ActionProcessorDelegate { 252 public: 253 void ActionCompleted(ActionProcessor* processor, 254 AbstractAction* action, 255 ErrorCode code) { 256 if (action->Type() == FilesystemVerifierAction::StaticType()) { 257 ran_ = true; 258 code_ = code; 259 } 260 } 261 bool ran_; 262 ErrorCode code_; 263}; 264 265TEST_F(FilesystemVerifierActionTest, MissingInputObjectTest) { 266 ActionProcessor processor; 267 FilesystemVerifierActionTest2Delegate delegate; 268 269 processor.set_delegate(&delegate); 270 271 FilesystemVerifierAction copier_action(&fake_boot_control_, 272 VerifierMode::kVerifyTargetHash); 273 ObjectCollectorAction<InstallPlan> collector_action; 274 275 BondActions(&copier_action, &collector_action); 276 277 processor.EnqueueAction(&copier_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, NonExistentDriveTest) { 286 ActionProcessor processor; 287 FilesystemVerifierActionTest2Delegate delegate; 288 289 processor.set_delegate(&delegate); 290 291 ObjectFeederAction<InstallPlan> feeder_action; 292 InstallPlan install_plan(false, 293 false, 294 "", 295 0, 296 "", 297 0, 298 "", 299 ""); 300 InstallPlan::Partition part; 301 part.name = "nope"; 302 part.source_path = "/no/such/file"; 303 part.target_path = "/no/such/file"; 304 install_plan.partitions = {part}; 305 306 feeder_action.set_obj(install_plan); 307 FilesystemVerifierAction verifier_action(&fake_boot_control_, 308 VerifierMode::kVerifyTargetHash); 309 ObjectCollectorAction<InstallPlan> collector_action; 310 311 BondActions(&verifier_action, &collector_action); 312 313 processor.EnqueueAction(&feeder_action); 314 processor.EnqueueAction(&verifier_action); 315 processor.EnqueueAction(&collector_action); 316 processor.StartProcessing(); 317 EXPECT_FALSE(processor.IsRunning()); 318 EXPECT_TRUE(delegate.ran_); 319 EXPECT_EQ(ErrorCode::kError, delegate.code_); 320} 321 322TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashTest) { 323 ASSERT_EQ(0, getuid()); 324 EXPECT_TRUE(DoTest(false, false, VerifierMode::kVerifyTargetHash)); 325 EXPECT_TRUE(DoTest(false, false, VerifierMode::kComputeSourceHash)); 326} 327 328TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashFailTest) { 329 ASSERT_EQ(0, getuid()); 330 EXPECT_TRUE(DoTest(false, true, VerifierMode::kVerifyTargetHash)); 331} 332 333TEST_F(FilesystemVerifierActionTest, RunAsRootTerminateEarlyTest) { 334 ASSERT_EQ(0, getuid()); 335 EXPECT_TRUE(DoTest(true, false, VerifierMode::kVerifyTargetHash)); 336 // TerminateEarlyTest may leak some null callbacks from the Stream class. 337 while (loop_.RunOnce(false)) {} 338} 339 340// Test that the rootfs and kernel size used for hashing in delta payloads for 341// major version 1 is properly read. 342TEST_F(FilesystemVerifierActionTest, RunAsRootDetermineLegacySizeTest) { 343 string img; 344 EXPECT_TRUE(utils::MakeTempFile("img.XXXXXX", &img, nullptr)); 345 ScopedPathUnlinker img_unlinker(img); 346 test_utils::CreateExtImageAtPath(img, nullptr); 347 // Extend the "partition" holding the file system from 10MiB to 20MiB. 348 EXPECT_EQ(0, truncate(img.c_str(), 20 * 1024 * 1024)); 349 350 InstallPlan install_plan; 351 install_plan.source_slot = 1; 352 353 fake_boot_control_.SetPartitionDevice( 354 kLegacyPartitionNameRoot, install_plan.source_slot, img); 355 fake_boot_control_.SetPartitionDevice( 356 kLegacyPartitionNameKernel, install_plan.source_slot, img); 357 FilesystemVerifierAction action(&fake_boot_control_, 358 VerifierMode::kComputeSourceHash); 359 360 ObjectFeederAction<InstallPlan> feeder_action; 361 feeder_action.set_obj(install_plan); 362 363 ObjectCollectorAction<InstallPlan> collector_action; 364 365 BondActions(&feeder_action, &action); 366 BondActions(&action, &collector_action); 367 ActionProcessor processor; 368 processor.EnqueueAction(&feeder_action); 369 processor.EnqueueAction(&action); 370 processor.EnqueueAction(&collector_action); 371 372 loop_.PostTask(FROM_HERE, 373 base::Bind([&processor]{ processor.StartProcessing(); })); 374 loop_.Run(); 375 install_plan = collector_action.object(); 376 377 ASSERT_EQ(2, install_plan.partitions.size()); 378 // When computing the size of the rootfs on legacy delta updates we use the 379 // size of the filesystem, but when updating the kernel we use the whole 380 // partition. 381 EXPECT_EQ(10 * 1024 * 1024, install_plan.partitions[0].source_size); 382 EXPECT_EQ(kLegacyPartitionNameRoot, install_plan.partitions[0].name); 383 EXPECT_EQ(20 * 1024 * 1024, install_plan.partitions[1].source_size); 384 EXPECT_EQ(kLegacyPartitionNameKernel, install_plan.partitions[1].name); 385} 386 387} // namespace chromeos_update_engine 388