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