p2p: Use p2p for updates
This is the main patch for enabling use of p2p for consuming and/or
sharing updates via p2p. Refer to the ddoc and other documentation for
how this works.
BUG=chromium:260426,chromium:273110
TEST=New unit tests + unit tests pass + manual testing
Change-Id: I6bc3bddae1e041ccc176969a651396e8e89cb3f0
Reviewed-on: https://chromium-review.googlesource.com/64829
Reviewed-by: David Zeuthen <[email protected]>
Commit-Queue: David Zeuthen <[email protected]>
Tested-by: David Zeuthen <[email protected]>
diff --git a/constants.cc b/constants.cc
index 93dd1e3..ffcd112 100644
--- a/constants.cc
+++ b/constants.cc
@@ -20,6 +20,8 @@
const char kSystemRebootedMarkerFile[] = "/tmp/update_engine_update_recorded";
+const char kP2PAllowInteractiveMarkerFile[] =
+ "/mnt/stateful_partition/p2p-allow-interactive";
// Constants defining keys for the persisted state of update engine.
const char kPrefsBackoffExpiryTime[] = "backoff-expiry-time";
diff --git a/constants.h b/constants.h
index e114956..f3e7450 100644
--- a/constants.h
+++ b/constants.h
@@ -26,6 +26,10 @@
// Path to the stateful partition on the root filesystem.
extern const char kStatefulPartition[];
+// Path to the marker file to allow p2p on interactive update attempts.
+// See utils::IsP2PAllowedForInteractiveChecks().
+extern const char kP2PAllowInteractiveMarkerFile[];
+
// Constants related to preferences.
extern const char kPrefsBackoffExpiryTime[];
extern const char kPrefsCertificateReportToSendDownload[];
@@ -92,12 +96,16 @@
kNumPayloadTypes
} PayloadType;
-// Maximum number of attempts using p2p.
+// Maximum number of times we'll allow using p2p for the same update payload.
const int kMaxP2PAttempts = 10;
-// Maximum wallclock time we allow attempting to update using p2p -
-// two days.
-const int kMaxP2PAttemptTimeSeconds = 2*24*60*60;
+// Maximum wallclock time we allow attempting to update using p2p for
+// the same update payload - two days.
+const int kMaxP2PAttemptTimeSeconds = 2 * 24 * 60 * 60;
+
+// The maximum amount of time to spend waiting for p2p-client(1) to
+// return while waiting in line to use the LAN - six hours.
+const int kMaxP2PNetworkWaitTimeSeconds = 6 * 60 * 60;
// The maximum number of payload files to keep in /var/cache/p2p.
const int kMaxP2PFilesToKeep = 3;
diff --git a/delta_performer.cc b/delta_performer.cc
index 0a33811..68161d2 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -488,6 +488,10 @@
return true;
}
+bool DeltaPerformer::IsManifestValid() {
+ return manifest_valid_;
+}
+
bool DeltaPerformer::CanPerformInstallOperation(
const chromeos_update_engine::DeltaArchiveManifest_InstallOperation&
operation) {
diff --git a/delta_performer.h b/delta_performer.h
index 33e39d7..7943d7a 100644
--- a/delta_performer.h
+++ b/delta_performer.h
@@ -97,6 +97,9 @@
// Closes both 'path' given to Open() and the kernel path.
int Close();
+ // Returns |true| only if the manifest has been processed and it's valid.
+ bool IsManifestValid();
+
// Verifies the downloaded payload against the signed hash included in the
// payload, against the update check hash (which is in base64 format) and
// size using the public key and returns kErrorCodeSuccess on success, an
diff --git a/download_action.cc b/download_action.cc
index da6b1fc..17d4538 100644
--- a/download_action.cc
+++ b/download_action.cc
@@ -8,12 +8,19 @@
#include <string>
#include <vector>
#include <glib.h>
+
+#include <base/file_path.h>
+#include <base/stringprintf.h>
+
#include "update_engine/action_pipe.h"
+#include "update_engine/p2p_manager.h"
#include "update_engine/subprocess.h"
using std::min;
using std::string;
using std::vector;
+using base::FilePath;
+using base::StringPrintf;
namespace chromeos_update_engine {
@@ -29,10 +36,118 @@
writer_(NULL),
code_(kErrorCodeSuccess),
delegate_(NULL),
- bytes_received_(0) {}
+ bytes_received_(0),
+ p2p_sharing_fd_(-1),
+ p2p_visible_(true) {}
DownloadAction::~DownloadAction() {}
+void DownloadAction::CloseP2PSharingFd(bool delete_p2p_file) {
+ if (p2p_sharing_fd_ != -1) {
+ if (close(p2p_sharing_fd_) != 0) {
+ PLOG(ERROR) << "Error closing p2p sharing fd";
+ }
+ p2p_sharing_fd_ = -1;
+ }
+
+ if (delete_p2p_file) {
+ FilePath path = system_state_->p2p_manager()->FileGetPath(p2p_file_id_);
+ if (unlink(path.value().c_str()) != 0) {
+ PLOG(ERROR) << "Error deleting p2p file " << path.value();
+ } else {
+ LOG(INFO) << "Deleted p2p file " << path.value();
+ }
+ }
+
+ // Don't use p2p from this point onwards.
+ p2p_file_id_.clear();
+}
+
+bool DownloadAction::SetupP2PSharingFd() {
+ P2PManager *p2p_manager = system_state_->p2p_manager();
+
+ if (!p2p_manager->FileShare(p2p_file_id_, install_plan_.payload_size)) {
+ LOG(ERROR) << "Unable to share file via p2p";
+ CloseP2PSharingFd(true); // delete p2p file
+ return false;
+ }
+
+ // File has already been created (and allocated, xattrs been
+ // populated etc.) by FileShare() so just open it for writing.
+ FilePath path = p2p_manager->FileGetPath(p2p_file_id_);
+ p2p_sharing_fd_ = open(path.value().c_str(), O_WRONLY);
+ if (p2p_sharing_fd_ == -1) {
+ PLOG(ERROR) << "Error opening file " << path.value();
+ CloseP2PSharingFd(true); // Delete p2p file.
+ return false;
+ }
+
+ // Ensure file to share is world-readable, otherwise
+ // p2p-server and p2p-http-server can't access it.
+ //
+ // (Q: Why doesn't the file have mode 0644 already? A: Because
+ // the process-wide umask is set to 0700 in main.cc.)
+ if (fchmod(p2p_sharing_fd_, 0644) != 0) {
+ PLOG(ERROR) << "Error setting mode 0644 on " << path.value();
+ CloseP2PSharingFd(true); // Delete p2p file.
+ return false;
+ }
+
+ // All good.
+ LOG(INFO) << "Writing payload contents to " << path.value();
+ p2p_manager->FileGetVisible(p2p_file_id_, &p2p_visible_);
+ return true;
+}
+
+void DownloadAction::WriteToP2PFile(const char *data,
+ size_t length,
+ off_t file_offset) {
+ if (p2p_sharing_fd_ == -1) {
+ if (!SetupP2PSharingFd())
+ return;
+ }
+
+ // Check that the file is at least |file_offset| bytes long - if
+ // it's not something is wrong and we must immediately delete the
+ // file to avoid propagating this problem to other peers.
+ //
+ // How can this happen? It could be that we're resuming an update
+ // after a system crash... in this case, it could be that
+ //
+ // 1. the p2p file didn't get properly synced to stable storage; or
+ // 2. the file was deleted at bootup (it's in /var/cache after all); or
+ // 3. other reasons
+ struct stat statbuf;
+ if (fstat(p2p_sharing_fd_, &statbuf) != 0) {
+ PLOG(ERROR) << "Error getting file status for p2p file";
+ CloseP2PSharingFd(true); // Delete p2p file.
+ return;
+ }
+ if (statbuf.st_size < file_offset) {
+ LOG(ERROR) << "Wanting to write to file offset " << file_offset
+ << " but existing p2p file is only " << statbuf.st_size
+ << " bytes.";
+ CloseP2PSharingFd(true); // Delete p2p file.
+ return;
+ }
+
+ off_t cur_file_offset = lseek(p2p_sharing_fd_, file_offset, SEEK_SET);
+ if (cur_file_offset != static_cast<off_t>(file_offset)) {
+ PLOG(ERROR) << "Error seeking to position "
+ << file_offset << " in p2p file";
+ CloseP2PSharingFd(true); // Delete p2p file.
+ } else {
+ // OK, seeking worked, now write the data
+ ssize_t bytes_written = write(p2p_sharing_fd_, data, length);
+ if (bytes_written != static_cast<ssize_t>(length)) {
+ PLOG(ERROR) << "Error writing "
+ << length << " bytes at file offset "
+ << file_offset << " in p2p file";
+ CloseP2PSharingFd(true); // Delete p2p file.
+ }
+ }
+}
+
void DownloadAction::PerformAction() {
http_fetcher_->set_delegate(this);
@@ -72,6 +187,33 @@
if (delegate_) {
delegate_->SetDownloadStatus(true); // Set to active.
}
+
+ if (system_state_ != NULL) {
+ string file_id = utils::CalculateP2PFileId(install_plan_.payload_hash,
+ install_plan_.payload_size);
+ if (system_state_->request_params()->use_p2p_for_sharing()) {
+ // If we're sharing the update, store the file_id to convey
+ // that we should write to the file.
+ p2p_file_id_ = file_id;
+ LOG(INFO) << "p2p file id: " << p2p_file_id_;
+ } else {
+ // Even if we're not sharing the update, it could be that
+ // there's a partial file from a previous attempt with the same
+ // hash. If this is the case, we NEED to clean it up otherwise
+ // we're essentially timing out other peers downloading from us
+ // (since we're never going to complete the file).
+ FilePath path = system_state_->p2p_manager()->FileGetPath(file_id);
+ if (!path.empty()) {
+ if (unlink(path.value().c_str()) != 0) {
+ PLOG(ERROR) << "Error deleting p2p file " << path.value();
+ } else {
+ LOG(INFO) << "Deleting partial p2p file " << path.value()
+ << " since we're not using p2p to share.";
+ }
+ }
+ }
+ }
+
http_fetcher_->BeginTransfer(install_plan_.download_url);
}
@@ -83,6 +225,7 @@
if (delegate_) {
delegate_->SetDownloadStatus(false); // Set to inactive.
}
+ CloseP2PSharingFd(false); // Keep p2p file.
// Terminates the transfer. The action is terminated, if necessary, when the
// TransferTerminated callback is received.
http_fetcher_->TerminateTransfer();
@@ -95,6 +238,11 @@
void DownloadAction::ReceivedBytes(HttpFetcher *fetcher,
const char* bytes,
int length) {
+ // Note that bytes_received_ is the current offset.
+ if (!p2p_file_id_.empty()) {
+ WriteToP2PFile(bytes, length, bytes_received_);
+ }
+
bytes_received_ += length;
if (delegate_)
delegate_->BytesReceived(bytes_received_, install_plan_.payload_size);
@@ -107,6 +255,15 @@
TerminateProcessing();
return;
}
+
+ // Call p2p_manager_->FileMakeVisible() when we've successfully
+ // verified the manifest!
+ if (!p2p_visible_ &&
+ delta_performer_.get() && delta_performer_->IsManifestValid()) {
+ LOG(INFO) << "Manifest has been validated. Making p2p file visible.";
+ system_state_->p2p_manager()->FileMakeVisible(p2p_file_id_);
+ p2p_visible_ = true;
+ }
}
void DownloadAction::TransferComplete(HttpFetcher *fetcher, bool successful) {
diff --git a/download_action.h b/download_action.h
index 020aa9c..e6099f9 100644
--- a/download_action.h
+++ b/download_action.h
@@ -81,7 +81,29 @@
HttpFetcher* http_fetcher() { return http_fetcher_.get(); }
+ // Returns the p2p file id for the file being written or the empty
+ // string if we're not writing to a p2p file.
+ std::string p2p_file_id() { return p2p_file_id_; };
+
private:
+ // Closes the file descriptor for the p2p file being written and
+ // clears |p2p_file_id_| to indicate that we're no longer sharing
+ // the file. If |delete_p2p_file| is True, also deletes the file.
+ // If there is no p2p file descriptor, this method does nothing.
+ void CloseP2PSharingFd(bool delete_p2p_file);
+
+ // Starts sharing the p2p file. Must be called before
+ // WriteToP2PFile(). Returns True if this worked.
+ bool SetupP2PSharingFd();
+
+ // Writes |length| bytes of payload from |data| into |file_offset|
+ // of the p2p file. Also does sanity checks; for example ensures we
+ // don't end up with a file with holes in it.
+ //
+ // This method does nothing if SetupP2PSharingFd() hasn't been
+ // called or if CloseP2PSharingFd() has been called.
+ void WriteToP2PFile(const char *data, size_t length, off_t file_offset);
+
// The InstallPlan passed in
InstallPlan install_plan_;
@@ -108,6 +130,17 @@
DownloadActionDelegate* delegate_;
uint64_t bytes_received_;
+ // The file-id for the file we're sharing or the empty string
+ // if we're not using p2p to share.
+ std::string p2p_file_id_;
+
+ // The file descriptor for the p2p file used for caching the payload or -1
+ // if we're not using p2p to share.
+ int p2p_sharing_fd_;
+
+ // Set to |false| if p2p file is not visible.
+ bool p2p_visible_;
+
DISALLOW_COPY_AND_ASSIGN(DownloadAction);
};
diff --git a/download_action_unittest.cc b/download_action_unittest.cc
index e937d71..c53b36c 100644
--- a/download_action_unittest.cc
+++ b/download_action_unittest.cc
@@ -2,17 +2,24 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <string>
-#include <vector>
-
#include <glib.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/file_path.h>
+#include <base/file_util.h>
+#include <base/stringprintf.h>
+
#include "update_engine/action_pipe.h"
#include "update_engine/download_action.h"
#include "update_engine/mock_http_fetcher.h"
+#include "update_engine/mock_system_state.h"
#include "update_engine/omaha_hash_calculator.h"
+#include "update_engine/fake_p2p_manager_configuration.h"
#include "update_engine/prefs_mock.h"
#include "update_engine/test_utils.h"
#include "update_engine/utils.h"
@@ -24,6 +31,10 @@
using testing::_;
using testing::AtLeast;
using testing::InSequence;
+using base::FilePath;
+using base::StringPrintf;
+using file_util::WriteFile;
+using file_util::ReadFileToString;
class DownloadActionTest : public ::testing::Test { };
@@ -417,4 +428,201 @@
g_main_loop_unref(loop);
}
+gboolean StartProcessorInRunLoopForP2P(gpointer user_data) {
+ ActionProcessor* processor = reinterpret_cast<ActionProcessor*>(user_data);
+ processor->StartProcessing();
+ return FALSE;
+}
+
+// Test fixture for P2P tests.
+class P2PDownloadActionTest : public testing::Test {
+protected:
+ P2PDownloadActionTest()
+ : loop_(NULL),
+ start_at_offset_(0) {}
+
+ virtual ~P2PDownloadActionTest() {}
+
+ // Derived from testing::Test.
+ virtual void SetUp() {
+ loop_ = g_main_loop_new(g_main_context_default(), FALSE);
+ }
+
+ // Derived from testing::Test.
+ virtual void TearDown() {
+ if (loop_ != NULL)
+ g_main_loop_unref(loop_);
+ }
+
+ // To be called by tests to setup the download. The
+ // |starting_offset| parameter is for where to resume.
+ void SetupDownload(off_t starting_offset) {
+ start_at_offset_ = starting_offset;
+ // Prepare data 10 kB of data.
+ data_.clear();
+ for (unsigned int i = 0; i < 10 * 1000; i++)
+ data_ += 'a' + (i % 25);
+
+ // Setup p2p.
+ FakeP2PManagerConfiguration *test_conf = new FakeP2PManagerConfiguration();
+ p2p_manager_.reset(P2PManager::Construct(test_conf, NULL, "cros_au", 3));
+ mock_system_state_.set_p2p_manager(p2p_manager_.get());
+ }
+
+ // To be called by tests to perform the download. The
+ // |use_p2p_to_share| parameter is used to indicate whether the
+ // payload should be shared via p2p.
+ void StartDownload(bool use_p2p_to_share) {
+ mock_system_state_.request_params()->set_use_p2p_for_sharing(
+ use_p2p_to_share);
+
+ ScopedTempFile output_temp_file;
+ TestDirectFileWriter writer;
+ InstallPlan install_plan(false,
+ false,
+ "",
+ data_.length(),
+ "1234hash",
+ 0,
+ "",
+ output_temp_file.GetPath(),
+ "");
+ ObjectFeederAction<InstallPlan> feeder_action;
+ feeder_action.set_obj(install_plan);
+ PrefsMock prefs;
+ http_fetcher_ = new MockHttpFetcher(data_.c_str(),
+ data_.length(),
+ NULL);
+ // Note that DownloadAction takes ownership of the passed in HttpFetcher.
+ download_action_.reset(new DownloadAction(&prefs, &mock_system_state_,
+ http_fetcher_));
+ download_action_->SetTestFileWriter(&writer);
+ BondActions(&feeder_action, download_action_.get());
+ DownloadActionTestProcessorDelegate delegate(kErrorCodeSuccess);
+ delegate.loop_ = loop_;
+ delegate.expected_data_ = vector<char>(data_.begin() + start_at_offset_,
+ data_.end());
+ delegate.path_ = output_temp_file.GetPath();
+ processor_.set_delegate(&delegate);
+ processor_.EnqueueAction(&feeder_action);
+ processor_.EnqueueAction(download_action_.get());
+
+ g_timeout_add(0, &StartProcessorInRunLoopForP2P, this);
+ g_main_loop_run(loop_);
+ }
+
+ // The DownloadAction instance under test.
+ scoped_ptr<DownloadAction> download_action_;
+
+ // The HttpFetcher used in the test.
+ MockHttpFetcher* http_fetcher_;
+
+ // The P2PManager used in the test.
+ scoped_ptr<P2PManager> p2p_manager_;
+
+ // The ActionProcessor used for running the actions.
+ ActionProcessor processor_;
+
+ // A fake system state.
+ MockSystemState mock_system_state_;
+
+ // The data being downloaded.
+ string data_;
+
+private:
+ // Callback used in StartDownload() method.
+ static gboolean StartProcessorInRunLoopForP2P(gpointer user_data) {
+ class P2PDownloadActionTest *test =
+ reinterpret_cast<P2PDownloadActionTest*>(user_data);
+ test->processor_.StartProcessing();
+ test->http_fetcher_->SetOffset(test->start_at_offset_);
+ return FALSE;
+ }
+
+ // Mainloop used to make StartDownload() synchronous.
+ GMainLoop *loop_;
+
+ // The requested starting offset passed to SetupDownload().
+ off_t start_at_offset_;
+};
+
+TEST_F(P2PDownloadActionTest, IsWrittenTo) {
+ SetupDownload(0); // starting_offset
+ StartDownload(true); // use_p2p_to_share
+
+ // Check the p2p file and its content matches what was sent.
+ string file_id = download_action_->p2p_file_id();
+ EXPECT_NE(file_id, "");
+ EXPECT_EQ(p2p_manager_->FileGetSize(file_id), data_.length());
+ EXPECT_EQ(p2p_manager_->FileGetExpectedSize(file_id), data_.length());
+ string p2p_file_contents;
+ EXPECT_TRUE(ReadFileToString(p2p_manager_->FileGetPath(file_id),
+ &p2p_file_contents));
+ EXPECT_EQ(data_, p2p_file_contents);
+}
+
+TEST_F(P2PDownloadActionTest, DeleteIfHoleExists) {
+ SetupDownload(1000); // starting_offset
+ StartDownload(true); // use_p2p_to_share
+
+ // DownloadAction should convey that the file is not being shared.
+ // and that we don't have any p2p files.
+ EXPECT_EQ(download_action_->p2p_file_id(), "");
+ EXPECT_EQ(p2p_manager_->CountSharedFiles(), 0);
+}
+
+TEST_F(P2PDownloadActionTest, CanAppend) {
+ SetupDownload(1000); // starting_offset
+
+ // Prepare the file with existing data before starting to write to
+ // it via DownloadAction.
+ string file_id = utils::CalculateP2PFileId("1234hash", data_.length());
+ ASSERT_TRUE(p2p_manager_->FileShare(file_id, data_.length()));
+ string existing_data;
+ for (unsigned int i = 0; i < 1000; i++)
+ existing_data += '0' + (i % 10);
+ ASSERT_EQ(WriteFile(p2p_manager_->FileGetPath(file_id), existing_data.c_str(),
+ 1000), 1000);
+
+ StartDownload(true); // use_p2p_to_share
+
+ // DownloadAction should convey the same file_id and the file should
+ // have the expected size.
+ EXPECT_EQ(download_action_->p2p_file_id(), file_id);
+ EXPECT_EQ(p2p_manager_->FileGetSize(file_id), data_.length());
+ EXPECT_EQ(p2p_manager_->FileGetExpectedSize(file_id), data_.length());
+ string p2p_file_contents;
+ // Check that the first 1000 bytes wasn't touched and that we
+ // appended the remaining as appropriate.
+ EXPECT_TRUE(ReadFileToString(p2p_manager_->FileGetPath(file_id),
+ &p2p_file_contents));
+ EXPECT_EQ(existing_data, p2p_file_contents.substr(0, 1000));
+ EXPECT_EQ(data_.substr(1000), p2p_file_contents.substr(1000));
+}
+
+
+TEST_F(P2PDownloadActionTest, DeletePartialP2PFileIfResumingWithoutP2P) {
+ SetupDownload(1000); // starting_offset
+
+ // Prepare the file with all existing data before starting to write
+ // to it via DownloadAction.
+ string file_id = utils::CalculateP2PFileId("1234hash", data_.length());
+ ASSERT_TRUE(p2p_manager_->FileShare(file_id, data_.length()));
+ string existing_data;
+ for (unsigned int i = 0; i < 1000; i++)
+ existing_data += '0' + (i % 10);
+ ASSERT_EQ(WriteFile(p2p_manager_->FileGetPath(file_id), existing_data.c_str(),
+ 1000), 1000);
+
+ // Check that the file is there.
+ EXPECT_EQ(p2p_manager_->FileGetSize(file_id), 1000);
+ EXPECT_EQ(p2p_manager_->CountSharedFiles(), 1);
+
+ StartDownload(false); // use_p2p_to_share
+
+ // DownloadAction should have deleted the p2p file. Check that it's gone.
+ EXPECT_EQ(p2p_manager_->FileGetSize(file_id), -1);
+ EXPECT_EQ(p2p_manager_->CountSharedFiles(), 0);
+}
+
} // namespace chromeos_update_engine
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index 0e984c0..1c11a4b 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -9,6 +9,7 @@
#include <sstream>
#include <string>
+#include <base/bind.h>
#include <base/logging.h>
#include <base/rand_util.h>
#include <base/string_number_conversions.h>
@@ -20,7 +21,9 @@
#include "update_engine/action_pipe.h"
#include "update_engine/constants.h"
+#include "update_engine/omaha_hash_calculator.h"
#include "update_engine/omaha_request_params.h"
+#include "update_engine/p2p_manager.h"
#include "update_engine/payload_state_interface.h"
#include "update_engine/prefs_interface.h"
#include "update_engine/utils.h"
@@ -47,6 +50,8 @@
// Deprecated: "NeedsAdmin"
static const char* kTagPrompt = "Prompt";
static const char* kTagSha256 = "sha256";
+static const char* kTagDisableP2PForDownloading = "DisableP2PForDownloading";
+static const char* kTagDisableP2PForSharing = "DisableP2PForSharing";
namespace {
@@ -490,10 +495,6 @@
if (!ParseParams(doc, output_object, completer))
return false;
- output_object->update_exists = true;
- SetOutputObject(*output_object);
- completer->set_code(kErrorCodeSuccess);
-
return true;
}
@@ -693,6 +694,10 @@
output_object->deadline = XmlGetProperty(pie_action_node, kTagDeadline);
output_object->max_days_to_scatter =
ParseInt(XmlGetProperty(pie_action_node, kTagMaxDaysToScatter));
+ output_object->disable_p2p_for_downloading =
+ (XmlGetProperty(pie_action_node, kTagDisableP2PForDownloading) == "true");
+ output_object->disable_p2p_for_sharing =
+ (XmlGetProperty(pie_action_node, kTagDisableP2PForSharing) == "true");
string max = XmlGetProperty(pie_action_node, kTagMaxFailureCountPerUrl);
if (!base::StringToUint(max, &output_object->max_failure_count_per_url))
@@ -771,6 +776,8 @@
OmahaResponse output_object;
if (!ParseResponse(doc.get(), &output_object, &completer))
return;
+ output_object.update_exists = true;
+ SetOutputObject(output_object);
if (params_->update_disabled()) {
LOG(INFO) << "Ignoring Omaha updates as updates are disabled by policy.";
@@ -786,11 +793,16 @@
return;
}
- if (ShouldDeferDownload(&output_object)) {
- output_object.update_exists = false;
- LOG(INFO) << "Ignoring Omaha updates as updates are deferred by policy.";
- completer.set_code(kErrorCodeOmahaUpdateDeferredPerPolicy);
- return;
+ // If Omaha says to disable p2p, respect that
+ if (output_object.disable_p2p_for_downloading) {
+ LOG(INFO) << "Forcibly disabling use of p2p for downloading as "
+ << "requested by Omaha.";
+ params_->set_use_p2p_for_downloading(false);
+ }
+ if (output_object.disable_p2p_for_sharing) {
+ LOG(INFO) << "Forcibly disabling use of p2p for sharing as "
+ << "requested by Omaha.";
+ params_->set_use_p2p_for_sharing(false);
}
// Update the payload state with the current response. The payload state
@@ -802,13 +814,100 @@
PayloadStateInterface* payload_state = system_state_->payload_state();
payload_state->SetResponse(output_object);
- if (payload_state->ShouldBackoffDownload()) {
+ // It could be we've already exceeded the deadline for when p2p is
+ // allowed or that we've tried too many times with p2p. Check that.
+ if (params_->use_p2p_for_downloading()) {
+ payload_state->P2PNewAttempt();
+ if (!payload_state->P2PAttemptAllowed()) {
+ LOG(INFO) << "Forcibly disabling use of p2p for downloading because "
+ << "of previous failures when using p2p.";
+ params_->set_use_p2p_for_downloading(false);
+ }
+ }
+
+ // From here on, we'll complete stuff in CompleteProcessing() so
+ // disable |completer| since we'll create a new one in that
+ // function.
+ completer.set_should_complete(false);
+
+ // If we're allowed to use p2p for downloading we do not pay
+ // attention to wall-clock-based waiting if the URL is indeed
+ // available via p2p. Therefore, check if the file is available via
+ // p2p before deferring...
+ if (params_->use_p2p_for_downloading()) {
+ LookupPayloadViaP2P(output_object);
+ } else {
+ CompleteProcessing();
+ }
+}
+
+void OmahaRequestAction::CompleteProcessing() {
+ ScopedActionCompleter completer(processor_, this);
+ OmahaResponse& output_object = const_cast<OmahaResponse&>(GetOutputObject());
+ PayloadStateInterface* payload_state = system_state_->payload_state();
+
+ if (ShouldDeferDownload(&output_object)) {
output_object.update_exists = false;
- LOG(INFO) << "Ignoring Omaha updates in order to backoff our retry "
- "attempts";
- completer.set_code(kErrorCodeOmahaUpdateDeferredForBackoff);
+ LOG(INFO) << "Ignoring Omaha updates as updates are deferred by policy.";
+ completer.set_code(kErrorCodeOmahaUpdateDeferredPerPolicy);
return;
}
+
+ // Only back off if we're not using p2p
+ if (params_->use_p2p_for_downloading() && !params_->p2p_url().empty()) {
+ LOG(INFO) << "Payload backoff logic is disabled because download "
+ << "will happen from local peer (via p2p).";
+ } else {
+ if (payload_state->ShouldBackoffDownload()) {
+ output_object.update_exists = false;
+ LOG(INFO) << "Ignoring Omaha updates in order to backoff our retry "
+ << "attempts";
+ completer.set_code(kErrorCodeOmahaUpdateDeferredForBackoff);
+ return;
+ }
+ }
+
+ completer.set_code(kErrorCodeSuccess);
+}
+
+void OmahaRequestAction::OnLookupPayloadViaP2PCompleted(const string& url) {
+ LOG(INFO) << "Lookup complete, p2p-client returned URL '" << url << "'";
+ if (!url.empty()) {
+ params_->set_p2p_url(url);
+ } else {
+ LOG(INFO) << "Forcibly disabling use of p2p for downloading "
+ << "because no suitable peer could be found.";
+ params_->set_use_p2p_for_downloading(false);
+ }
+ CompleteProcessing();
+}
+
+void OmahaRequestAction::LookupPayloadViaP2P(const OmahaResponse& response) {
+ // The kPrefsUpdateStateNextDataOffset state variable tracks the
+ // offset into the payload of the last completed operation if we're
+ // in the middle of an update. As such, p2p is only useful if the
+ // peer actually has that many bytes.
+ size_t minimum_size = 0;
+ int64_t next_data_offset = -1;
+ if (system_state_ != NULL &&
+ system_state_->prefs()->GetInt64(kPrefsUpdateStateNextDataOffset,
+ &next_data_offset)) {
+ if (next_data_offset > 0) {
+ minimum_size = next_data_offset;
+ }
+ }
+
+ string file_id = utils::CalculateP2PFileId(response.hash, response.size);
+ if (system_state_->p2p_manager() != NULL) {
+ LOG(INFO) << "Checking if payload is available via p2p, file_id="
+ << file_id << " minimum_size=" << minimum_size;
+ system_state_->p2p_manager()->LookupUrlForFile(
+ file_id,
+ minimum_size,
+ TimeDelta::FromHours(kMaxP2PNetworkWaitTimeSeconds),
+ base::Bind(&OmahaRequestAction::OnLookupPayloadViaP2PCompleted,
+ base::Unretained(this)));
+ }
}
bool OmahaRequestAction::ShouldDeferDownload(OmahaResponse* output_object) {
@@ -817,6 +916,16 @@
return false;
}
+ // If we're using p2p to download _and_ we have a p2p URL, we never
+ // defer the download. This is because the download will always
+ // happen from a peer on the LAN and we've been waiting in line for
+ // our turn.
+ if (params_->use_p2p_for_downloading() && !params_->p2p_url().empty()) {
+ LOG(INFO) << "Download not deferred because download "
+ << "will happen from a local peer (via p2p).";
+ return false;
+ }
+
// We should defer the downloads only if we've first satisfied the
// wall-clock-based-waiting period and then the update-check-based waiting
// period, if required.
diff --git a/omaha_request_action.h b/omaha_request_action.h
index 7a4d3f2..4e1bc7d 100644
--- a/omaha_request_action.h
+++ b/omaha_request_action.h
@@ -203,6 +203,16 @@
OmahaResponse* output_object,
ScopedActionCompleter* completer);
+ // Called by TransferComplete() to complete processing, either
+ // asynchronously after looking up resources via p2p or directly.
+ void CompleteProcessing();
+
+ // Helper to asynchronously look up payload on the LAN.
+ void LookupPayloadViaP2P(const OmahaResponse& response);
+
+ // Callback used by LookupPayloadViaP2P().
+ void OnLookupPayloadViaP2PCompleted(const std::string& url);
+
// Global system context.
SystemState* system_state_;
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index cb305f7..428a286 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -58,8 +58,10 @@
false, // delta okay
false, // interactive
"http://url",
- false, // update_disabled
- ""); // target_version_prefix);
+ false, // update_disabled
+ "", // target_version_prefix
+ false, // use_p2p_for_downloading
+ false); // use_p2p_for_sharing
string GetNoUpdateResponse(const string& app_id) {
return string(
@@ -79,7 +81,9 @@
const string& needsadmin,
const string& size,
const string& deadline,
- const string& max_days_to_scatter) {
+ const string& max_days_to_scatter,
+ bool disable_p2p_for_downloading,
+ bool disable_p2p_for_sharing) {
string response =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
"protocol=\"3.0\">"
@@ -99,6 +103,9 @@
"sha256=\"" + hash + "\" "
"needsadmin=\"" + needsadmin + "\" " +
(deadline.empty() ? "" : ("deadline=\"" + deadline + "\" ")) +
+ (disable_p2p_for_downloading ?
+ "DisableP2PForDownloading=\"true\" " : "") +
+ (disable_p2p_for_sharing ? "DisableP2PForSharing=\"true\" " : "") +
"/></actions></manifest></updatecheck></app></response>";
LOG(INFO) << "Response = " << response;
return response;
@@ -124,7 +131,9 @@
needsadmin,
size,
deadline,
- "7");
+ "7",
+ false, // disable_p2p_for_downloading
+ false); // disable_p2p_for sharing
}
class OmahaRequestActionTestProcessorDelegate : public ActionProcessorDelegate {
@@ -196,12 +205,16 @@
// Returns true iff an output response was obtained from the
// OmahaRequestAction. |prefs| may be NULL, in which case a local PrefsMock is
-// used. out_response may be NULL. If |fail_http_response_code| is non-negative,
+// used. |payload_state| may be NULL, in which case a local mock is used.
+// |p2p_manager| may be NULL, in which case a local mock is used.
+// out_response may be NULL. If |fail_http_response_code| is non-negative,
// the transfer will fail with that code. |ping_only| is passed through to the
// OmahaRequestAction constructor. out_post_data may be null; if non-null, the
// post-data received by the mock HttpFetcher is returned.
bool TestUpdateCheck(PrefsInterface* prefs,
- OmahaRequestParams params,
+ PayloadStateInterface *payload_state,
+ P2PManager *p2p_manager,
+ OmahaRequestParams& params,
const string& http_response,
int fail_http_response_code,
bool ping_only,
@@ -218,6 +231,10 @@
MockSystemState mock_system_state;
if (prefs)
mock_system_state.set_prefs(prefs);
+ if (payload_state)
+ mock_system_state.set_payload_state(payload_state);
+ if (p2p_manager)
+ mock_system_state.set_p2p_manager(p2p_manager);
mock_system_state.set_request_params(¶ms);
OmahaRequestAction action(&mock_system_state,
NULL,
@@ -276,6 +293,8 @@
OmahaResponse response;
ASSERT_TRUE(
TestUpdateCheck(NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
GetNoUpdateResponse(OmahaRequestParams::kAppId),
-1,
@@ -290,6 +309,8 @@
OmahaResponse response;
ASSERT_TRUE(
TestUpdateCheck(NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
GetUpdateResponse(OmahaRequestParams::kAppId,
"1.2.3.4", // version
@@ -323,6 +344,8 @@
params.set_update_disabled(true);
ASSERT_FALSE(
TestUpdateCheck(NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
GetUpdateResponse(OmahaRequestParams::kAppId,
"1.2.3.4", // version
@@ -348,6 +371,8 @@
params.set_update_disabled(true);
ASSERT_TRUE(
TestUpdateCheck(NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
GetNoUpdateResponse(OmahaRequestParams::kAppId),
-1,
@@ -376,6 +401,8 @@
ASSERT_FALSE(
TestUpdateCheck(&prefs, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
GetUpdateResponse2(OmahaRequestParams::kAppId,
"1.2.3.4", // version
@@ -387,7 +414,9 @@
"false", // needs admin
"123", // size
"", // deadline
- "7"), // max days to scatter
+ "7", // max days to scatter
+ false, // disable_p2p_for_downloading
+ false), // disable_p2p_for sharing
-1,
false, // ping_only
kErrorCodeOmahaUpdateDeferredPerPolicy,
@@ -399,6 +428,8 @@
params.set_interactive(true);
ASSERT_TRUE(
TestUpdateCheck(&prefs, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
GetUpdateResponse2(OmahaRequestParams::kAppId,
"1.2.3.4", // version
@@ -410,7 +441,9 @@
"false", // needs admin
"123", // size
"", // deadline
- "7"), // max days to scatter
+ "7", // max days to scatter
+ false, // disable_p2p_for_downloading
+ false), // disable_p2p_for sharing
-1,
false, // ping_only
kErrorCodeSuccess,
@@ -440,6 +473,8 @@
ASSERT_TRUE(
TestUpdateCheck(&prefs, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
GetUpdateResponse2(OmahaRequestParams::kAppId,
"1.2.3.4", // version
@@ -451,7 +486,9 @@
"false", // needs admin
"123", // size
"", // deadline
- "7"), // max days to scatter
+ "7", // max days to scatter
+ false, // disable_p2p_for_downloading
+ false), // disable_p2p_for sharing
-1,
false, // ping_only
kErrorCodeSuccess,
@@ -481,6 +518,8 @@
ASSERT_TRUE(
TestUpdateCheck(&prefs, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
GetUpdateResponse2(OmahaRequestParams::kAppId,
"1.2.3.4", // version
@@ -492,7 +531,9 @@
"false", // needs admin
"123", // size
"", // deadline
- "0"), // max days to scatter
+ "0", // max days to scatter
+ false, // disable_p2p_for_downloading
+ false), // disable_p2p_for sharing
-1,
false, // ping_only
kErrorCodeSuccess,
@@ -523,6 +564,8 @@
ASSERT_TRUE(TestUpdateCheck(
&prefs, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
GetUpdateResponse2(OmahaRequestParams::kAppId,
"1.2.3.4", // version
@@ -534,7 +577,9 @@
"false", // needs admin
"123", // size
"", // deadline
- "7"), // max days to scatter
+ "7", // max days to scatter
+ false, // disable_p2p_for_downloading
+ false), // disable_p2p_for sharing
-1,
false, // ping_only
kErrorCodeSuccess,
@@ -568,6 +613,8 @@
ASSERT_FALSE(TestUpdateCheck(
&prefs, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
GetUpdateResponse2(OmahaRequestParams::kAppId,
"1.2.3.4", // version
@@ -579,7 +626,9 @@
"false", // needs admin
"123", // size
"", // deadline
- "7"), // max days to scatter
+ "7", // max days to scatter
+ false, // disable_p2p_for_downloading
+ false), // disable_p2p_for sharing
-1,
false, // ping_only
kErrorCodeOmahaUpdateDeferredPerPolicy,
@@ -595,6 +644,8 @@
params.set_interactive(true);
ASSERT_TRUE(
TestUpdateCheck(&prefs, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
GetUpdateResponse2(OmahaRequestParams::kAppId,
"1.2.3.4", // version
@@ -606,7 +657,9 @@
"false", // needs admin
"123", // size
"", // deadline
- "7"), // max days to scatter
+ "7", // max days to scatter
+ false, // disable_p2p_for_downloading
+ false), // disable_p2p_for sharing
-1,
false, // ping_only
kErrorCodeSuccess,
@@ -638,6 +691,8 @@
ASSERT_FALSE(TestUpdateCheck(
&prefs, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
GetUpdateResponse2(OmahaRequestParams::kAppId,
"1.2.3.4", // version
@@ -649,7 +704,9 @@
"false", // needs admin
"123", // size
"", // deadline
- "7"), // max days to scatter
+ "7", // max days to scatter
+ false, // disable_p2p_for_downloading
+ false), // disable_p2p_for sharing
-1,
false, // ping_only
kErrorCodeOmahaUpdateDeferredPerPolicy,
@@ -667,6 +724,8 @@
params.set_interactive(true);
ASSERT_TRUE(
TestUpdateCheck(&prefs, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
GetUpdateResponse2(OmahaRequestParams::kAppId,
"1.2.3.4", // version
@@ -678,7 +737,9 @@
"false", // needs admin
"123", // size
"", // deadline
- "7"), // max days to scatter
+ "7", // max days to scatter
+ false, // disable_p2p_for_downloading
+ false), // disable_p2p_for sharing
-1,
false, // ping_only
kErrorCodeSuccess,
@@ -716,6 +777,8 @@
OmahaResponse response;
ASSERT_FALSE(
TestUpdateCheck(NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
"invalid xml>",
-1,
@@ -730,6 +793,8 @@
OmahaResponse response;
ASSERT_FALSE(
TestUpdateCheck(NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
"",
-1,
@@ -744,6 +809,8 @@
OmahaResponse response;
ASSERT_FALSE(TestUpdateCheck(
NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
"<daystart elapsed_seconds=\"100\"/>"
@@ -762,6 +829,8 @@
OmahaResponse response;
ASSERT_FALSE(TestUpdateCheck(
NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
"<daystart elapsed_seconds=\"100\"/>"
@@ -780,6 +849,8 @@
OmahaResponse response;
ASSERT_FALSE(TestUpdateCheck(
NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
"<daystart elapsed_seconds=\"100\"/>"
@@ -816,6 +887,8 @@
OmahaResponse response;
ASSERT_TRUE(TestUpdateCheck(NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
input_response,
-1,
@@ -901,11 +974,15 @@
false, // delta okay
false, // interactive
"http://url",
- false, // update_disabled
- ""); // target_version_prefix
+ false, // update_disabled
+ "", // target_version_prefix
+ false, // use_p2p_for_downloading
+ false); // use_p2p_for_sharing
OmahaResponse response;
ASSERT_FALSE(
TestUpdateCheck(NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
"invalid xml>",
-1,
@@ -929,6 +1006,8 @@
OmahaResponse response;
ASSERT_TRUE(
TestUpdateCheck(NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
GetUpdateResponse(OmahaRequestParams::kAppId,
"1.2.3.4", // version
@@ -955,6 +1034,8 @@
OmahaResponse response;
ASSERT_TRUE(
TestUpdateCheck(NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
GetUpdateResponse(OmahaRequestParams::kAppId,
"1.2.3.4", // version
@@ -983,6 +1064,8 @@
.WillOnce(DoAll(SetArgumentPointee<1>(string("")), Return(true)));
EXPECT_CALL(prefs, SetString(kPrefsPreviousVersion, _)).Times(1);
ASSERT_FALSE(TestUpdateCheck(&prefs,
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
"invalid xml>",
-1,
@@ -1014,6 +1097,8 @@
OmahaRequestParams params = kDefaultTestParams;
params.set_update_disabled(true);
ASSERT_FALSE(TestUpdateCheck(&prefs,
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
"invalid xml>",
-1,
@@ -1119,9 +1204,13 @@
delta_okay,
false, // interactive
"http://url",
- false, // update_disabled
- ""); // target_version_prefix
+ false, // update_disabled
+ "", // target_version_prefix
+ false, // use_p2p_for_downloading
+ false); // use_p2p_for_sharing
ASSERT_FALSE(TestUpdateCheck(NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
"invalid xml>",
-1,
@@ -1155,12 +1244,16 @@
"OEM MODEL REV 1234",
"ChromeOSFirmware.1.0",
"EC100",
- true, // delta_okay
+ true, // delta_okay
interactive,
"http://url",
- false, // update_disabled
- ""); // target_version_prefix
+ false, // update_disabled
+ "", // target_version_prefix
+ false, // use_p2p_for_downloading
+ false); // use_p2p_for_sharing
ASSERT_FALSE(TestUpdateCheck(NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
"invalid xml>",
-1,
@@ -1211,6 +1304,8 @@
vector<char> post_data;
ASSERT_TRUE(
TestUpdateCheck(&prefs,
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
GetNoUpdateResponse(OmahaRequestParams::kAppId),
-1,
@@ -1243,6 +1338,8 @@
vector<char> post_data;
ASSERT_TRUE(
TestUpdateCheck(&prefs,
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
GetNoUpdateResponse(OmahaRequestParams::kAppId),
-1,
@@ -1267,6 +1364,8 @@
vector<char> post_data;
ASSERT_TRUE(
TestUpdateCheck(&prefs,
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
GetNoUpdateResponse(OmahaRequestParams::kAppId),
-1,
@@ -1292,6 +1391,8 @@
vector<char> post_data;
ASSERT_TRUE(
TestUpdateCheck(&prefs,
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
GetNoUpdateResponse(OmahaRequestParams::kAppId),
-1,
@@ -1316,6 +1417,8 @@
vector<char> post_data;
EXPECT_TRUE(
TestUpdateCheck(&prefs,
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
GetNoUpdateResponse(OmahaRequestParams::kAppId),
-1,
@@ -1341,6 +1444,8 @@
vector<char> post_data;
ASSERT_TRUE(
TestUpdateCheck(&prefs,
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
"protocol=\"3.0\"><daystart elapsed_seconds=\"100\"/>"
@@ -1373,6 +1478,8 @@
.WillOnce(Return(true));
ASSERT_TRUE(
TestUpdateCheck(&prefs,
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
"protocol=\"3.0\"><daystart elapsed_seconds=\"200\"/>"
@@ -1391,6 +1498,8 @@
EXPECT_CALL(prefs, SetInt64(kPrefsLastRollCallPingDay, _)).Times(0);
ASSERT_TRUE(
TestUpdateCheck(&prefs,
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
"protocol=\"3.0\"><daystart blah=\"200\"/>"
@@ -1409,6 +1518,8 @@
EXPECT_CALL(prefs, SetInt64(kPrefsLastRollCallPingDay, _)).Times(0);
ASSERT_TRUE(
TestUpdateCheck(&prefs,
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
"protocol=\"3.0\"><daystart elapsed_seconds=\"x\"/>"
@@ -1424,6 +1535,8 @@
TEST(OmahaRequestActionTest, NoUniqueIDTest) {
vector<char> post_data;
ASSERT_FALSE(TestUpdateCheck(NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
"invalid xml>",
-1,
@@ -1441,6 +1554,8 @@
OmahaResponse response;
ASSERT_FALSE(
TestUpdateCheck(NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
"",
501,
@@ -1456,6 +1571,8 @@
OmahaResponse response;
ASSERT_FALSE(
TestUpdateCheck(NULL, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
kDefaultTestParams,
"",
1500,
@@ -1485,6 +1602,8 @@
ASSERT_FALSE(TestUpdateCheck(
&prefs, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
GetUpdateResponse2(OmahaRequestParams::kAppId,
"1.2.3.4", // version
@@ -1496,7 +1615,9 @@
"false", // needs admin
"123", // size
"", // deadline
- "7"), // max days to scatter
+ "7", // max days to scatter
+ false, // disable_p2p_for_downloading
+ false), // disable_p2p_for sharing
-1,
false, // ping_only
kErrorCodeOmahaUpdateDeferredPerPolicy,
@@ -1512,6 +1633,8 @@
params.set_interactive(true);
ASSERT_TRUE(
TestUpdateCheck(&prefs, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
GetUpdateResponse2(OmahaRequestParams::kAppId,
"1.2.3.4", // version
@@ -1523,7 +1646,9 @@
"false", // needs admin
"123", // size
"", // deadline
- "7"), // max days to scatter
+ "7", // max days to scatter
+ false, // disable_p2p_for_downloading
+ false), // disable_p2p_for sharing
-1,
false, // ping_only
kErrorCodeSuccess,
@@ -1555,6 +1680,8 @@
ASSERT_TRUE(prefs.SetInt64(kPrefsUpdateFirstSeenAt, t1.ToInternalValue()));
ASSERT_TRUE(TestUpdateCheck(
&prefs, // prefs
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
GetUpdateResponse2(OmahaRequestParams::kAppId,
"1.2.3.4", // version
@@ -1566,7 +1693,9 @@
"false", // needs admin
"123", // size
"", // deadline
- "7"), // max days to scatter
+ "7", // max days to scatter
+ false, // disable_p2p_for_downloading
+ false), // disable_p2p_for sharing
-1,
false, // ping_only
kErrorCodeSuccess,
@@ -1610,6 +1739,8 @@
EXPECT_TRUE(params.to_more_stable_channel());
EXPECT_TRUE(params.is_powerwash_allowed());
ASSERT_FALSE(TestUpdateCheck(&prefs,
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
"invalid xml>",
-1,
@@ -1655,6 +1786,8 @@
EXPECT_FALSE(params.to_more_stable_channel());
EXPECT_FALSE(params.is_powerwash_allowed());
ASSERT_FALSE(TestUpdateCheck(&prefs,
+ NULL, // payload_state
+ NULL, // p2p_manager
params,
"invalid xml>",
-1,
@@ -1673,4 +1806,145 @@
ASSERT_TRUE(utils::RecursiveUnlinkDir(test_dir));
}
+void P2PTest(bool initial_allow_p2p_for_downloading,
+ bool initial_allow_p2p_for_sharing,
+ bool omaha_disable_p2p_for_downloading,
+ bool omaha_disable_p2p_for_sharing,
+ bool payload_state_allow_p2p_attempt,
+ bool expect_p2p_client_lookup,
+ const string& p2p_client_result_url,
+ bool expected_allow_p2p_for_downloading,
+ bool expected_allow_p2p_for_sharing,
+ const string& expected_p2p_url) {
+ OmahaResponse response;
+ OmahaRequestParams request_params = kDefaultTestParams;
+ request_params.set_use_p2p_for_downloading(initial_allow_p2p_for_downloading);
+ request_params.set_use_p2p_for_sharing(initial_allow_p2p_for_sharing);
+
+ MockPayloadState mock_payload_state;
+ EXPECT_CALL(mock_payload_state, P2PAttemptAllowed())
+ .WillRepeatedly(Return(payload_state_allow_p2p_attempt));
+ MockP2PManager mock_p2p_manager;
+ mock_p2p_manager.fake().SetLookupUrlForFileResult(p2p_client_result_url);
+
+ EXPECT_CALL(mock_p2p_manager, LookupUrlForFile(_, _, _, _))
+ .Times(expect_p2p_client_lookup ? 1 : 0);
+
+ ASSERT_TRUE(
+ TestUpdateCheck(NULL, // prefs
+ &mock_payload_state,
+ &mock_p2p_manager,
+ request_params,
+ GetUpdateResponse2(OmahaRequestParams::kAppId,
+ "1.2.3.4", // version
+ "http://more/info",
+ "true", // prompt
+ "http://code/base/", // dl url
+ "file.signed", // file name
+ "HASH1234=", // checksum
+ "false", // needs admin
+ "123", // size
+ "", // deadline
+ "7", // max days to scatter
+ omaha_disable_p2p_for_downloading,
+ omaha_disable_p2p_for_sharing),
+ -1,
+ false, // ping_only
+ kErrorCodeSuccess,
+ &response,
+ NULL));
+ EXPECT_TRUE(response.update_exists);
+
+ EXPECT_EQ(response.disable_p2p_for_downloading,
+ omaha_disable_p2p_for_downloading);
+ EXPECT_EQ(response.disable_p2p_for_sharing,
+ omaha_disable_p2p_for_sharing);
+
+ EXPECT_EQ(request_params.use_p2p_for_downloading(),
+ expected_allow_p2p_for_downloading);
+
+ EXPECT_EQ(request_params.use_p2p_for_sharing(),
+ expected_allow_p2p_for_sharing);
+
+ EXPECT_EQ(request_params.p2p_url(), expected_p2p_url);
+}
+
+TEST(OmahaRequestActionTest, P2PWithPeer) {
+ P2PTest(true, // initial_allow_p2p_for_downloading
+ true, // initial_allow_p2p_for_sharing
+ false, // omaha_disable_p2p_for_downloading
+ false, // omaha_disable_p2p_for_sharing
+ true, // payload_state_allow_p2p_attempt
+ true, // expect_p2p_client_lookup
+ "http://1.3.5.7/p2p", // p2p_client_result_url
+ true, // expected_allow_p2p_for_downloading
+ true, // expected_allow_p2p_for_sharing
+ "http://1.3.5.7/p2p"); // expected_p2p_url
+}
+
+TEST(OmahaRequestActionTest, P2PWithoutPeer) {
+ P2PTest(true, // initial_allow_p2p_for_downloading
+ true, // initial_allow_p2p_for_sharing
+ false, // omaha_disable_p2p_for_downloading
+ false, // omaha_disable_p2p_for_sharing
+ true, // payload_state_allow_p2p_attempt
+ true, // expect_p2p_client_lookup
+ "", // p2p_client_result_url
+ false, // expected_allow_p2p_for_downloading
+ true, // expected_allow_p2p_for_sharing
+ ""); // expected_p2p_url
+}
+
+TEST(OmahaRequestActionTest, P2PDownloadNotAllowed) {
+ P2PTest(false, // initial_allow_p2p_for_downloading
+ true, // initial_allow_p2p_for_sharing
+ false, // omaha_disable_p2p_for_downloading
+ false, // omaha_disable_p2p_for_sharing
+ true, // payload_state_allow_p2p_attempt
+ false, // expect_p2p_client_lookup
+ "unset", // p2p_client_result_url
+ false, // expected_allow_p2p_for_downloading
+ true, // expected_allow_p2p_for_sharing
+ ""); // expected_p2p_url
+}
+
+TEST(OmahaRequestActionTest, P2PWithPeerDownloadDisabledByOmaha) {
+ P2PTest(true, // initial_allow_p2p_for_downloading
+ true, // initial_allow_p2p_for_sharing
+ true, // omaha_disable_p2p_for_downloading
+ false, // omaha_disable_p2p_for_sharing
+ true, // payload_state_allow_p2p_attempt
+ false, // expect_p2p_client_lookup
+ "unset", // p2p_client_result_url
+ false, // expected_allow_p2p_for_downloading
+ true, // expected_allow_p2p_for_sharing
+ ""); // expected_p2p_url
+}
+
+TEST(OmahaRequestActionTest, P2PWithPeerSharingDisabledByOmaha) {
+ P2PTest(true, // initial_allow_p2p_for_downloading
+ true, // initial_allow_p2p_for_sharing
+ false, // omaha_disable_p2p_for_downloading
+ true, // omaha_disable_p2p_for_sharing
+ true, // payload_state_allow_p2p_attempt
+ true, // expect_p2p_client_lookup
+ "http://1.3.5.7/p2p", // p2p_client_result_url
+ true, // expected_allow_p2p_for_downloading
+ false, // expected_allow_p2p_for_sharing
+ "http://1.3.5.7/p2p"); // expected_p2p_url
+}
+
+TEST(OmahaRequestActionTest, P2PWithPeerBothDisabledByOmaha) {
+ P2PTest(true, // initial_allow_p2p_for_downloading
+ true, // initial_allow_p2p_for_sharing
+ true, // omaha_disable_p2p_for_downloading
+ true, // omaha_disable_p2p_for_sharing
+ true, // payload_state_allow_p2p_attempt
+ false, // expect_p2p_client_lookup
+ "unset", // p2p_client_result_url
+ false, // expected_allow_p2p_for_downloading
+ false, // expected_allow_p2p_for_sharing
+ ""); // expected_p2p_url
+}
+
} // namespace chromeos_update_engine
diff --git a/omaha_request_params.h b/omaha_request_params.h
index 19a1900..f644775 100644
--- a/omaha_request_params.h
+++ b/omaha_request_params.h
@@ -44,7 +44,9 @@
max_update_checks_allowed_(kDefaultMaxUpdateChecks),
is_powerwash_allowed_(false),
force_lock_down_(false),
- forced_lock_down_(false) {
+ forced_lock_down_(false),
+ use_p2p_for_downloading_(false),
+ use_p2p_for_sharing_(false) {
InitFromLsbValue();
}
@@ -64,7 +66,9 @@
bool in_interactive,
const std::string& in_update_url,
bool in_update_disabled,
- const std::string& in_target_version_prefix)
+ const std::string& in_target_version_prefix,
+ bool in_use_p2p_for_downloading,
+ bool in_use_p2p_for_sharing)
: system_state_(system_state),
os_platform_(in_os_platform),
os_version_(in_os_version),
@@ -90,7 +94,9 @@
max_update_checks_allowed_(kDefaultMaxUpdateChecks),
is_powerwash_allowed_(false),
force_lock_down_(false),
- forced_lock_down_(false) {}
+ forced_lock_down_(false),
+ use_p2p_for_downloading_(in_use_p2p_for_downloading),
+ use_p2p_for_sharing_(in_use_p2p_for_sharing) {}
// Setters and getters for the various properties.
inline std::string os_platform() const { return os_platform_; }
@@ -171,6 +177,27 @@
return max_update_checks_allowed_;
}
+ inline void set_use_p2p_for_downloading(bool value) {
+ use_p2p_for_downloading_ = value;
+ }
+ inline bool use_p2p_for_downloading() const {
+ return use_p2p_for_downloading_;
+ }
+
+ inline void set_use_p2p_for_sharing(bool value) {
+ use_p2p_for_sharing_ = value;
+ }
+ inline bool use_p2p_for_sharing() const {
+ return use_p2p_for_sharing_;
+ }
+
+ inline void set_p2p_url(const std::string& value) {
+ p2p_url_ = value;
+ }
+ inline std::string p2p_url() const {
+ return p2p_url_;
+ }
+
// True if we're trying to update to a more stable channel.
// i.e. index(target_channel) > index(current_channel).
bool to_more_stable_channel() const;
@@ -354,6 +381,18 @@
bool force_lock_down_;
bool forced_lock_down_;
+ // True if we may use p2p to download. This is based on owner
+ // preferences and policy.
+ bool use_p2p_for_downloading_;
+
+ // True if we may use p2p to share. This is based on owner
+ // preferences and policy.
+ bool use_p2p_for_sharing_;
+
+ // An URL to a local peer serving the requested payload or "" if no
+ // such peer is available.
+ std::string p2p_url_;
+
// TODO(jaysri): Uncomment this after fixing unit tests, as part of
// chromium-os:39752
// DISALLOW_COPY_AND_ASSIGN(OmahaRequestParams);
diff --git a/omaha_response.h b/omaha_response.h
index 85cec5a..04fb878 100644
--- a/omaha_response.h
+++ b/omaha_response.h
@@ -26,7 +26,9 @@
max_failure_count_per_url(0),
prompt(false),
is_delta_payload(false),
- disable_payload_backoff(false) {}
+ disable_payload_backoff(false),
+ disable_p2p_for_downloading(false),
+ disable_p2p_for_sharing(false) {}
// True iff there is an update to be downloaded.
bool update_exists;
@@ -61,6 +63,12 @@
// True if the Omaha rule instructs us to disable the backoff logic
// on the client altogether. False otherwise.
bool disable_payload_backoff;
+
+ // True if the Omaha rule instructs us to disable p2p for downloading.
+ bool disable_p2p_for_downloading;
+
+ // True if the Omaha rule instructs us to disable p2p for sharing.
+ bool disable_p2p_for_sharing;
};
COMPILE_ASSERT(sizeof(off_t) == 8, off_t_not_64bit);
diff --git a/omaha_response_handler_action.cc b/omaha_response_handler_action.cc
index 2acc803..7df4c8d 100644
--- a/omaha_response_handler_action.cc
+++ b/omaha_response_handler_action.cc
@@ -69,6 +69,16 @@
install_plan_.download_url = current_url;
install_plan_.version = response.version;
+ OmahaRequestParams* params = system_state_->request_params();
+
+ // If we're using p2p to download and there is a local peer, use it.
+ if (params->use_p2p_for_downloading() && !params->p2p_url().empty()) {
+ LOG(INFO) << "Replacing URL " << install_plan_.download_url
+ << " with local URL " << params->p2p_url()
+ << " since p2p is enabled.";
+ install_plan_.download_url = params->p2p_url();
+ }
+
// Fill up the other properties based on the response.
install_plan_.payload_size = response.size;
install_plan_.payload_hash = response.hash;
@@ -98,7 +108,6 @@
system_state_->hardware()->KernelDeviceOfBootDevice(
install_plan_.install_path);
- OmahaRequestParams* params = system_state_->request_params();
if (params->to_more_stable_channel() && params->is_powerwash_allowed())
install_plan_.powerwash_required = true;
@@ -139,6 +148,13 @@
return false;
}
+ // If we're using p2p, |install_plan_.download_url| may contain a
+ // HTTP URL even if |response.payload_urls| contain only HTTPS URLs.
+ if (!StartsWithASCII(install_plan_.download_url, "https://", false)) {
+ LOG(INFO) << "Mandating hash checks since download_url is not HTTPS.";
+ return true;
+ }
+
// TODO(jaysri): VALIDATION: For official builds, we currently waive hash
// checks for HTTPS until we have rolled out at least once and are confident
// nothing breaks. chromium-os:37082 tracks turning this on for HTTPS
diff --git a/omaha_response_handler_action_unittest.cc b/omaha_response_handler_action_unittest.cc
index c3efb8b..9624b41 100644
--- a/omaha_response_handler_action_unittest.cc
+++ b/omaha_response_handler_action_unittest.cc
@@ -362,4 +362,29 @@
ASSERT_TRUE(utils::RecursiveUnlinkDir(test_dir));
}
+TEST_F(OmahaResponseHandlerActionTest, P2PUrlIsUsedAndHashChecksMandatory) {
+ OmahaResponse in;
+ in.update_exists = true;
+ in.version = "a.b.c.d";
+ in.payload_urls.push_back("https://would.not/cause/hash/checks");
+ in.more_info_url = "http://more/info";
+ in.hash = "HASHj+";
+ in.size = 12;
+
+ MockSystemState mock_system_state;
+ OmahaRequestParams params(&mock_system_state);
+ mock_system_state.set_request_params(¶ms);
+
+ string p2p_url = "http://9.8.7.6/p2p";
+ params.set_p2p_url(p2p_url);
+ params.set_use_p2p_for_downloading(true);
+
+ InstallPlan install_plan;
+ EXPECT_TRUE(DoTestCommon(&mock_system_state, in, "/dev/sda5", "",
+ &install_plan));
+ EXPECT_EQ(in.hash, install_plan.payload_hash);
+ EXPECT_EQ(install_plan.download_url, p2p_url);
+ EXPECT_TRUE(install_plan.hash_checks_mandatory);
+}
+
} // namespace chromeos_update_engine
diff --git a/update_attempter.cc b/update_attempter.cc
index 9ed321d..72d77c9 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -27,6 +27,7 @@
#include "update_engine/omaha_request_action.h"
#include "update_engine/omaha_request_params.h"
#include "update_engine/omaha_response_handler_action.h"
+#include "update_engine/p2p_manager.h"
#include "update_engine/payload_state_interface.h"
#include "update_engine/postinstall_runner_action.h"
#include "update_engine/prefs_interface.h"
@@ -218,6 +219,48 @@
system_state_->set_device_policy(device_policy);
}
+void UpdateAttempter::CalculateP2PParams(bool interactive) {
+ bool use_p2p_for_downloading = false;
+ bool use_p2p_for_sharing = false;
+
+ // Never use p2p for downloading in interactive checks unless the
+ // developer has opted in for it via a marker file.
+ //
+ // (Why would a developer want to opt in? If he's working on the
+ // update_engine or p2p codebases so he can actually test his
+ // code.).
+
+ if (system_state_ != NULL) {
+ if (!system_state_->p2p_manager()->IsP2PEnabled()) {
+ LOG(INFO) << "p2p is not enabled - disallowing p2p for both"
+ << " downloading and sharing.";
+ } else {
+ // Allow p2p for sharing, even in interactive checks.
+ use_p2p_for_sharing = true;
+ if (!interactive) {
+ LOG(INFO) << "Non-interactive check - allowing p2p for downloading";
+ use_p2p_for_downloading = true;
+ } else {
+ // TODO(zeuthen,chromium:273251): Remove this marker once we
+ // can easily trigger non-interactive checks.
+ if (utils::IsP2PAllowedForInteractiveChecks()) {
+ LOG(INFO) << "Found marker file at "
+ << kP2PAllowInteractiveMarkerFile
+ << " - allowing p2p for downloading despite the fact"
+ << " the attempt is interactive.";
+ use_p2p_for_downloading = true;
+ } else {
+ LOG(INFO) << "Forcibly disabling use of p2p for downloading "
+ << "since this update attempt is interactive.";
+ }
+ }
+ }
+ }
+
+ omaha_request_params_->set_use_p2p_for_downloading(use_p2p_for_downloading);
+ omaha_request_params_->set_use_p2p_for_sharing(use_p2p_for_sharing);
+}
+
bool UpdateAttempter::CalculateUpdateParams(const string& app_version,
const string& omaha_url,
bool obey_proxies,
@@ -252,6 +295,19 @@
CalculateScatteringParams(interactive);
+ CalculateP2PParams(interactive);
+ if (omaha_request_params_->use_p2p_for_downloading() ||
+ omaha_request_params_->use_p2p_for_sharing()) {
+ // OK, p2p is to be used - start it and perform housekeeping.
+ if (!StartP2PAndPerformHousekeeping()) {
+ // If this fails, disable p2p for this attempt
+ LOG(INFO) << "Forcibly disabling use of p2p since starting p2p or "
+ << "performing housekeeping failed.";
+ omaha_request_params_->set_use_p2p_for_downloading(false);
+ omaha_request_params_->set_use_p2p_for_sharing(false);
+ }
+ }
+
// Determine whether an alternative test address should be used.
string omaha_url_to_use = omaha_url;
if ((is_using_test_url_ = (omaha_url_to_use.empty() && is_test_mode_))) {
@@ -311,6 +367,11 @@
<< ", Waiting Period = " << utils::FormatSecs(
omaha_request_params_->waiting_period().InSeconds());
+ LOG(INFO) << "Use p2p For Downloading = "
+ << omaha_request_params_->use_p2p_for_downloading()
+ << ", Use p2p For Sharing = "
+ << omaha_request_params_->use_p2p_for_sharing();
+
obeying_proxies_ = true;
if (obey_proxies || proxy_manual_checks_ == 0) {
LOG(INFO) << "forced to obey proxies";
@@ -1250,8 +1311,51 @@
return false;
}
+
void UpdateAttempter::UpdateEngineStarted() {
system_state_->payload_state()->UpdateEngineStarted();
+ StartP2PAtStartup();
+}
+
+bool UpdateAttempter::StartP2PAtStartup() {
+ if (system_state_ == NULL ||
+ !system_state_->p2p_manager()->IsP2PEnabled()) {
+ LOG(INFO) << "Not starting p2p at startup since it's not enabled.";
+ return false;
+ }
+
+ if (system_state_->p2p_manager()->CountSharedFiles() < 1) {
+ LOG(INFO) << "Not starting p2p at startup since our application "
+ << "is not sharing any files.";
+ return false;
+ }
+
+ return StartP2PAndPerformHousekeeping();
+}
+
+bool UpdateAttempter::StartP2PAndPerformHousekeeping() {
+ if (system_state_ == NULL)
+ return false;
+
+ if (!system_state_->p2p_manager()->IsP2PEnabled()) {
+ LOG(INFO) << "Not starting p2p since it's not enabled.";
+ return false;
+ }
+
+ LOG(INFO) << "Ensuring that p2p is running.";
+ if (!system_state_->p2p_manager()->EnsureP2PRunning()) {
+ LOG(ERROR) << "Error starting p2p.";
+ return false;
+ }
+
+ LOG(INFO) << "Performing p2p housekeeping.";
+ if (!system_state_->p2p_manager()->PerformHousekeeping()) {
+ LOG(ERROR) << "Error performing housekeeping for p2p.";
+ return false;
+ }
+
+ LOG(INFO) << "Done performing p2p housekeeping.";
+ return true;
}
} // namespace chromeos_update_engine
diff --git a/update_attempter.h b/update_attempter.h
index dea9bc9..b2ee3b0 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -305,6 +305,20 @@
// Returns True if successfully decremented, false otherwise.
bool DecrementUpdateCheckCount();
+ // Starts p2p and performs housekeeping. Returns true only if p2p is
+ // running and housekeeping was done.
+ bool StartP2PAndPerformHousekeeping();
+
+ // Calculates whether peer-to-peer should be used. Sets the
+ // |use_p2p_to_download_| and |use_p2p_to_share_| parameters
+ // on the |omaha_request_params_| object.
+ void CalculateP2PParams(bool interactive);
+
+ // Starts P2P if it's enabled and there are files to actually share.
+ // Called only at program startup. Returns true only if p2p was
+ // started and housekeeping was performed.
+ bool StartP2PAtStartup();
+
// Last status notification timestamp used for throttling. Use monotonic
// TimeTicks to ensure that notifications are sent even if the system clock is
// set back in the middle of an update.
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 1a52095..7036c83 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -13,6 +13,7 @@
#include "update_engine/install_plan.h"
#include "update_engine/mock_dbus_interface.h"
#include "update_engine/mock_http_fetcher.h"
+#include "update_engine/mock_p2p_manager.h"
#include "update_engine/mock_payload_state.h"
#include "update_engine/mock_system_state.h"
#include "update_engine/postinstall_runner_action.h"
@@ -125,6 +126,21 @@
static gboolean StaticNoScatteringDoneDuringManualUpdateTestStart(
gpointer data);
+ void P2PNotEnabledStart();
+ static gboolean StaticP2PNotEnabled(gpointer data);
+
+ void P2PEnabledStart();
+ static gboolean StaticP2PEnabled(gpointer data);
+
+ void P2PEnabledInteractiveStart();
+ static gboolean StaticP2PEnabledInteractive(gpointer data);
+
+ void P2PEnabledStartingFailsStart();
+ static gboolean StaticP2PEnabledStartingFails(gpointer data);
+
+ void P2PEnabledHousekeepingFailsStart();
+ static gboolean StaticP2PEnabledHousekeepingFails(gpointer data);
+
NiceMock<MockSystemState> mock_system_state_;
NiceMock<MockDbusGlib> dbus_;
UpdateAttempterUnderTest attempter_;
@@ -673,6 +689,167 @@
g_idle_add(&StaticQuitMainLoop, this);
}
+TEST_F(UpdateAttempterTest, P2PNotStartedAtStartupWhenNotEnabled) {
+ MockP2PManager mock_p2p_manager;
+ mock_system_state_.set_p2p_manager(&mock_p2p_manager);
+ mock_p2p_manager.fake().SetP2PEnabled(false);
+ EXPECT_CALL(mock_p2p_manager, EnsureP2PRunning()).Times(0);
+ attempter_.UpdateEngineStarted();
+}
+
+TEST_F(UpdateAttempterTest, P2PNotStartedAtStartupWhenEnabledButNotSharing) {
+ MockP2PManager mock_p2p_manager;
+ mock_system_state_.set_p2p_manager(&mock_p2p_manager);
+ mock_p2p_manager.fake().SetP2PEnabled(true);
+ EXPECT_CALL(mock_p2p_manager, EnsureP2PRunning()).Times(0);
+ attempter_.UpdateEngineStarted();
+}
+
+TEST_F(UpdateAttempterTest, P2PStartedAtStartupWhenEnabledAndSharing) {
+ MockP2PManager mock_p2p_manager;
+ mock_system_state_.set_p2p_manager(&mock_p2p_manager);
+ mock_p2p_manager.fake().SetP2PEnabled(true);
+ mock_p2p_manager.fake().SetCountSharedFilesResult(1);
+ EXPECT_CALL(mock_p2p_manager, EnsureP2PRunning()).Times(1);
+ attempter_.UpdateEngineStarted();
+}
+
+TEST_F(UpdateAttempterTest, P2PNotEnabled) {
+ loop_ = g_main_loop_new(g_main_context_default(), FALSE);
+ g_idle_add(&StaticP2PNotEnabled, this);
+ g_main_loop_run(loop_);
+ g_main_loop_unref(loop_);
+ loop_ = NULL;
+}
+gboolean UpdateAttempterTest::StaticP2PNotEnabled(gpointer data) {
+ UpdateAttempterTest* ua_test = reinterpret_cast<UpdateAttempterTest*>(data);
+ ua_test->P2PNotEnabledStart();
+ return FALSE;
+}
+void UpdateAttempterTest::P2PNotEnabledStart() {
+ // If P2P is not enabled, check that we do not attempt housekeeping
+ // and do not convey that p2p is to be used.
+ MockP2PManager mock_p2p_manager;
+ mock_system_state_.set_p2p_manager(&mock_p2p_manager);
+ mock_p2p_manager.fake().SetP2PEnabled(false);
+ EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(0);
+ attempter_.Update("", "", false, false, false);
+ EXPECT_FALSE(attempter_.omaha_request_params_->use_p2p_for_downloading());
+ EXPECT_FALSE(attempter_.omaha_request_params_->use_p2p_for_sharing());
+ g_idle_add(&StaticQuitMainLoop, this);
+}
+
+TEST_F(UpdateAttempterTest, P2PEnabledStartingFails) {
+ loop_ = g_main_loop_new(g_main_context_default(), FALSE);
+ g_idle_add(&StaticP2PEnabledStartingFails, this);
+ g_main_loop_run(loop_);
+ g_main_loop_unref(loop_);
+ loop_ = NULL;
+}
+gboolean UpdateAttempterTest::StaticP2PEnabledStartingFails(
+ gpointer data) {
+ UpdateAttempterTest* ua_test = reinterpret_cast<UpdateAttempterTest*>(data);
+ ua_test->P2PEnabledStartingFailsStart();
+ return FALSE;
+}
+void UpdateAttempterTest::P2PEnabledStartingFailsStart() {
+ // If p2p is enabled, but starting it fails ensure we don't do
+ // any housekeeping and do not convey that p2p should be used.
+ MockP2PManager mock_p2p_manager;
+ mock_system_state_.set_p2p_manager(&mock_p2p_manager);
+ mock_p2p_manager.fake().SetP2PEnabled(true);
+ mock_p2p_manager.fake().SetEnsureP2PRunningResult(false);
+ mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
+ EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(0);
+ attempter_.Update("", "", false, false, false);
+ EXPECT_FALSE(attempter_.omaha_request_params_->use_p2p_for_downloading());
+ EXPECT_FALSE(attempter_.omaha_request_params_->use_p2p_for_sharing());
+ g_idle_add(&StaticQuitMainLoop, this);
+}
+
+TEST_F(UpdateAttempterTest, P2PEnabledHousekeepingFails) {
+ loop_ = g_main_loop_new(g_main_context_default(), FALSE);
+ g_idle_add(&StaticP2PEnabledHousekeepingFails, this);
+ g_main_loop_run(loop_);
+ g_main_loop_unref(loop_);
+ loop_ = NULL;
+}
+gboolean UpdateAttempterTest::StaticP2PEnabledHousekeepingFails(
+ gpointer data) {
+ UpdateAttempterTest* ua_test = reinterpret_cast<UpdateAttempterTest*>(data);
+ ua_test->P2PEnabledHousekeepingFailsStart();
+ return FALSE;
+}
+void UpdateAttempterTest::P2PEnabledHousekeepingFailsStart() {
+ // If p2p is enabled, starting it works but housekeeping fails, ensure
+ // we do not convey p2p is to be used.
+ MockP2PManager mock_p2p_manager;
+ mock_system_state_.set_p2p_manager(&mock_p2p_manager);
+ mock_p2p_manager.fake().SetP2PEnabled(true);
+ mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
+ mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
+ EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(1);
+ attempter_.Update("", "", false, false, false);
+ EXPECT_FALSE(attempter_.omaha_request_params_->use_p2p_for_downloading());
+ EXPECT_FALSE(attempter_.omaha_request_params_->use_p2p_for_sharing());
+ g_idle_add(&StaticQuitMainLoop, this);
+}
+
+TEST_F(UpdateAttempterTest, P2PEnabled) {
+ loop_ = g_main_loop_new(g_main_context_default(), FALSE);
+ g_idle_add(&StaticP2PEnabled, this);
+ g_main_loop_run(loop_);
+ g_main_loop_unref(loop_);
+ loop_ = NULL;
+}
+gboolean UpdateAttempterTest::StaticP2PEnabled(gpointer data) {
+ UpdateAttempterTest* ua_test = reinterpret_cast<UpdateAttempterTest*>(data);
+ ua_test->P2PEnabledStart();
+ return FALSE;
+}
+void UpdateAttempterTest::P2PEnabledStart() {
+ MockP2PManager mock_p2p_manager;
+ mock_system_state_.set_p2p_manager(&mock_p2p_manager);
+ // If P2P is enabled and starting it works, check that we performed
+ // housekeeping and that we convey p2p should be used.
+ mock_p2p_manager.fake().SetP2PEnabled(true);
+ mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
+ mock_p2p_manager.fake().SetPerformHousekeepingResult(true);
+ EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(1);
+ attempter_.Update("", "", false, false, false);
+ EXPECT_TRUE(attempter_.omaha_request_params_->use_p2p_for_downloading());
+ EXPECT_TRUE(attempter_.omaha_request_params_->use_p2p_for_sharing());
+ g_idle_add(&StaticQuitMainLoop, this);
+}
+
+TEST_F(UpdateAttempterTest, P2PEnabledInteractive) {
+ loop_ = g_main_loop_new(g_main_context_default(), FALSE);
+ g_idle_add(&StaticP2PEnabledInteractive, this);
+ g_main_loop_run(loop_);
+ g_main_loop_unref(loop_);
+ loop_ = NULL;
+}
+gboolean UpdateAttempterTest::StaticP2PEnabledInteractive(gpointer data) {
+ UpdateAttempterTest* ua_test = reinterpret_cast<UpdateAttempterTest*>(data);
+ ua_test->P2PEnabledInteractiveStart();
+ return FALSE;
+}
+void UpdateAttempterTest::P2PEnabledInteractiveStart() {
+ MockP2PManager mock_p2p_manager;
+ mock_system_state_.set_p2p_manager(&mock_p2p_manager);
+ // For an interactive check, if P2P is enabled and starting it
+ // works, check that we performed housekeeping and that we convey
+ // p2p should be used for sharing but NOT for downloading.
+ mock_p2p_manager.fake().SetP2PEnabled(true);
+ mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
+ mock_p2p_manager.fake().SetPerformHousekeepingResult(true);
+ EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(1);
+ attempter_.Update("", "", false, true /* interactive */, false);
+ EXPECT_FALSE(attempter_.omaha_request_params_->use_p2p_for_downloading());
+ EXPECT_TRUE(attempter_.omaha_request_params_->use_p2p_for_sharing());
+ g_idle_add(&StaticQuitMainLoop, this);
+}
+
TEST_F(UpdateAttempterTest, ReadTargetVersionPrefixFromPolicy) {
loop_ = g_main_loop_new(g_main_context_default(), FALSE);
g_idle_add(&StaticReadTargetVersionPrefixFromPolicyTestStart, this);
diff --git a/utils.cc b/utils.cc
index 8b4bffe..8f04134 100644
--- a/utils.cc
+++ b/utils.cc
@@ -1026,6 +1026,11 @@
return true;
}
+bool IsP2PAllowedForInteractiveChecks() {
+ struct stat statbuf;
+ return stat(kP2PAllowInteractiveMarkerFile, &statbuf) == 0;
+}
+
Time TimeFromStructTimespec(struct timespec *ts) {
int64 us = static_cast<int64>(ts->tv_sec) * Time::kMicrosecondsPerSecond +
static_cast<int64>(ts->tv_nsec) / Time::kNanosecondsPerMicrosecond;
@@ -1056,6 +1061,16 @@
return str;
}
+string CalculateP2PFileId(const string& payload_hash, size_t payload_size) {
+ string encoded_hash;
+ OmahaHashCalculator::Base64Encode(payload_hash.c_str(),
+ payload_hash.size(),
+ &encoded_hash);
+ return StringPrintf("cros_update_size_%zu_hash_%s",
+ payload_size,
+ encoded_hash.c_str());
+}
+
} // namespace utils
} // namespace chromeos_update_engine
diff --git a/utils.h b/utils.h
index 79b6917..5518da6 100644
--- a/utils.h
+++ b/utils.h
@@ -35,6 +35,16 @@
// boot mode. Returns false if the boot mode is developer.
bool IsNormalBootMode();
+// Returns true if and only if P2P is allowed for interactive update
+// checks. This is implemented by checking the existence of a marker
+// file that can be created by the following command
+//
+// # touch /mnt/stateful_partition/p2p-allow-interactive
+//
+// This function returns false if the required marker file does not
+// exist or if an error occurred.
+bool IsP2PAllowedForInteractiveChecks();
+
// Converts a struct timespec representing a number of seconds since
// the Unix epoch to a base::Time. Sub-microsecond time is rounded
// down.
@@ -49,6 +59,10 @@
// Does no escaping, only use this for presentation in error messages.
std::string StringVectorToString(const std::vector<std::string> &vector);
+// Calculates the p2p file id from payload hash and size
+std::string CalculateP2PFileId(const std::string& payload_hash,
+ size_t payload_size);
+
// Returns the HWID or an empty string on error.
std::string GetHardwareClass();