1 // 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 // 16 17 #include "update_engine/p2p_manager.h" 18 19 #include <dirent.h> 20 #include <fcntl.h> 21 #include <sys/stat.h> 22 #include <sys/types.h> 23 #include <sys/xattr.h> 24 #include <unistd.h> 25 26 #include <memory> 27 #include <string> 28 #include <vector> 29 30 #include <base/bind.h> 31 #include <base/callback.h> 32 #include <base/files/file_util.h> 33 #include <base/message_loop/message_loop.h> 34 #include <base/strings/stringprintf.h> 35 #include <brillo/asynchronous_signal_handler.h> 36 #include <brillo/message_loops/base_message_loop.h> 37 #include <brillo/message_loops/message_loop.h> 38 #include <brillo/message_loops/message_loop_utils.h> 39 #include <gmock/gmock.h> 40 #include <gtest/gtest.h> 41 #include <policy/libpolicy.h> 42 #include <policy/mock_device_policy.h> 43 44 #include "update_engine/common/fake_clock.h" 45 #include "update_engine/common/prefs.h" 46 #include "update_engine/common/test_utils.h" 47 #include "update_engine/common/utils.h" 48 #include "update_engine/fake_p2p_manager_configuration.h" 49 #include "update_engine/update_manager/fake_update_manager.h" 50 #include "update_engine/update_manager/mock_policy.h" 51 52 using base::TimeDelta; 53 using brillo::MessageLoop; 54 using std::string; 55 using std::unique_ptr; 56 using std::vector; 57 using testing::DoAll; 58 using testing::Return; 59 using testing::SetArgPointee; 60 using testing::_; 61 62 namespace chromeos_update_engine { 63 64 // Test fixture that sets up a testing configuration (with e.g. a 65 // temporary p2p dir) for P2PManager and cleans up when the test is 66 // done. 67 class P2PManagerTest : public testing::Test { 68 protected: 69 P2PManagerTest() : fake_um_(&fake_clock_) {} 70 ~P2PManagerTest() override {} 71 72 // Derived from testing::Test. 73 void SetUp() override { 74 loop_.SetAsCurrent(); 75 async_signal_handler_.Init(); 76 subprocess_.Init(&async_signal_handler_); 77 test_conf_ = new FakeP2PManagerConfiguration(); 78 79 // Allocate and install a mock policy implementation in the fake Update 80 // Manager. Note that the FakeUpdateManager takes ownership of the policy 81 // object. 82 mock_policy_ = new chromeos_update_manager::MockPolicy(&fake_clock_); 83 fake_um_.set_policy(mock_policy_); 84 85 // Construct the P2P manager under test. 86 manager_.reset(P2PManager::Construct(test_conf_, &fake_clock_, &fake_um_, 87 "cros_au", 3, 88 TimeDelta::FromDays(5))); 89 } 90 91 base::MessageLoopForIO base_loop_; 92 brillo::BaseMessageLoop loop_{&base_loop_}; 93 brillo::AsynchronousSignalHandler async_signal_handler_; 94 Subprocess subprocess_; 95 96 // The P2PManager::Configuration instance used for testing. 97 FakeP2PManagerConfiguration *test_conf_; 98 99 FakeClock fake_clock_; 100 chromeos_update_manager::MockPolicy *mock_policy_ = nullptr; 101 chromeos_update_manager::FakeUpdateManager fake_um_; 102 103 unique_ptr<P2PManager> manager_; 104 }; 105 106 107 // Check that IsP2PEnabled() polls the policy correctly, with the value not 108 // changing between calls. 109 TEST_F(P2PManagerTest, P2PEnabledInitAndNotChanged) { 110 EXPECT_CALL(*mock_policy_, P2PEnabled(_, _, _, _)); 111 EXPECT_CALL(*mock_policy_, P2PEnabledChanged(_, _, _, _, false)); 112 113 EXPECT_FALSE(manager_->IsP2PEnabled()); 114 brillo::MessageLoopRunMaxIterations(MessageLoop::current(), 100); 115 EXPECT_FALSE(manager_->IsP2PEnabled()); 116 } 117 118 // Check that IsP2PEnabled() polls the policy correctly, with the value changing 119 // between calls. 120 TEST_F(P2PManagerTest, P2PEnabledInitAndChanged) { 121 EXPECT_CALL(*mock_policy_, P2PEnabled(_, _, _, _)) 122 .WillOnce(DoAll( 123 SetArgPointee<3>(true), 124 Return(chromeos_update_manager::EvalStatus::kSucceeded))); 125 EXPECT_CALL(*mock_policy_, P2PEnabledChanged(_, _, _, _, true)); 126 EXPECT_CALL(*mock_policy_, P2PEnabledChanged(_, _, _, _, false)); 127 128 EXPECT_TRUE(manager_->IsP2PEnabled()); 129 brillo::MessageLoopRunMaxIterations(MessageLoop::current(), 100); 130 EXPECT_FALSE(manager_->IsP2PEnabled()); 131 } 132 133 // Check that we keep the $N newest files with the .$EXT.p2p extension. 134 TEST_F(P2PManagerTest, HousekeepingCountLimit) { 135 // Specifically pass 0 for |max_file_age| to allow files of any age. Note that 136 // we need to reallocate the test_conf_ member, whose currently aliased object 137 // will be freed. 138 test_conf_ = new FakeP2PManagerConfiguration(); 139 manager_.reset(P2PManager::Construct( 140 test_conf_, &fake_clock_, &fake_um_, "cros_au", 3, 141 TimeDelta() /* max_file_age */)); 142 EXPECT_EQ(manager_->CountSharedFiles(), 0); 143 144 base::Time start_time = base::Time::FromDoubleT(1246996800.); 145 // Generate files with different timestamps matching our pattern and generate 146 // other files not matching the pattern. 147 for (int n = 0; n < 5; n++) { 148 base::FilePath path = test_conf_->GetP2PDir().Append(base::StringPrintf( 149 "file_%d.cros_au.p2p", n)); 150 base::Time file_time = start_time + TimeDelta::FromMinutes(n); 151 EXPECT_EQ(0, base::WriteFile(path, nullptr, 0)); 152 EXPECT_TRUE(base::TouchFile(path, file_time, file_time)); 153 154 path = test_conf_->GetP2PDir().Append(base::StringPrintf( 155 "file_%d.OTHER.p2p", n)); 156 EXPECT_EQ(0, base::WriteFile(path, nullptr, 0)); 157 EXPECT_TRUE(base::TouchFile(path, file_time, file_time)); 158 } 159 // CountSharedFiles() only counts 'cros_au' files. 160 EXPECT_EQ(manager_->CountSharedFiles(), 5); 161 162 EXPECT_TRUE(manager_->PerformHousekeeping()); 163 164 // At this point - after HouseKeeping - we should only have 165 // eight files left. 166 for (int n = 0; n < 5; n++) { 167 string file_name; 168 bool expect; 169 170 expect = (n >= 2); 171 file_name = base::StringPrintf( 172 "%s/file_%d.cros_au.p2p", 173 test_conf_->GetP2PDir().value().c_str(), n); 174 EXPECT_EQ(expect, utils::FileExists(file_name.c_str())); 175 176 file_name = base::StringPrintf( 177 "%s/file_%d.OTHER.p2p", 178 test_conf_->GetP2PDir().value().c_str(), n); 179 EXPECT_TRUE(utils::FileExists(file_name.c_str())); 180 } 181 // CountSharedFiles() only counts 'cros_au' files. 182 EXPECT_EQ(manager_->CountSharedFiles(), 3); 183 } 184 185 // Check that we keep files with the .$EXT.p2p extension not older 186 // than some specificed age (5 days, in this test). 187 TEST_F(P2PManagerTest, HousekeepingAgeLimit) { 188 // We set the cutoff time to be 1 billion seconds (01:46:40 UTC on 9 189 // September 2001 - arbitrary number, but constant to avoid test 190 // flakiness) since the epoch and then we put two files before that 191 // date and three files after. 192 base::Time cutoff_time = base::Time::FromTimeT(1000000000); 193 TimeDelta age_limit = TimeDelta::FromDays(5); 194 195 // Set the clock just so files with a timestamp before |cutoff_time| 196 // will be deleted at housekeeping. 197 fake_clock_.SetWallclockTime(cutoff_time + age_limit); 198 199 // Specifically pass 0 for |num_files_to_keep| to allow any number of files. 200 // Note that we need to reallocate the test_conf_ member, whose currently 201 // aliased object will be freed. 202 test_conf_ = new FakeP2PManagerConfiguration(); 203 manager_.reset(P2PManager::Construct( 204 test_conf_, &fake_clock_, &fake_um_, "cros_au", 205 0 /* num_files_to_keep */, age_limit)); 206 EXPECT_EQ(manager_->CountSharedFiles(), 0); 207 208 // Generate files with different timestamps matching our pattern and generate 209 // other files not matching the pattern. 210 for (int n = 0; n < 5; n++) { 211 base::FilePath path = test_conf_->GetP2PDir().Append(base::StringPrintf( 212 "file_%d.cros_au.p2p", n)); 213 214 // With five files and aiming for two of them to be before 215 // |cutoff_time|, we distribute it like this: 216 // 217 // -------- 0 -------- 1 -------- 2 -------- 3 -------- 4 -------- 218 // | 219 // cutoff_time 220 // 221 base::Time file_date = cutoff_time + (n - 2) * TimeDelta::FromDays(1) 222 + TimeDelta::FromHours(12); 223 224 EXPECT_EQ(0, base::WriteFile(path, nullptr, 0)); 225 EXPECT_TRUE(base::TouchFile(path, file_date, file_date)); 226 227 path = test_conf_->GetP2PDir().Append(base::StringPrintf( 228 "file_%d.OTHER.p2p", n)); 229 EXPECT_EQ(0, base::WriteFile(path, nullptr, 0)); 230 EXPECT_TRUE(base::TouchFile(path, file_date, file_date)); 231 } 232 // CountSharedFiles() only counts 'cros_au' files. 233 EXPECT_EQ(manager_->CountSharedFiles(), 5); 234 235 EXPECT_TRUE(manager_->PerformHousekeeping()); 236 237 // At this point - after HouseKeeping - we should only have 238 // eight files left. 239 for (int n = 0; n < 5; n++) { 240 string file_name; 241 bool expect; 242 243 expect = (n >= 2); 244 file_name = base::StringPrintf( 245 "%s/file_%d.cros_au.p2p", 246 test_conf_->GetP2PDir().value().c_str(), n); 247 EXPECT_EQ(expect, utils::FileExists(file_name.c_str())); 248 249 file_name = base::StringPrintf( 250 "%s/file_%d.OTHER.p2p", 251 test_conf_->GetP2PDir().value().c_str(), n); 252 EXPECT_TRUE(utils::FileExists(file_name.c_str())); 253 } 254 // CountSharedFiles() only counts 'cros_au' files. 255 EXPECT_EQ(manager_->CountSharedFiles(), 3); 256 } 257 258 static bool CheckP2PFile(const string& p2p_dir, const string& file_name, 259 ssize_t expected_size, ssize_t expected_size_xattr) { 260 string path = p2p_dir + "/" + file_name; 261 char ea_value[64] = { 0 }; 262 ssize_t ea_size; 263 264 off_t p2p_size = utils::FileSize(path); 265 if (p2p_size < 0) { 266 LOG(ERROR) << "File " << path << " does not exist"; 267 return false; 268 } 269 270 if (expected_size != 0) { 271 if (p2p_size != expected_size) { 272 LOG(ERROR) << "Expected size " << expected_size 273 << " but size was " << p2p_size; 274 return false; 275 } 276 } 277 278 if (expected_size_xattr == 0) { 279 ea_size = getxattr(path.c_str(), "user.cros-p2p-filesize", 280 &ea_value, sizeof ea_value - 1); 281 if (ea_size == -1 && errno == ENODATA) { 282 // This is valid behavior as we support files without the xattr set. 283 } else { 284 PLOG(ERROR) << "getxattr() didn't fail with ENODATA as expected, " 285 << "ea_size=" << ea_size << ", errno=" << errno; 286 return false; 287 } 288 } else { 289 ea_size = getxattr(path.c_str(), "user.cros-p2p-filesize", 290 &ea_value, sizeof ea_value - 1); 291 if (ea_size < 0) { 292 LOG(ERROR) << "Error getting xattr attribute"; 293 return false; 294 } 295 char* endp = nullptr; 296 long long int val = strtoll(ea_value, &endp, 0); // NOLINT(runtime/int) 297 if (endp == nullptr || *endp != '\0') { 298 LOG(ERROR) << "Error parsing xattr '" << ea_value 299 << "' as an integer"; 300 return false; 301 } 302 if (val != expected_size_xattr) { 303 LOG(ERROR) << "Expected xattr size " << expected_size_xattr 304 << " but size was " << val; 305 return false; 306 } 307 } 308 309 return true; 310 } 311 312 static bool CreateP2PFile(string p2p_dir, string file_name, 313 size_t size, size_t size_xattr) { 314 string path = p2p_dir + "/" + file_name; 315 316 int fd = open(path.c_str(), O_CREAT|O_RDWR, 0644); 317 if (fd == -1) { 318 PLOG(ERROR) << "Error creating file with path " << path; 319 return false; 320 } 321 if (ftruncate(fd, size) != 0) { 322 PLOG(ERROR) << "Error truncating " << path << " to size " << size; 323 close(fd); 324 return false; 325 } 326 327 if (size_xattr != 0) { 328 string decimal_size = std::to_string(size_xattr); 329 if (fsetxattr(fd, "user.cros-p2p-filesize", 330 decimal_size.c_str(), decimal_size.size(), 0) != 0) { 331 PLOG(ERROR) << "Error setting xattr on " << path; 332 close(fd); 333 return false; 334 } 335 } 336 337 close(fd); 338 return true; 339 } 340 341 // Check that sharing a *new* file works. 342 TEST_F(P2PManagerTest, ShareFile) { 343 if (!test_utils::IsXAttrSupported(base::FilePath("/tmp"))) { 344 LOG(WARNING) << "Skipping test because /tmp does not support xattr. " 345 << "Please update your system to support this feature."; 346 return; 347 } 348 const int kP2PTestFileSize = 1000 * 1000; // 1 MB 349 350 EXPECT_TRUE(manager_->FileShare("foo", kP2PTestFileSize)); 351 EXPECT_EQ(manager_->FileGetPath("foo"), 352 test_conf_->GetP2PDir().Append("foo.cros_au.p2p.tmp")); 353 EXPECT_TRUE(CheckP2PFile(test_conf_->GetP2PDir().value(), 354 "foo.cros_au.p2p.tmp", 0, kP2PTestFileSize)); 355 356 // Sharing it again - with the same expected size - should return true 357 EXPECT_TRUE(manager_->FileShare("foo", kP2PTestFileSize)); 358 359 // ... but if we use the wrong size, it should fail 360 EXPECT_FALSE(manager_->FileShare("foo", kP2PTestFileSize + 1)); 361 } 362 363 // Check that making a shared file visible, does what is expected. 364 TEST_F(P2PManagerTest, MakeFileVisible) { 365 if (!test_utils::IsXAttrSupported(base::FilePath("/tmp"))) { 366 LOG(WARNING) << "Skipping test because /tmp does not support xattr. " 367 << "Please update your system to support this feature."; 368 return; 369 } 370 const int kP2PTestFileSize = 1000 * 1000; // 1 MB 371 372 // First, check that it's not visible. 373 manager_->FileShare("foo", kP2PTestFileSize); 374 EXPECT_EQ(manager_->FileGetPath("foo"), 375 test_conf_->GetP2PDir().Append("foo.cros_au.p2p.tmp")); 376 EXPECT_TRUE(CheckP2PFile(test_conf_->GetP2PDir().value(), 377 "foo.cros_au.p2p.tmp", 0, kP2PTestFileSize)); 378 // Make the file visible and check that it changed its name. Do it 379 // twice to check that FileMakeVisible() is idempotent. 380 for (int n = 0; n < 2; n++) { 381 manager_->FileMakeVisible("foo"); 382 EXPECT_EQ(manager_->FileGetPath("foo"), 383 test_conf_->GetP2PDir().Append("foo.cros_au.p2p")); 384 EXPECT_TRUE(CheckP2PFile(test_conf_->GetP2PDir().value(), 385 "foo.cros_au.p2p", 0, kP2PTestFileSize)); 386 } 387 } 388 389 // Check that we return the right values for existing files in P2P_DIR. 390 TEST_F(P2PManagerTest, ExistingFiles) { 391 if (!test_utils::IsXAttrSupported(base::FilePath("/tmp"))) { 392 LOG(WARNING) << "Skipping test because /tmp does not support xattr. " 393 << "Please update your system to support this feature."; 394 return; 395 } 396 397 bool visible; 398 399 // Check that errors are returned if the file does not exist 400 EXPECT_EQ(manager_->FileGetPath("foo"), base::FilePath()); 401 EXPECT_EQ(manager_->FileGetSize("foo"), -1); 402 EXPECT_EQ(manager_->FileGetExpectedSize("foo"), -1); 403 EXPECT_FALSE(manager_->FileGetVisible("foo", nullptr)); 404 // ... then create the file ... 405 EXPECT_TRUE(CreateP2PFile(test_conf_->GetP2PDir().value(), 406 "foo.cros_au.p2p", 42, 43)); 407 // ... and then check that the expected values are returned 408 EXPECT_EQ(manager_->FileGetPath("foo"), 409 test_conf_->GetP2PDir().Append("foo.cros_au.p2p")); 410 EXPECT_EQ(manager_->FileGetSize("foo"), 42); 411 EXPECT_EQ(manager_->FileGetExpectedSize("foo"), 43); 412 EXPECT_TRUE(manager_->FileGetVisible("foo", &visible)); 413 EXPECT_TRUE(visible); 414 415 // One more time, this time with a .tmp variant. First ensure it errors out.. 416 EXPECT_EQ(manager_->FileGetPath("bar"), base::FilePath()); 417 EXPECT_EQ(manager_->FileGetSize("bar"), -1); 418 EXPECT_EQ(manager_->FileGetExpectedSize("bar"), -1); 419 EXPECT_FALSE(manager_->FileGetVisible("bar", nullptr)); 420 // ... then create the file ... 421 EXPECT_TRUE(CreateP2PFile(test_conf_->GetP2PDir().value(), 422 "bar.cros_au.p2p.tmp", 44, 45)); 423 // ... and then check that the expected values are returned 424 EXPECT_EQ(manager_->FileGetPath("bar"), 425 test_conf_->GetP2PDir().Append("bar.cros_au.p2p.tmp")); 426 EXPECT_EQ(manager_->FileGetSize("bar"), 44); 427 EXPECT_EQ(manager_->FileGetExpectedSize("bar"), 45); 428 EXPECT_TRUE(manager_->FileGetVisible("bar", &visible)); 429 EXPECT_FALSE(visible); 430 } 431 432 // This is a little bit ugly but short of mocking a 'p2p' service this 433 // will have to do. E.g. we essentially simulate the various 434 // behaviours of initctl(8) that we rely on. 435 TEST_F(P2PManagerTest, StartP2P) { 436 // Check that we can start the service 437 test_conf_->SetInitctlStartCommand({"true"}); 438 EXPECT_TRUE(manager_->EnsureP2PRunning()); 439 test_conf_->SetInitctlStartCommand({"false"}); 440 EXPECT_FALSE(manager_->EnsureP2PRunning()); 441 test_conf_->SetInitctlStartCommand({ 442 "sh", "-c", "echo \"initctl: Job is already running: p2p\" >&2; false"}); 443 EXPECT_TRUE(manager_->EnsureP2PRunning()); 444 test_conf_->SetInitctlStartCommand({ 445 "sh", "-c", "echo something else >&2; false"}); 446 EXPECT_FALSE(manager_->EnsureP2PRunning()); 447 } 448 449 // Same comment as for StartP2P 450 TEST_F(P2PManagerTest, StopP2P) { 451 // Check that we can start the service 452 test_conf_->SetInitctlStopCommand({"true"}); 453 EXPECT_TRUE(manager_->EnsureP2PNotRunning()); 454 test_conf_->SetInitctlStopCommand({"false"}); 455 EXPECT_FALSE(manager_->EnsureP2PNotRunning()); 456 test_conf_->SetInitctlStopCommand({ 457 "sh", "-c", "echo \"initctl: Unknown instance \" >&2; false"}); 458 EXPECT_TRUE(manager_->EnsureP2PNotRunning()); 459 test_conf_->SetInitctlStopCommand({ 460 "sh", "-c", "echo something else >&2; false"}); 461 EXPECT_FALSE(manager_->EnsureP2PNotRunning()); 462 } 463 464 static void ExpectUrl(const string& expected_url, 465 const string& url) { 466 EXPECT_EQ(url, expected_url); 467 MessageLoop::current()->BreakLoop(); 468 } 469 470 // Like StartP2P, we're mocking the different results that p2p-client 471 // can return. It's not pretty but it works. 472 TEST_F(P2PManagerTest, LookupURL) { 473 // Emulate p2p-client returning valid URL with "fooX", 42 and "cros_au" 474 // being propagated in the right places. 475 test_conf_->SetP2PClientCommand({ 476 "echo", "http://1.2.3.4/{file_id}_{minsize}"}); 477 manager_->LookupUrlForFile("fooX", 42, TimeDelta(), 478 base::Bind(ExpectUrl, 479 "http://1.2.3.4/fooX.cros_au_42")); 480 loop_.Run(); 481 482 // Emulate p2p-client returning invalid URL. 483 test_conf_->SetP2PClientCommand({"echo", "not_a_valid_url"}); 484 manager_->LookupUrlForFile("foobar", 42, TimeDelta(), 485 base::Bind(ExpectUrl, "")); 486 loop_.Run(); 487 488 // Emulate p2p-client conveying failure. 489 test_conf_->SetP2PClientCommand({"false"}); 490 manager_->LookupUrlForFile("foobar", 42, TimeDelta(), 491 base::Bind(ExpectUrl, "")); 492 loop_.Run(); 493 494 // Emulate p2p-client not existing. 495 test_conf_->SetP2PClientCommand({"/path/to/non/existent/helper/program"}); 496 manager_->LookupUrlForFile("foobar", 42, 497 TimeDelta(), 498 base::Bind(ExpectUrl, "")); 499 loop_.Run(); 500 501 // Emulate p2p-client crashing. 502 test_conf_->SetP2PClientCommand({"sh", "-c", "kill -SEGV $$"}); 503 manager_->LookupUrlForFile("foobar", 42, TimeDelta(), 504 base::Bind(ExpectUrl, "")); 505 loop_.Run(); 506 507 // Emulate p2p-client exceeding its timeout. 508 test_conf_->SetP2PClientCommand({ 509 "sh", "-c", "echo http://1.2.3.4/; sleep 2"}); 510 manager_->LookupUrlForFile("foobar", 42, TimeDelta::FromMilliseconds(500), 511 base::Bind(ExpectUrl, "")); 512 loop_.Run(); 513 } 514 515 } // namespace chromeos_update_engine 516