blob: 6dc98162ac04d49e2d7206e4989ef181d490be91 [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 Vakulenko3f39d5c2015-10-13 09:27:13 -070029#include <brillo/bind_lambda.h>
30#include <brillo/message_loops/fake_message_loop.h>
31#include <brillo/message_loops/message_loop_utils.h>
Allie Woodeb9e6d82015-04-17 13:55:30 -070032#include <gmock/gmock.h>
33#include <gtest/gtest.h>
34
Alex Deymoe5e5fe92015-10-05 09:28:19 -070035#include "update_engine/fake_boot_control.h"
Allie Woodeb9e6d82015-04-17 13:55:30 -070036#include "update_engine/omaha_hash_calculator.h"
Alex Deymoe5e5fe92015-10-05 09:28:19 -070037#include "update_engine/payload_constants.h"
Allie Woodeb9e6d82015-04-17 13:55:30 -070038#include "update_engine/test_utils.h"
39#include "update_engine/utils.h"
40
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -070041using brillo::MessageLoop;
Allie Woodeb9e6d82015-04-17 13:55:30 -070042using std::set;
43using std::string;
44using std::vector;
45
46namespace chromeos_update_engine {
47
48class FilesystemVerifierActionTest : public ::testing::Test {
49 protected:
Alex Deymo20c99202015-07-09 16:14:16 -070050 void SetUp() override {
51 loop_.SetAsCurrent();
52 }
53
54 void TearDown() override {
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -070055 EXPECT_EQ(0, brillo::MessageLoopRunMaxIterations(&loop_, 1));
Alex Deymo20c99202015-07-09 16:14:16 -070056 }
57
Allie Woodeb9e6d82015-04-17 13:55:30 -070058 // Returns true iff test has completed successfully.
59 bool DoTest(bool terminate_early,
60 bool hash_fail,
Alex Deymoe5e5fe92015-10-05 09:28:19 -070061 VerifierMode verifier_mode);
Allie Woodeb9e6d82015-04-17 13:55:30 -070062
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -070063 brillo::FakeMessageLoop loop_{nullptr};
Alex Deymoe5e5fe92015-10-05 09:28:19 -070064 FakeBootControl fake_boot_control_;
Allie Woodeb9e6d82015-04-17 13:55:30 -070065};
66
67class FilesystemVerifierActionTestDelegate : public ActionProcessorDelegate {
68 public:
Alex Deymo20c99202015-07-09 16:14:16 -070069 explicit FilesystemVerifierActionTestDelegate(
70 FilesystemVerifierAction* action)
71 : action_(action), ran_(false), code_(ErrorCode::kError) {}
Allie Woodeb9e6d82015-04-17 13:55:30 -070072 void ExitMainLoop() {
Alex Deymo20c99202015-07-09 16:14:16 -070073 // 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();
Allie Woodeb9e6d82015-04-17 13:55:30 -070083 }
Allie Woodeb9e6d82015-04-17 13:55:30 -070084 }
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:
Allie Woodeb9e6d82015-04-17 13:55:30 -0700103 FilesystemVerifierAction* action_;
104 bool ran_;
105 ErrorCode code_;
106};
107
Alex Deymo20c99202015-07-09 16:14:16 -0700108void StartProcessorInRunLoop(ActionProcessor* processor,
109 FilesystemVerifierAction* filesystem_copier_action,
110 bool terminate_early) {
Allie Woodeb9e6d82015-04-17 13:55:30 -0700111 processor->StartProcessing();
Alex Deymo20c99202015-07-09 16:14:16 -0700112 if (terminate_early) {
113 EXPECT_NE(nullptr, filesystem_copier_action);
114 processor->StopProcessing();
Allie Woodeb9e6d82015-04-17 13:55:30 -0700115 }
Allie Woodeb9e6d82015-04-17 13:55:30 -0700116}
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());
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700124 bool test = DoTest(false, false, VerifierMode::kComputeSourceHash);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700125 EXPECT_TRUE(test);
126 if (!test)
127 return;
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700128 test = DoTest(false, false, VerifierMode::kVerifyTargetHash);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700129 EXPECT_TRUE(test);
130}
131
132bool FilesystemVerifierActionTest::DoTest(bool terminate_early,
133 bool hash_fail,
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700134 VerifierMode verifier_mode) {
Allie Woodeb9e6d82015-04-17 13:55:30 -0700135 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
Alex Deymo20c99202015-07-09 16:14:16 -0700143 // Make random data for a.
Allie Woodeb9e6d82015-04-17 13:55:30 -0700144 const size_t kLoopFileSize = 10 * 1024 * 1024 + 512;
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -0700145 brillo::Blob a_loop_data(kLoopFileSize);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700146 test_utils::FillWithData(&a_loop_data);
147
Allie Woodeb9e6d82015-04-17 13:55:30 -0700148 // 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;
Alex Deymo763e7db2015-08-27 21:08:08 -0700168 install_plan.source_slot = 0;
169 install_plan.target_slot = 1;
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700170 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 (!OmahaHashCalculator::RawHashOfData(a_loop_data, &part.target_hash)) {
Allie Woodeb9e6d82015-04-17 13:55:30 -0700179 ADD_FAILURE();
180 success = false;
181 }
182 break;
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700183 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 (!OmahaHashCalculator::RawHashOfData(a_loop_data, &part.source_hash)) {
Allie Woodeb9e6d82015-04-17 13:55:30 -0700189 ADD_FAILURE();
190 success = false;
191 }
192 break;
193 }
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700194 install_plan.partitions = {part};
Allie Woodeb9e6d82015-04-17 13:55:30 -0700195
Allie Woodeb9e6d82015-04-17 13:55:30 -0700196 ActionProcessor processor;
197
198 ObjectFeederAction<InstallPlan> feeder_action;
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700199 FilesystemVerifierAction copier_action(&fake_boot_control_, verifier_mode);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700200 ObjectCollectorAction<InstallPlan> collector_action;
201
202 BondActions(&feeder_action, &copier_action);
203 BondActions(&copier_action, &collector_action);
204
Alex Deymo20c99202015-07-09 16:14:16 -0700205 FilesystemVerifierActionTestDelegate delegate(&copier_action);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700206 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
Alex Deymo20c99202015-07-09 16:14:16 -0700213 loop_.PostTask(FROM_HERE, base::Bind(&StartProcessorInRunLoop,
214 &processor,
215 &copier_action,
216 terminate_early));
217 loop_.Run();
Allie Woodeb9e6d82015-04-17 13:55:30 -0700218
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) {
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700228 ErrorCode expected_exit_code = ErrorCode::kNewRootfsVerificationError;
Allie Woodeb9e6d82015-04-17 13:55:30 -0700229 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
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -0700235 brillo::Blob a_out;
Allie Woodeb9e6d82015-04-17 13:55:30 -0700236 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;
Allie Woodeb9e6d82015-04-17 13:55:30 -0700248 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 }
Allie Woodeb9e6d82015-04-17 13:55:30 -0700261 bool ran_;
262 ErrorCode code_;
263};
264
265TEST_F(FilesystemVerifierActionTest, MissingInputObjectTest) {
266 ActionProcessor processor;
267 FilesystemVerifierActionTest2Delegate delegate;
268
269 processor.set_delegate(&delegate);
270
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700271 FilesystemVerifierAction copier_action(&fake_boot_control_,
272 VerifierMode::kVerifyTargetHash);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700273 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 "",
Allie Woodeb9e6d82015-04-17 13:55:30 -0700299 "");
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700300 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
Allie Woodeb9e6d82015-04-17 13:55:30 -0700306 feeder_action.set_obj(install_plan);
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700307 FilesystemVerifierAction verifier_action(&fake_boot_control_,
308 VerifierMode::kVerifyTargetHash);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700309 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());
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700324 EXPECT_TRUE(DoTest(false, false, VerifierMode::kVerifyTargetHash));
325 EXPECT_TRUE(DoTest(false, false, VerifierMode::kComputeSourceHash));
Allie Woodeb9e6d82015-04-17 13:55:30 -0700326}
327
328TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashFailTest) {
329 ASSERT_EQ(0, getuid());
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700330 EXPECT_TRUE(DoTest(false, true, VerifierMode::kVerifyTargetHash));
Allie Woodeb9e6d82015-04-17 13:55:30 -0700331}
332
333TEST_F(FilesystemVerifierActionTest, RunAsRootTerminateEarlyTest) {
334 ASSERT_EQ(0, getuid());
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700335 EXPECT_TRUE(DoTest(true, false, VerifierMode::kVerifyTargetHash));
Alex Deymob9e8e262015-08-03 20:23:03 -0700336 // TerminateEarlyTest may leak some null callbacks from the Stream class.
337 while (loop_.RunOnce(false)) {}
Allie Woodeb9e6d82015-04-17 13:55:30 -0700338}
339
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700340// 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) {
Allie Woodeb9e6d82015-04-17 13:55:30 -0700343 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.
Alex Deymo20c99202015-07-09 16:14:16 -0700348 EXPECT_EQ(0, truncate(img.c_str(), 20 * 1024 * 1024));
Allie Woodeb9e6d82015-04-17 13:55:30 -0700349
Alex Deymoe5e5fe92015-10-05 09:28:19 -0700350 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);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700385}
386
387} // namespace chromeos_update_engine