blob: 37cdf3801726276728882ef146547afb1a0dfb13 [file] [log] [blame]
Alex Deymoaea4c1c2015-08-19 20:24:43 -07001//
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//
Allie Woodeb9e6d82015-04-17 13:55:30 -070016
17#include "update_engine/filesystem_verifier_action.h"
18
19#include <fcntl.h>
20
21#include <set>
22#include <string>
23#include <vector>
24
Alex Deymo20c99202015-07-09 16:14:16 -070025#include <base/bind.h>
Allie Woodeb9e6d82015-04-17 13:55:30 -070026#include <base/posix/eintr_wrapper.h>
27#include <base/strings/string_util.h>
28#include <base/strings/stringprintf.h>
Alex Deymo0b3db6b2015-08-10 15:19:37 -070029#include <chromeos/message_loops/fake_message_loop.h>
Alex Deymo20c99202015-07-09 16:14:16 -070030#include <chromeos/message_loops/message_loop_utils.h>
Allie Woodeb9e6d82015-04-17 13:55:30 -070031#include <gmock/gmock.h>
32#include <gtest/gtest.h>
33
34#include "update_engine/fake_system_state.h"
35#include "update_engine/mock_hardware.h"
36#include "update_engine/omaha_hash_calculator.h"
37#include "update_engine/test_utils.h"
38#include "update_engine/utils.h"
39
Alex Deymo20c99202015-07-09 16:14:16 -070040using chromeos::MessageLoop;
Allie Woodeb9e6d82015-04-17 13:55:30 -070041using std::set;
42using std::string;
43using std::vector;
44
45namespace chromeos_update_engine {
46
47class FilesystemVerifierActionTest : public ::testing::Test {
48 protected:
Alex Deymo20c99202015-07-09 16:14:16 -070049 void SetUp() override {
50 loop_.SetAsCurrent();
51 }
52
53 void TearDown() override {
54 EXPECT_EQ(0, chromeos::MessageLoopRunMaxIterations(&loop_, 1));
55 }
56
Allie Woodeb9e6d82015-04-17 13:55:30 -070057 // Returns true iff test has completed successfully.
58 bool DoTest(bool terminate_early,
59 bool hash_fail,
60 PartitionType partition_type);
61
Alex Deymo0b3db6b2015-08-10 15:19:37 -070062 chromeos::FakeMessageLoop loop_{nullptr};
Allie Woodeb9e6d82015-04-17 13:55:30 -070063 FakeSystemState fake_system_state_;
64};
65
66class FilesystemVerifierActionTestDelegate : public ActionProcessorDelegate {
67 public:
Alex Deymo20c99202015-07-09 16:14:16 -070068 explicit FilesystemVerifierActionTestDelegate(
69 FilesystemVerifierAction* action)
70 : action_(action), ran_(false), code_(ErrorCode::kError) {}
Allie Woodeb9e6d82015-04-17 13:55:30 -070071 void ExitMainLoop() {
Alex Deymo20c99202015-07-09 16:14:16 -070072 // We need to wait for the Action to call Cleanup.
73 if (action_->IsCleanupPending()) {
74 LOG(INFO) << "Waiting for Cleanup() to be called.";
75 MessageLoop::current()->PostDelayedTask(
76 FROM_HERE,
77 base::Bind(&FilesystemVerifierActionTestDelegate::ExitMainLoop,
78 base::Unretained(this)),
79 base::TimeDelta::FromMilliseconds(100));
80 } else {
81 MessageLoop::current()->BreakLoop();
Allie Woodeb9e6d82015-04-17 13:55:30 -070082 }
Allie Woodeb9e6d82015-04-17 13:55:30 -070083 }
84 void ProcessingDone(const ActionProcessor* processor, ErrorCode code) {
85 ExitMainLoop();
86 }
87 void ProcessingStopped(const ActionProcessor* processor) {
88 ExitMainLoop();
89 }
90 void ActionCompleted(ActionProcessor* processor,
91 AbstractAction* action,
92 ErrorCode code) {
93 if (action->Type() == FilesystemVerifierAction::StaticType()) {
94 ran_ = true;
95 code_ = code;
96 }
97 }
98 bool ran() const { return ran_; }
99 ErrorCode code() const { return code_; }
100
101 private:
Allie Woodeb9e6d82015-04-17 13:55:30 -0700102 FilesystemVerifierAction* action_;
103 bool ran_;
104 ErrorCode code_;
105};
106
Alex Deymo20c99202015-07-09 16:14:16 -0700107void StartProcessorInRunLoop(ActionProcessor* processor,
108 FilesystemVerifierAction* filesystem_copier_action,
109 bool terminate_early) {
Allie Woodeb9e6d82015-04-17 13:55:30 -0700110 processor->StartProcessing();
Alex Deymo20c99202015-07-09 16:14:16 -0700111 if (terminate_early) {
112 EXPECT_NE(nullptr, filesystem_copier_action);
113 processor->StopProcessing();
Allie Woodeb9e6d82015-04-17 13:55:30 -0700114 }
Allie Woodeb9e6d82015-04-17 13:55:30 -0700115}
116
117// TODO(garnold) Temporarily disabling this test, see chromium-os:31082 for
118// details; still trying to track down the root cause for these rare write
119// failures and whether or not they are due to the test setup or an inherent
120// issue with the chroot environment, library versions we use, etc.
121TEST_F(FilesystemVerifierActionTest, DISABLED_RunAsRootSimpleTest) {
122 ASSERT_EQ(0, getuid());
123 bool test = DoTest(false, false, PartitionType::kKernel);
124 EXPECT_TRUE(test);
125 if (!test)
126 return;
127 test = DoTest(false, false, PartitionType::kRootfs);
128 EXPECT_TRUE(test);
129}
130
131bool FilesystemVerifierActionTest::DoTest(bool terminate_early,
132 bool hash_fail,
133 PartitionType partition_type) {
134 // We need MockHardware to verify MarkUnbootable calls, but don't want
135 // warnings about other usages.
136 testing::NiceMock<MockHardware> mock_hardware;
137 fake_system_state_.set_hardware(&mock_hardware);
138
Allie Woodeb9e6d82015-04-17 13:55:30 -0700139 string a_loop_file;
140
141 if (!(utils::MakeTempFile("a_loop_file.XXXXXX", &a_loop_file, nullptr))) {
142 ADD_FAILURE();
143 return false;
144 }
145 ScopedPathUnlinker a_loop_file_unlinker(a_loop_file);
146
Alex Deymo20c99202015-07-09 16:14:16 -0700147 // Make random data for a.
Allie Woodeb9e6d82015-04-17 13:55:30 -0700148 const size_t kLoopFileSize = 10 * 1024 * 1024 + 512;
149 chromeos::Blob a_loop_data(kLoopFileSize);
150 test_utils::FillWithData(&a_loop_data);
151
152
153 // Write data to disk
154 if (!(test_utils::WriteFileVector(a_loop_file, a_loop_data))) {
155 ADD_FAILURE();
156 return false;
157 }
158
159 // Attach loop devices to the files
160 string a_dev;
161 test_utils::ScopedLoopbackDeviceBinder a_dev_releaser(a_loop_file, &a_dev);
162 if (!(a_dev_releaser.is_bound())) {
163 ADD_FAILURE();
164 return false;
165 }
166
167 LOG(INFO) << "verifying: " << a_loop_file << " (" << a_dev << ")";
168
169 bool success = true;
170
171 // Set up the action objects
172 InstallPlan install_plan;
173 switch (partition_type) {
174 case PartitionType::kRootfs:
175 install_plan.rootfs_size = kLoopFileSize - (hash_fail ? 1 : 0);
176 install_plan.install_path = a_dev;
177 if (!OmahaHashCalculator::RawHashOfData(
178 a_loop_data, &install_plan.rootfs_hash)) {
179 ADD_FAILURE();
180 success = false;
181 }
182 break;
183 case PartitionType::kKernel:
184 install_plan.kernel_size = kLoopFileSize - (hash_fail ? 1 : 0);
185 install_plan.kernel_install_path = a_dev;
186 if (!OmahaHashCalculator::RawHashOfData(
187 a_loop_data, &install_plan.kernel_hash)) {
188 ADD_FAILURE();
189 success = false;
190 }
191 break;
192 case PartitionType::kSourceRootfs:
193 install_plan.source_path = a_dev;
194 if (!OmahaHashCalculator::RawHashOfData(
195 a_loop_data, &install_plan.source_rootfs_hash)) {
196 ADD_FAILURE();
197 success = false;
198 }
199 break;
200 case PartitionType::kSourceKernel:
201 install_plan.kernel_source_path = a_dev;
202 if (!OmahaHashCalculator::RawHashOfData(
203 a_loop_data, &install_plan.source_kernel_hash)) {
204 ADD_FAILURE();
205 success = false;
206 }
207 break;
208 }
209
210 EXPECT_CALL(mock_hardware,
211 MarkKernelUnbootable(a_dev)).Times(
212 partition_type == PartitionType::kKernel ? 1 : 0);
213
214 ActionProcessor processor;
215
216 ObjectFeederAction<InstallPlan> feeder_action;
217 FilesystemVerifierAction copier_action(&fake_system_state_, partition_type);
218 ObjectCollectorAction<InstallPlan> collector_action;
219
220 BondActions(&feeder_action, &copier_action);
221 BondActions(&copier_action, &collector_action);
222
Alex Deymo20c99202015-07-09 16:14:16 -0700223 FilesystemVerifierActionTestDelegate delegate(&copier_action);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700224 processor.set_delegate(&delegate);
225 processor.EnqueueAction(&feeder_action);
226 processor.EnqueueAction(&copier_action);
227 processor.EnqueueAction(&collector_action);
228
229 feeder_action.set_obj(install_plan);
230
Alex Deymo20c99202015-07-09 16:14:16 -0700231 loop_.PostTask(FROM_HERE, base::Bind(&StartProcessorInRunLoop,
232 &processor,
233 &copier_action,
234 terminate_early));
235 loop_.Run();
Allie Woodeb9e6d82015-04-17 13:55:30 -0700236
237 if (!terminate_early) {
238 bool is_delegate_ran = delegate.ran();
239 EXPECT_TRUE(is_delegate_ran);
240 success = success && is_delegate_ran;
241 } else {
242 EXPECT_EQ(ErrorCode::kError, delegate.code());
243 return (ErrorCode::kError == delegate.code());
244 }
245 if (hash_fail) {
246 ErrorCode expected_exit_code =
247 ((partition_type == PartitionType::kKernel ||
248 partition_type == PartitionType::kSourceKernel) ?
249 ErrorCode::kNewKernelVerificationError :
250 ErrorCode::kNewRootfsVerificationError);
251 EXPECT_EQ(expected_exit_code, delegate.code());
252 return (expected_exit_code == delegate.code());
253 }
254 EXPECT_EQ(ErrorCode::kSuccess, delegate.code());
255
256 // Make sure everything in the out_image is there
257 chromeos::Blob a_out;
258 if (!utils::ReadFile(a_dev, &a_out)) {
259 ADD_FAILURE();
260 return false;
261 }
262 const bool is_a_file_reading_eq =
263 test_utils::ExpectVectorsEq(a_loop_data, a_out);
264 EXPECT_TRUE(is_a_file_reading_eq);
265 success = success && is_a_file_reading_eq;
266
267 bool is_install_plan_eq = (collector_action.object() == install_plan);
268 EXPECT_TRUE(is_install_plan_eq);
269 success = success && is_install_plan_eq;
270
271 LOG(INFO) << "Verifying bootable flag on: " << a_dev;
272 bool bootable;
273 EXPECT_TRUE(mock_hardware.fake().IsKernelBootable(a_dev, &bootable));
274 // We should always mark a partition as unbootable if it's a kernel
275 // partition, but never if it's anything else.
276 EXPECT_EQ(bootable, (partition_type != PartitionType::kKernel));
277
278 return success;
279}
280
281class FilesystemVerifierActionTest2Delegate : public ActionProcessorDelegate {
282 public:
283 void ActionCompleted(ActionProcessor* processor,
284 AbstractAction* action,
285 ErrorCode code) {
286 if (action->Type() == FilesystemVerifierAction::StaticType()) {
287 ran_ = true;
288 code_ = code;
289 }
290 }
Allie Woodeb9e6d82015-04-17 13:55:30 -0700291 bool ran_;
292 ErrorCode code_;
293};
294
295TEST_F(FilesystemVerifierActionTest, MissingInputObjectTest) {
296 ActionProcessor processor;
297 FilesystemVerifierActionTest2Delegate delegate;
298
299 processor.set_delegate(&delegate);
300
301 FilesystemVerifierAction copier_action(&fake_system_state_,
302 PartitionType::kRootfs);
303 ObjectCollectorAction<InstallPlan> collector_action;
304
305 BondActions(&copier_action, &collector_action);
306
307 processor.EnqueueAction(&copier_action);
308 processor.EnqueueAction(&collector_action);
309 processor.StartProcessing();
310 EXPECT_FALSE(processor.IsRunning());
311 EXPECT_TRUE(delegate.ran_);
312 EXPECT_EQ(ErrorCode::kError, delegate.code_);
313}
314
315TEST_F(FilesystemVerifierActionTest, NonExistentDriveTest) {
316 ActionProcessor processor;
317 FilesystemVerifierActionTest2Delegate delegate;
318
319 processor.set_delegate(&delegate);
320
321 ObjectFeederAction<InstallPlan> feeder_action;
322 InstallPlan install_plan(false,
323 false,
324 "",
325 0,
326 "",
327 0,
328 "",
329 "/no/such/file",
330 "/no/such/file",
331 "/no/such/file",
332 "/no/such/file",
333 "");
334 feeder_action.set_obj(install_plan);
335 FilesystemVerifierAction verifier_action(&fake_system_state_,
336 PartitionType::kRootfs);
337 ObjectCollectorAction<InstallPlan> collector_action;
338
339 BondActions(&verifier_action, &collector_action);
340
341 processor.EnqueueAction(&feeder_action);
342 processor.EnqueueAction(&verifier_action);
343 processor.EnqueueAction(&collector_action);
344 processor.StartProcessing();
345 EXPECT_FALSE(processor.IsRunning());
346 EXPECT_TRUE(delegate.ran_);
347 EXPECT_EQ(ErrorCode::kError, delegate.code_);
348}
349
350TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashTest) {
351 ASSERT_EQ(0, getuid());
352 EXPECT_TRUE(DoTest(false, false, PartitionType::kRootfs));
353 EXPECT_TRUE(DoTest(false, false, PartitionType::kKernel));
354 EXPECT_TRUE(DoTest(false, false, PartitionType::kSourceRootfs));
355 EXPECT_TRUE(DoTest(false, false, PartitionType::kSourceKernel));
356}
357
358TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashFailTest) {
359 ASSERT_EQ(0, getuid());
360 EXPECT_TRUE(DoTest(false, true, PartitionType::kRootfs));
361 EXPECT_TRUE(DoTest(false, true, PartitionType::kKernel));
362}
363
364TEST_F(FilesystemVerifierActionTest, RunAsRootTerminateEarlyTest) {
365 ASSERT_EQ(0, getuid());
366 EXPECT_TRUE(DoTest(true, false, PartitionType::kKernel));
Alex Deymob9e8e262015-08-03 20:23:03 -0700367 // TerminateEarlyTest may leak some null callbacks from the Stream class.
368 while (loop_.RunOnce(false)) {}
Allie Woodeb9e6d82015-04-17 13:55:30 -0700369}
370
371TEST_F(FilesystemVerifierActionTest, RunAsRootDetermineFilesystemSizeTest) {
372 string img;
373 EXPECT_TRUE(utils::MakeTempFile("img.XXXXXX", &img, nullptr));
374 ScopedPathUnlinker img_unlinker(img);
375 test_utils::CreateExtImageAtPath(img, nullptr);
376 // Extend the "partition" holding the file system from 10MiB to 20MiB.
Alex Deymo20c99202015-07-09 16:14:16 -0700377 EXPECT_EQ(0, truncate(img.c_str(), 20 * 1024 * 1024));
Allie Woodeb9e6d82015-04-17 13:55:30 -0700378
Alex Deymob9e8e262015-08-03 20:23:03 -0700379 {
380 FilesystemVerifierAction action(&fake_system_state_,
381 PartitionType::kSourceKernel);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700382 EXPECT_EQ(kint64max, action.remaining_size_);
Alex Deymob9e8e262015-08-03 20:23:03 -0700383 action.DetermineFilesystemSize(img);
384 EXPECT_EQ(kint64max, action.remaining_size_);
385 }
386 {
387 FilesystemVerifierAction action(&fake_system_state_,
388 PartitionType::kSourceRootfs);
389 action.DetermineFilesystemSize(img);
390 EXPECT_EQ(10 * 1024 * 1024, action.remaining_size_);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700391 }
392}
393
394} // namespace chromeos_update_engine