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