blob: 32f3c59e9c20c58c856d476af29607474c826336 [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) {
Allie Woodeb9e6d82015-04-17 13:55:30 -0700134 string a_loop_file;
135
136 if (!(utils::MakeTempFile("a_loop_file.XXXXXX", &a_loop_file, nullptr))) {
137 ADD_FAILURE();
138 return false;
139 }
140 ScopedPathUnlinker a_loop_file_unlinker(a_loop_file);
141
Alex Deymo20c99202015-07-09 16:14:16 -0700142 // Make random data for a.
Allie Woodeb9e6d82015-04-17 13:55:30 -0700143 const size_t kLoopFileSize = 10 * 1024 * 1024 + 512;
144 chromeos::Blob a_loop_data(kLoopFileSize);
145 test_utils::FillWithData(&a_loop_data);
146
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;
Alex Deymo763e7db2015-08-27 21:08:08 -0700168 install_plan.source_slot = 0;
169 install_plan.target_slot = 1;
Allie Woodeb9e6d82015-04-17 13:55:30 -0700170 switch (partition_type) {
171 case PartitionType::kRootfs:
172 install_plan.rootfs_size = kLoopFileSize - (hash_fail ? 1 : 0);
173 install_plan.install_path = a_dev;
174 if (!OmahaHashCalculator::RawHashOfData(
175 a_loop_data, &install_plan.rootfs_hash)) {
176 ADD_FAILURE();
177 success = false;
178 }
179 break;
180 case PartitionType::kKernel:
181 install_plan.kernel_size = kLoopFileSize - (hash_fail ? 1 : 0);
182 install_plan.kernel_install_path = a_dev;
183 if (!OmahaHashCalculator::RawHashOfData(
184 a_loop_data, &install_plan.kernel_hash)) {
185 ADD_FAILURE();
186 success = false;
187 }
188 break;
189 case PartitionType::kSourceRootfs:
190 install_plan.source_path = a_dev;
191 if (!OmahaHashCalculator::RawHashOfData(
192 a_loop_data, &install_plan.source_rootfs_hash)) {
193 ADD_FAILURE();
194 success = false;
195 }
196 break;
197 case PartitionType::kSourceKernel:
198 install_plan.kernel_source_path = a_dev;
199 if (!OmahaHashCalculator::RawHashOfData(
200 a_loop_data, &install_plan.source_kernel_hash)) {
201 ADD_FAILURE();
202 success = false;
203 }
204 break;
205 }
206
Alex Deymo763e7db2015-08-27 21:08:08 -0700207 fake_system_state_.fake_boot_control()->SetSlotBootable(
208 install_plan.target_slot, true);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700209
210 ActionProcessor processor;
211
212 ObjectFeederAction<InstallPlan> feeder_action;
213 FilesystemVerifierAction copier_action(&fake_system_state_, partition_type);
214 ObjectCollectorAction<InstallPlan> collector_action;
215
216 BondActions(&feeder_action, &copier_action);
217 BondActions(&copier_action, &collector_action);
218
Alex Deymo20c99202015-07-09 16:14:16 -0700219 FilesystemVerifierActionTestDelegate delegate(&copier_action);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700220 processor.set_delegate(&delegate);
221 processor.EnqueueAction(&feeder_action);
222 processor.EnqueueAction(&copier_action);
223 processor.EnqueueAction(&collector_action);
224
225 feeder_action.set_obj(install_plan);
226
Alex Deymo20c99202015-07-09 16:14:16 -0700227 loop_.PostTask(FROM_HERE, base::Bind(&StartProcessorInRunLoop,
228 &processor,
229 &copier_action,
230 terminate_early));
231 loop_.Run();
Allie Woodeb9e6d82015-04-17 13:55:30 -0700232
233 if (!terminate_early) {
234 bool is_delegate_ran = delegate.ran();
235 EXPECT_TRUE(is_delegate_ran);
236 success = success && is_delegate_ran;
237 } else {
238 EXPECT_EQ(ErrorCode::kError, delegate.code());
239 return (ErrorCode::kError == delegate.code());
240 }
241 if (hash_fail) {
242 ErrorCode expected_exit_code =
243 ((partition_type == PartitionType::kKernel ||
244 partition_type == PartitionType::kSourceKernel) ?
245 ErrorCode::kNewKernelVerificationError :
246 ErrorCode::kNewRootfsVerificationError);
247 EXPECT_EQ(expected_exit_code, delegate.code());
248 return (expected_exit_code == delegate.code());
249 }
250 EXPECT_EQ(ErrorCode::kSuccess, delegate.code());
251
252 // Make sure everything in the out_image is there
253 chromeos::Blob a_out;
254 if (!utils::ReadFile(a_dev, &a_out)) {
255 ADD_FAILURE();
256 return false;
257 }
258 const bool is_a_file_reading_eq =
259 test_utils::ExpectVectorsEq(a_loop_data, a_out);
260 EXPECT_TRUE(is_a_file_reading_eq);
261 success = success && is_a_file_reading_eq;
262
263 bool is_install_plan_eq = (collector_action.object() == install_plan);
264 EXPECT_TRUE(is_install_plan_eq);
265 success = success && is_install_plan_eq;
266
267 LOG(INFO) << "Verifying bootable flag on: " << a_dev;
Alex Deymo763e7db2015-08-27 21:08:08 -0700268
Allie Woodeb9e6d82015-04-17 13:55:30 -0700269 // We should always mark a partition as unbootable if it's a kernel
270 // partition, but never if it's anything else.
Alex Deymo763e7db2015-08-27 21:08:08 -0700271 EXPECT_EQ((partition_type != PartitionType::kKernel),
272 fake_system_state_.fake_boot_control()->IsSlotBootable(
273 install_plan.target_slot));
Allie Woodeb9e6d82015-04-17 13:55:30 -0700274 return success;
275}
276
277class FilesystemVerifierActionTest2Delegate : public ActionProcessorDelegate {
278 public:
279 void ActionCompleted(ActionProcessor* processor,
280 AbstractAction* action,
281 ErrorCode code) {
282 if (action->Type() == FilesystemVerifierAction::StaticType()) {
283 ran_ = true;
284 code_ = code;
285 }
286 }
Allie Woodeb9e6d82015-04-17 13:55:30 -0700287 bool ran_;
288 ErrorCode code_;
289};
290
291TEST_F(FilesystemVerifierActionTest, MissingInputObjectTest) {
292 ActionProcessor processor;
293 FilesystemVerifierActionTest2Delegate delegate;
294
295 processor.set_delegate(&delegate);
296
297 FilesystemVerifierAction copier_action(&fake_system_state_,
298 PartitionType::kRootfs);
299 ObjectCollectorAction<InstallPlan> collector_action;
300
301 BondActions(&copier_action, &collector_action);
302
303 processor.EnqueueAction(&copier_action);
304 processor.EnqueueAction(&collector_action);
305 processor.StartProcessing();
306 EXPECT_FALSE(processor.IsRunning());
307 EXPECT_TRUE(delegate.ran_);
308 EXPECT_EQ(ErrorCode::kError, delegate.code_);
309}
310
311TEST_F(FilesystemVerifierActionTest, NonExistentDriveTest) {
312 ActionProcessor processor;
313 FilesystemVerifierActionTest2Delegate delegate;
314
315 processor.set_delegate(&delegate);
316
317 ObjectFeederAction<InstallPlan> feeder_action;
318 InstallPlan install_plan(false,
319 false,
320 "",
321 0,
322 "",
323 0,
324 "",
325 "/no/such/file",
326 "/no/such/file",
327 "/no/such/file",
328 "/no/such/file",
329 "");
330 feeder_action.set_obj(install_plan);
331 FilesystemVerifierAction verifier_action(&fake_system_state_,
332 PartitionType::kRootfs);
333 ObjectCollectorAction<InstallPlan> collector_action;
334
335 BondActions(&verifier_action, &collector_action);
336
337 processor.EnqueueAction(&feeder_action);
338 processor.EnqueueAction(&verifier_action);
339 processor.EnqueueAction(&collector_action);
340 processor.StartProcessing();
341 EXPECT_FALSE(processor.IsRunning());
342 EXPECT_TRUE(delegate.ran_);
343 EXPECT_EQ(ErrorCode::kError, delegate.code_);
344}
345
346TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashTest) {
347 ASSERT_EQ(0, getuid());
348 EXPECT_TRUE(DoTest(false, false, PartitionType::kRootfs));
349 EXPECT_TRUE(DoTest(false, false, PartitionType::kKernel));
350 EXPECT_TRUE(DoTest(false, false, PartitionType::kSourceRootfs));
351 EXPECT_TRUE(DoTest(false, false, PartitionType::kSourceKernel));
352}
353
354TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashFailTest) {
355 ASSERT_EQ(0, getuid());
356 EXPECT_TRUE(DoTest(false, true, PartitionType::kRootfs));
357 EXPECT_TRUE(DoTest(false, true, PartitionType::kKernel));
358}
359
360TEST_F(FilesystemVerifierActionTest, RunAsRootTerminateEarlyTest) {
361 ASSERT_EQ(0, getuid());
362 EXPECT_TRUE(DoTest(true, false, PartitionType::kKernel));
Alex Deymob9e8e262015-08-03 20:23:03 -0700363 // TerminateEarlyTest may leak some null callbacks from the Stream class.
364 while (loop_.RunOnce(false)) {}
Allie Woodeb9e6d82015-04-17 13:55:30 -0700365}
366
367TEST_F(FilesystemVerifierActionTest, RunAsRootDetermineFilesystemSizeTest) {
368 string img;
369 EXPECT_TRUE(utils::MakeTempFile("img.XXXXXX", &img, nullptr));
370 ScopedPathUnlinker img_unlinker(img);
371 test_utils::CreateExtImageAtPath(img, nullptr);
372 // Extend the "partition" holding the file system from 10MiB to 20MiB.
Alex Deymo20c99202015-07-09 16:14:16 -0700373 EXPECT_EQ(0, truncate(img.c_str(), 20 * 1024 * 1024));
Allie Woodeb9e6d82015-04-17 13:55:30 -0700374
Alex Deymob9e8e262015-08-03 20:23:03 -0700375 {
376 FilesystemVerifierAction action(&fake_system_state_,
377 PartitionType::kSourceKernel);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700378 EXPECT_EQ(kint64max, action.remaining_size_);
Alex Deymob9e8e262015-08-03 20:23:03 -0700379 action.DetermineFilesystemSize(img);
380 EXPECT_EQ(kint64max, action.remaining_size_);
381 }
382 {
383 FilesystemVerifierAction action(&fake_system_state_,
384 PartitionType::kSourceRootfs);
385 action.DetermineFilesystemSize(img);
386 EXPECT_EQ(10 * 1024 * 1024, action.remaining_size_);
Allie Woodeb9e6d82015-04-17 13:55:30 -0700387 }
388}
389
390} // namespace chromeos_update_engine