blob: 66f8571ab7b76077f065727ee6ab4a081f25ee3c [file] [log] [blame]
Allie Woodeb9e6d82015-04-17 13:55:30 -07001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "update_engine/filesystem_verifier_action.h"
6
7#include <fcntl.h>
8
9#include <set>
10#include <string>
11#include <vector>
12
Alex Deymo20c99202015-07-09 16:14:16 -070013#include <base/bind.h>
Allie Woodeb9e6d82015-04-17 13:55:30 -070014#include <base/posix/eintr_wrapper.h>
15#include <base/strings/string_util.h>
16#include <base/strings/stringprintf.h>
Alex Deymo20c99202015-07-09 16:14:16 -070017#include <chromeos/message_loops/glib_message_loop.h>
18#include <chromeos/message_loops/message_loop_utils.h>
Allie Woodeb9e6d82015-04-17 13:55:30 -070019#include <gmock/gmock.h>
20#include <gtest/gtest.h>
21
22#include "update_engine/fake_system_state.h"
23#include "update_engine/mock_hardware.h"
24#include "update_engine/omaha_hash_calculator.h"
25#include "update_engine/test_utils.h"
26#include "update_engine/utils.h"
27
Alex Deymo20c99202015-07-09 16:14:16 -070028using chromeos::MessageLoop;
Allie Woodeb9e6d82015-04-17 13:55:30 -070029using std::set;
30using std::string;
31using std::vector;
32
33namespace chromeos_update_engine {
34
35class FilesystemVerifierActionTest : public ::testing::Test {
36 protected:
Alex Deymo20c99202015-07-09 16:14:16 -070037 void SetUp() override {
38 loop_.SetAsCurrent();
39 }
40
41 void TearDown() override {
42 EXPECT_EQ(0, chromeos::MessageLoopRunMaxIterations(&loop_, 1));
43 }
44
Allie Woodeb9e6d82015-04-17 13:55:30 -070045 // Returns true iff test has completed successfully.
46 bool DoTest(bool terminate_early,
47 bool hash_fail,
48 PartitionType partition_type);
49
Alex Deymo20c99202015-07-09 16:14:16 -070050 chromeos::GlibMessageLoop loop_;
Allie Woodeb9e6d82015-04-17 13:55:30 -070051 FakeSystemState fake_system_state_;
52};
53
54class FilesystemVerifierActionTestDelegate : public ActionProcessorDelegate {
55 public:
Alex Deymo20c99202015-07-09 16:14:16 -070056 explicit FilesystemVerifierActionTestDelegate(
57 FilesystemVerifierAction* action)
58 : action_(action), ran_(false), code_(ErrorCode::kError) {}
Allie Woodeb9e6d82015-04-17 13:55:30 -070059 void ExitMainLoop() {
Alex Deymo20c99202015-07-09 16:14:16 -070060 // We need to wait for the Action to call Cleanup.
61 if (action_->IsCleanupPending()) {
62 LOG(INFO) << "Waiting for Cleanup() to be called.";
63 MessageLoop::current()->PostDelayedTask(
64 FROM_HERE,
65 base::Bind(&FilesystemVerifierActionTestDelegate::ExitMainLoop,
66 base::Unretained(this)),
67 base::TimeDelta::FromMilliseconds(100));
68 } else {
69 MessageLoop::current()->BreakLoop();
Allie Woodeb9e6d82015-04-17 13:55:30 -070070 }
Allie Woodeb9e6d82015-04-17 13:55:30 -070071 }
72 void ProcessingDone(const ActionProcessor* processor, ErrorCode code) {
73 ExitMainLoop();
74 }
75 void ProcessingStopped(const ActionProcessor* processor) {
76 ExitMainLoop();
77 }
78 void ActionCompleted(ActionProcessor* processor,
79 AbstractAction* action,
80 ErrorCode code) {
81 if (action->Type() == FilesystemVerifierAction::StaticType()) {
82 ran_ = true;
83 code_ = code;
84 }
85 }
86 bool ran() const { return ran_; }
87 ErrorCode code() const { return code_; }
88
89 private:
Allie Woodeb9e6d82015-04-17 13:55:30 -070090 FilesystemVerifierAction* action_;
91 bool ran_;
92 ErrorCode code_;
93};
94
Alex Deymo20c99202015-07-09 16:14:16 -070095void StartProcessorInRunLoop(ActionProcessor* processor,
96 FilesystemVerifierAction* filesystem_copier_action,
97 bool terminate_early) {
Allie Woodeb9e6d82015-04-17 13:55:30 -070098 processor->StartProcessing();
Alex Deymo20c99202015-07-09 16:14:16 -070099 if (terminate_early) {
100 EXPECT_NE(nullptr, filesystem_copier_action);
101 processor->StopProcessing();
Allie Woodeb9e6d82015-04-17 13:55:30 -0700102 }
Allie Woodeb9e6d82015-04-17 13:55:30 -0700103}
104
105// TODO(garnold) Temporarily disabling this test, see chromium-os:31082 for
106// details; still trying to track down the root cause for these rare write
107// failures and whether or not they are due to the test setup or an inherent
108// issue with the chroot environment, library versions we use, etc.
109TEST_F(FilesystemVerifierActionTest, DISABLED_RunAsRootSimpleTest) {
110 ASSERT_EQ(0, getuid());
111 bool test = DoTest(false, false, PartitionType::kKernel);
112 EXPECT_TRUE(test);
113 if (!test)
114 return;
115 test = DoTest(false, false, PartitionType::kRootfs);
116 EXPECT_TRUE(test);
117}
118
119bool FilesystemVerifierActionTest::DoTest(bool terminate_early,
120 bool hash_fail,
121 PartitionType partition_type) {
122 // We need MockHardware to verify MarkUnbootable calls, but don't want
123 // warnings about other usages.
124 testing::NiceMock<MockHardware> mock_hardware;
125 fake_system_state_.set_hardware(&mock_hardware);
126
Allie Woodeb9e6d82015-04-17 13:55:30 -0700127 string a_loop_file;
128
129 if (!(utils::MakeTempFile("a_loop_file.XXXXXX", &a_loop_file, nullptr))) {
130 ADD_FAILURE();
131 return false;
132 }
133 ScopedPathUnlinker a_loop_file_unlinker(a_loop_file);
134
Alex Deymo20c99202015-07-09 16:14:16 -0700135 // Make random data for a.
Allie Woodeb9e6d82015-04-17 13:55:30 -0700136 const size_t kLoopFileSize = 10 * 1024 * 1024 + 512;
137 chromeos::Blob a_loop_data(kLoopFileSize);
138 test_utils::FillWithData(&a_loop_data);
139
140
141 // Write data to disk
142 if (!(test_utils::WriteFileVector(a_loop_file, a_loop_data))) {
143 ADD_FAILURE();
144 return false;
145 }
146
147 // Attach loop devices to the files
148 string a_dev;
149 test_utils::ScopedLoopbackDeviceBinder a_dev_releaser(a_loop_file, &a_dev);
150 if (!(a_dev_releaser.is_bound())) {
151 ADD_FAILURE();
152 return false;
153 }
154
155 LOG(INFO) << "verifying: " << a_loop_file << " (" << a_dev << ")";
156
157 bool success = true;
158
159 // Set up the action objects
160 InstallPlan install_plan;
161 switch (partition_type) {
162 case PartitionType::kRootfs:
163 install_plan.rootfs_size = kLoopFileSize - (hash_fail ? 1 : 0);
164 install_plan.install_path = a_dev;
165 if (!OmahaHashCalculator::RawHashOfData(
166 a_loop_data, &install_plan.rootfs_hash)) {
167 ADD_FAILURE();
168 success = false;
169 }
170 break;
171 case PartitionType::kKernel:
172 install_plan.kernel_size = kLoopFileSize - (hash_fail ? 1 : 0);
173 install_plan.kernel_install_path = a_dev;
174 if (!OmahaHashCalculator::RawHashOfData(
175 a_loop_data, &install_plan.kernel_hash)) {
176 ADD_FAILURE();
177 success = false;
178 }
179 break;
180 case PartitionType::kSourceRootfs:
181 install_plan.source_path = a_dev;
182 if (!OmahaHashCalculator::RawHashOfData(
183 a_loop_data, &install_plan.source_rootfs_hash)) {
184 ADD_FAILURE();
185 success = false;
186 }
187 break;
188 case PartitionType::kSourceKernel:
189 install_plan.kernel_source_path = a_dev;
190 if (!OmahaHashCalculator::RawHashOfData(
191 a_loop_data, &install_plan.source_kernel_hash)) {
192 ADD_FAILURE();
193 success = false;
194 }
195 break;
196 }
197
198 EXPECT_CALL(mock_hardware,
199 MarkKernelUnbootable(a_dev)).Times(
200 partition_type == PartitionType::kKernel ? 1 : 0);
201
202 ActionProcessor processor;
203
204 ObjectFeederAction<InstallPlan> feeder_action;
205 FilesystemVerifierAction copier_action(&fake_system_state_, partition_type);
206 ObjectCollectorAction<InstallPlan> collector_action;
207
208 BondActions(&feeder_action, &copier_action);
209 BondActions(&copier_action, &collector_action);
210
Alex Deymo20c99202015-07-09 16:14:16 -0700211 FilesystemVerifierActionTestDelegate delegate(&copier_action);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700212 processor.set_delegate(&delegate);
213 processor.EnqueueAction(&feeder_action);
214 processor.EnqueueAction(&copier_action);
215 processor.EnqueueAction(&collector_action);
216
217 feeder_action.set_obj(install_plan);
218
Alex Deymo20c99202015-07-09 16:14:16 -0700219 loop_.PostTask(FROM_HERE, base::Bind(&StartProcessorInRunLoop,
220 &processor,
221 &copier_action,
222 terminate_early));
223 loop_.Run();
Allie Woodeb9e6d82015-04-17 13:55:30 -0700224
225 if (!terminate_early) {
226 bool is_delegate_ran = delegate.ran();
227 EXPECT_TRUE(is_delegate_ran);
228 success = success && is_delegate_ran;
229 } else {
230 EXPECT_EQ(ErrorCode::kError, delegate.code());
231 return (ErrorCode::kError == delegate.code());
232 }
233 if (hash_fail) {
234 ErrorCode expected_exit_code =
235 ((partition_type == PartitionType::kKernel ||
236 partition_type == PartitionType::kSourceKernel) ?
237 ErrorCode::kNewKernelVerificationError :
238 ErrorCode::kNewRootfsVerificationError);
239 EXPECT_EQ(expected_exit_code, delegate.code());
240 return (expected_exit_code == delegate.code());
241 }
242 EXPECT_EQ(ErrorCode::kSuccess, delegate.code());
243
244 // Make sure everything in the out_image is there
245 chromeos::Blob a_out;
246 if (!utils::ReadFile(a_dev, &a_out)) {
247 ADD_FAILURE();
248 return false;
249 }
250 const bool is_a_file_reading_eq =
251 test_utils::ExpectVectorsEq(a_loop_data, a_out);
252 EXPECT_TRUE(is_a_file_reading_eq);
253 success = success && is_a_file_reading_eq;
254
255 bool is_install_plan_eq = (collector_action.object() == install_plan);
256 EXPECT_TRUE(is_install_plan_eq);
257 success = success && is_install_plan_eq;
258
259 LOG(INFO) << "Verifying bootable flag on: " << a_dev;
260 bool bootable;
261 EXPECT_TRUE(mock_hardware.fake().IsKernelBootable(a_dev, &bootable));
262 // We should always mark a partition as unbootable if it's a kernel
263 // partition, but never if it's anything else.
264 EXPECT_EQ(bootable, (partition_type != PartitionType::kKernel));
265
266 return success;
267}
268
269class FilesystemVerifierActionTest2Delegate : public ActionProcessorDelegate {
270 public:
271 void ActionCompleted(ActionProcessor* processor,
272 AbstractAction* action,
273 ErrorCode code) {
274 if (action->Type() == FilesystemVerifierAction::StaticType()) {
275 ran_ = true;
276 code_ = code;
277 }
278 }
Allie Woodeb9e6d82015-04-17 13:55:30 -0700279 bool ran_;
280 ErrorCode code_;
281};
282
283TEST_F(FilesystemVerifierActionTest, MissingInputObjectTest) {
284 ActionProcessor processor;
285 FilesystemVerifierActionTest2Delegate delegate;
286
287 processor.set_delegate(&delegate);
288
289 FilesystemVerifierAction copier_action(&fake_system_state_,
290 PartitionType::kRootfs);
291 ObjectCollectorAction<InstallPlan> collector_action;
292
293 BondActions(&copier_action, &collector_action);
294
295 processor.EnqueueAction(&copier_action);
296 processor.EnqueueAction(&collector_action);
297 processor.StartProcessing();
298 EXPECT_FALSE(processor.IsRunning());
299 EXPECT_TRUE(delegate.ran_);
300 EXPECT_EQ(ErrorCode::kError, delegate.code_);
301}
302
303TEST_F(FilesystemVerifierActionTest, NonExistentDriveTest) {
304 ActionProcessor processor;
305 FilesystemVerifierActionTest2Delegate delegate;
306
307 processor.set_delegate(&delegate);
308
309 ObjectFeederAction<InstallPlan> feeder_action;
310 InstallPlan install_plan(false,
311 false,
312 "",
313 0,
314 "",
315 0,
316 "",
317 "/no/such/file",
318 "/no/such/file",
319 "/no/such/file",
320 "/no/such/file",
321 "");
322 feeder_action.set_obj(install_plan);
323 FilesystemVerifierAction verifier_action(&fake_system_state_,
324 PartitionType::kRootfs);
325 ObjectCollectorAction<InstallPlan> collector_action;
326
327 BondActions(&verifier_action, &collector_action);
328
329 processor.EnqueueAction(&feeder_action);
330 processor.EnqueueAction(&verifier_action);
331 processor.EnqueueAction(&collector_action);
332 processor.StartProcessing();
333 EXPECT_FALSE(processor.IsRunning());
334 EXPECT_TRUE(delegate.ran_);
335 EXPECT_EQ(ErrorCode::kError, delegate.code_);
336}
337
338TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashTest) {
339 ASSERT_EQ(0, getuid());
340 EXPECT_TRUE(DoTest(false, false, PartitionType::kRootfs));
341 EXPECT_TRUE(DoTest(false, false, PartitionType::kKernel));
342 EXPECT_TRUE(DoTest(false, false, PartitionType::kSourceRootfs));
343 EXPECT_TRUE(DoTest(false, false, PartitionType::kSourceKernel));
344}
345
346TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashFailTest) {
347 ASSERT_EQ(0, getuid());
348 EXPECT_TRUE(DoTest(false, true, PartitionType::kRootfs));
349 EXPECT_TRUE(DoTest(false, true, PartitionType::kKernel));
350}
351
352TEST_F(FilesystemVerifierActionTest, RunAsRootTerminateEarlyTest) {
353 ASSERT_EQ(0, getuid());
354 EXPECT_TRUE(DoTest(true, false, PartitionType::kKernel));
Alex Deymob9e8e262015-08-03 20:23:03 -0700355 // TerminateEarlyTest may leak some null callbacks from the Stream class.
356 while (loop_.RunOnce(false)) {}
Allie Woodeb9e6d82015-04-17 13:55:30 -0700357}
358
359TEST_F(FilesystemVerifierActionTest, RunAsRootDetermineFilesystemSizeTest) {
360 string img;
361 EXPECT_TRUE(utils::MakeTempFile("img.XXXXXX", &img, nullptr));
362 ScopedPathUnlinker img_unlinker(img);
363 test_utils::CreateExtImageAtPath(img, nullptr);
364 // Extend the "partition" holding the file system from 10MiB to 20MiB.
Alex Deymo20c99202015-07-09 16:14:16 -0700365 EXPECT_EQ(0, truncate(img.c_str(), 20 * 1024 * 1024));
Allie Woodeb9e6d82015-04-17 13:55:30 -0700366
Alex Deymob9e8e262015-08-03 20:23:03 -0700367 {
368 FilesystemVerifierAction action(&fake_system_state_,
369 PartitionType::kSourceKernel);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700370 EXPECT_EQ(kint64max, action.remaining_size_);
Alex Deymob9e8e262015-08-03 20:23:03 -0700371 action.DetermineFilesystemSize(img);
372 EXPECT_EQ(kint64max, action.remaining_size_);
373 }
374 {
375 FilesystemVerifierAction action(&fake_system_state_,
376 PartitionType::kSourceRootfs);
377 action.DetermineFilesystemSize(img);
378 EXPECT_EQ(10 * 1024 * 1024, action.remaining_size_);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700379 }
380}
381
382} // namespace chromeos_update_engine