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