blob: a1842e871b5e5b43484e7a1cc5d7d9d67fbe210c [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
13#include <base/posix/eintr_wrapper.h>
14#include <base/strings/string_util.h>
15#include <base/strings/stringprintf.h>
16#include <glib.h>
17#include <gmock/gmock.h>
18#include <gtest/gtest.h>
19
20#include "update_engine/fake_system_state.h"
21#include "update_engine/mock_hardware.h"
22#include "update_engine/omaha_hash_calculator.h"
23#include "update_engine/test_utils.h"
24#include "update_engine/utils.h"
25
26using std::set;
27using std::string;
28using std::vector;
29
30namespace chromeos_update_engine {
31
32class FilesystemVerifierActionTest : public ::testing::Test {
33 protected:
34 // Returns true iff test has completed successfully.
35 bool DoTest(bool terminate_early,
36 bool hash_fail,
37 PartitionType partition_type);
38
39 FakeSystemState fake_system_state_;
40};
41
42class FilesystemVerifierActionTestDelegate : public ActionProcessorDelegate {
43 public:
44 FilesystemVerifierActionTestDelegate(GMainLoop* loop,
45 FilesystemVerifierAction* action)
46 : loop_(loop), action_(action), ran_(false), code_(ErrorCode::kError) {}
47 void ExitMainLoop() {
48 GMainContext* context = g_main_loop_get_context(loop_);
49 // We cannot use g_main_context_pending() alone to determine if it is safe
50 // to quit the main loop here because g_main_context_pending() may return
51 // FALSE when g_input_stream_read_async() in FilesystemVerifierAction has
52 // been cancelled but the callback has not yet been invoked.
53 while (g_main_context_pending(context) || action_->IsCleanupPending()) {
54 g_main_context_iteration(context, false);
55 g_usleep(100);
56 }
57 g_main_loop_quit(loop_);
58 }
59 void ProcessingDone(const ActionProcessor* processor, ErrorCode code) {
60 ExitMainLoop();
61 }
62 void ProcessingStopped(const ActionProcessor* processor) {
63 ExitMainLoop();
64 }
65 void ActionCompleted(ActionProcessor* processor,
66 AbstractAction* action,
67 ErrorCode code) {
68 if (action->Type() == FilesystemVerifierAction::StaticType()) {
69 ran_ = true;
70 code_ = code;
71 }
72 }
73 bool ran() const { return ran_; }
74 ErrorCode code() const { return code_; }
75
76 private:
77 GMainLoop* loop_;
78 FilesystemVerifierAction* action_;
79 bool ran_;
80 ErrorCode code_;
81};
82
83struct StartProcessorCallbackArgs {
84 ActionProcessor* processor;
85 FilesystemVerifierAction* filesystem_copier_action;
86 bool terminate_early;
87};
88
89gboolean StartProcessorInRunLoop(gpointer data) {
90 StartProcessorCallbackArgs* args =
91 reinterpret_cast<StartProcessorCallbackArgs*>(data);
92 ActionProcessor* processor = args->processor;
93 processor->StartProcessing();
94 if (args->terminate_early) {
95 EXPECT_TRUE(args->filesystem_copier_action);
96 args->processor->StopProcessing();
97 }
98 return FALSE;
99}
100
101// TODO(garnold) Temporarily disabling this test, see chromium-os:31082 for
102// details; still trying to track down the root cause for these rare write
103// failures and whether or not they are due to the test setup or an inherent
104// issue with the chroot environment, library versions we use, etc.
105TEST_F(FilesystemVerifierActionTest, DISABLED_RunAsRootSimpleTest) {
106 ASSERT_EQ(0, getuid());
107 bool test = DoTest(false, false, PartitionType::kKernel);
108 EXPECT_TRUE(test);
109 if (!test)
110 return;
111 test = DoTest(false, false, PartitionType::kRootfs);
112 EXPECT_TRUE(test);
113}
114
115bool FilesystemVerifierActionTest::DoTest(bool terminate_early,
116 bool hash_fail,
117 PartitionType partition_type) {
118 // We need MockHardware to verify MarkUnbootable calls, but don't want
119 // warnings about other usages.
120 testing::NiceMock<MockHardware> mock_hardware;
121 fake_system_state_.set_hardware(&mock_hardware);
122
123 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
124
125 string a_loop_file;
126
127 if (!(utils::MakeTempFile("a_loop_file.XXXXXX", &a_loop_file, nullptr))) {
128 ADD_FAILURE();
129 return false;
130 }
131 ScopedPathUnlinker a_loop_file_unlinker(a_loop_file);
132
133 // Make random data for a, zero filled data for b.
134 const size_t kLoopFileSize = 10 * 1024 * 1024 + 512;
135 chromeos::Blob a_loop_data(kLoopFileSize);
136 test_utils::FillWithData(&a_loop_data);
137
138
139 // Write data to disk
140 if (!(test_utils::WriteFileVector(a_loop_file, a_loop_data))) {
141 ADD_FAILURE();
142 return false;
143 }
144
145 // Attach loop devices to the files
146 string a_dev;
147 test_utils::ScopedLoopbackDeviceBinder a_dev_releaser(a_loop_file, &a_dev);
148 if (!(a_dev_releaser.is_bound())) {
149 ADD_FAILURE();
150 return false;
151 }
152
153 LOG(INFO) << "verifying: " << a_loop_file << " (" << a_dev << ")";
154
155 bool success = true;
156
157 // Set up the action objects
158 InstallPlan install_plan;
159 switch (partition_type) {
160 case PartitionType::kRootfs:
161 install_plan.rootfs_size = kLoopFileSize - (hash_fail ? 1 : 0);
162 install_plan.install_path = a_dev;
163 if (!OmahaHashCalculator::RawHashOfData(
164 a_loop_data, &install_plan.rootfs_hash)) {
165 ADD_FAILURE();
166 success = false;
167 }
168 break;
169 case PartitionType::kKernel:
170 install_plan.kernel_size = kLoopFileSize - (hash_fail ? 1 : 0);
171 install_plan.kernel_install_path = a_dev;
172 if (!OmahaHashCalculator::RawHashOfData(
173 a_loop_data, &install_plan.kernel_hash)) {
174 ADD_FAILURE();
175 success = false;
176 }
177 break;
178 case PartitionType::kSourceRootfs:
179 install_plan.source_path = a_dev;
180 if (!OmahaHashCalculator::RawHashOfData(
181 a_loop_data, &install_plan.source_rootfs_hash)) {
182 ADD_FAILURE();
183 success = false;
184 }
185 break;
186 case PartitionType::kSourceKernel:
187 install_plan.kernel_source_path = a_dev;
188 if (!OmahaHashCalculator::RawHashOfData(
189 a_loop_data, &install_plan.source_kernel_hash)) {
190 ADD_FAILURE();
191 success = false;
192 }
193 break;
194 }
195
196 EXPECT_CALL(mock_hardware,
197 MarkKernelUnbootable(a_dev)).Times(
198 partition_type == PartitionType::kKernel ? 1 : 0);
199
200 ActionProcessor processor;
201
202 ObjectFeederAction<InstallPlan> feeder_action;
203 FilesystemVerifierAction copier_action(&fake_system_state_, partition_type);
204 ObjectCollectorAction<InstallPlan> collector_action;
205
206 BondActions(&feeder_action, &copier_action);
207 BondActions(&copier_action, &collector_action);
208
209 FilesystemVerifierActionTestDelegate delegate(loop, &copier_action);
210 processor.set_delegate(&delegate);
211 processor.EnqueueAction(&feeder_action);
212 processor.EnqueueAction(&copier_action);
213 processor.EnqueueAction(&collector_action);
214
215 feeder_action.set_obj(install_plan);
216
217 StartProcessorCallbackArgs start_callback_args;
218 start_callback_args.processor = &processor;
219 start_callback_args.filesystem_copier_action = &copier_action;
220 start_callback_args.terminate_early = terminate_early;
221
222 g_timeout_add(0, &StartProcessorInRunLoop, &start_callback_args);
223 g_main_loop_run(loop);
224 g_main_loop_unref(loop);
225
226 if (!terminate_early) {
227 bool is_delegate_ran = delegate.ran();
228 EXPECT_TRUE(is_delegate_ran);
229 success = success && is_delegate_ran;
230 } else {
231 EXPECT_EQ(ErrorCode::kError, delegate.code());
232 return (ErrorCode::kError == delegate.code());
233 }
234 if (hash_fail) {
235 ErrorCode expected_exit_code =
236 ((partition_type == PartitionType::kKernel ||
237 partition_type == PartitionType::kSourceKernel) ?
238 ErrorCode::kNewKernelVerificationError :
239 ErrorCode::kNewRootfsVerificationError);
240 EXPECT_EQ(expected_exit_code, delegate.code());
241 return (expected_exit_code == delegate.code());
242 }
243 EXPECT_EQ(ErrorCode::kSuccess, delegate.code());
244
245 // Make sure everything in the out_image is there
246 chromeos::Blob a_out;
247 if (!utils::ReadFile(a_dev, &a_out)) {
248 ADD_FAILURE();
249 return false;
250 }
251 const bool is_a_file_reading_eq =
252 test_utils::ExpectVectorsEq(a_loop_data, a_out);
253 EXPECT_TRUE(is_a_file_reading_eq);
254 success = success && is_a_file_reading_eq;
255
256 bool is_install_plan_eq = (collector_action.object() == install_plan);
257 EXPECT_TRUE(is_install_plan_eq);
258 success = success && is_install_plan_eq;
259
260 LOG(INFO) << "Verifying bootable flag on: " << a_dev;
261 bool bootable;
262 EXPECT_TRUE(mock_hardware.fake().IsKernelBootable(a_dev, &bootable));
263 // We should always mark a partition as unbootable if it's a kernel
264 // partition, but never if it's anything else.
265 EXPECT_EQ(bootable, (partition_type != PartitionType::kKernel));
266
267 return success;
268}
269
270class FilesystemVerifierActionTest2Delegate : public ActionProcessorDelegate {
271 public:
272 void ActionCompleted(ActionProcessor* processor,
273 AbstractAction* action,
274 ErrorCode code) {
275 if (action->Type() == FilesystemVerifierAction::StaticType()) {
276 ran_ = true;
277 code_ = code;
278 }
279 }
280 GMainLoop *loop_;
281 bool ran_;
282 ErrorCode code_;
283};
284
285TEST_F(FilesystemVerifierActionTest, MissingInputObjectTest) {
286 ActionProcessor processor;
287 FilesystemVerifierActionTest2Delegate delegate;
288
289 processor.set_delegate(&delegate);
290
291 FilesystemVerifierAction copier_action(&fake_system_state_,
292 PartitionType::kRootfs);
293 ObjectCollectorAction<InstallPlan> collector_action;
294
295 BondActions(&copier_action, &collector_action);
296
297 processor.EnqueueAction(&copier_action);
298 processor.EnqueueAction(&collector_action);
299 processor.StartProcessing();
300 EXPECT_FALSE(processor.IsRunning());
301 EXPECT_TRUE(delegate.ran_);
302 EXPECT_EQ(ErrorCode::kError, delegate.code_);
303}
304
305TEST_F(FilesystemVerifierActionTest, NonExistentDriveTest) {
306 ActionProcessor processor;
307 FilesystemVerifierActionTest2Delegate delegate;
308
309 processor.set_delegate(&delegate);
310
311 ObjectFeederAction<InstallPlan> feeder_action;
312 InstallPlan install_plan(false,
313 false,
314 "",
315 0,
316 "",
317 0,
318 "",
319 "/no/such/file",
320 "/no/such/file",
321 "/no/such/file",
322 "/no/such/file",
323 "");
324 feeder_action.set_obj(install_plan);
325 FilesystemVerifierAction verifier_action(&fake_system_state_,
326 PartitionType::kRootfs);
327 ObjectCollectorAction<InstallPlan> collector_action;
328
329 BondActions(&verifier_action, &collector_action);
330
331 processor.EnqueueAction(&feeder_action);
332 processor.EnqueueAction(&verifier_action);
333 processor.EnqueueAction(&collector_action);
334 processor.StartProcessing();
335 EXPECT_FALSE(processor.IsRunning());
336 EXPECT_TRUE(delegate.ran_);
337 EXPECT_EQ(ErrorCode::kError, delegate.code_);
338}
339
340TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashTest) {
341 ASSERT_EQ(0, getuid());
342 EXPECT_TRUE(DoTest(false, false, PartitionType::kRootfs));
343 EXPECT_TRUE(DoTest(false, false, PartitionType::kKernel));
344 EXPECT_TRUE(DoTest(false, false, PartitionType::kSourceRootfs));
345 EXPECT_TRUE(DoTest(false, false, PartitionType::kSourceKernel));
346}
347
348TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashFailTest) {
349 ASSERT_EQ(0, getuid());
350 EXPECT_TRUE(DoTest(false, true, PartitionType::kRootfs));
351 EXPECT_TRUE(DoTest(false, true, PartitionType::kKernel));
352}
353
354TEST_F(FilesystemVerifierActionTest, RunAsRootTerminateEarlyTest) {
355 ASSERT_EQ(0, getuid());
356 EXPECT_TRUE(DoTest(true, false, PartitionType::kKernel));
357}
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.
365 EXPECT_EQ(0, test_utils::System(base::StringPrintf(
366 "dd if=/dev/zero of=%s seek=20971519 bs=1 count=1 status=none",
367 img.c_str())));
368 EXPECT_EQ(20 * 1024 * 1024, utils::FileSize(img));
369
370 for (int i = 0; i < 2; ++i) {
371 PartitionType fs_type =
372 i ? PartitionType::kSourceKernel : PartitionType::kSourceRootfs;
373 FilesystemVerifierAction action(&fake_system_state_, fs_type);
374 EXPECT_EQ(kint64max, action.remaining_size_);
375 {
376 int fd = HANDLE_EINTR(open(img.c_str(), O_RDONLY));
377 EXPECT_GT(fd, 0);
378 ScopedFdCloser fd_closer(&fd);
379 action.DetermineFilesystemSize(fd);
380 }
381 EXPECT_EQ(i ? kint64max : 10 * 1024 * 1024,
382 action.remaining_size_);
383 }
384}
385
386} // namespace chromeos_update_engine