Sync update_engine from chromiumos/platform2.
The update_engine project was synced from chromium gerrit instance with
the latest changes.You can find the old version here:
https://chromium.googlesource.com/chromiumos/platform2/+/830d80acf26c8232d4b6e14916b7dad543bcf5ec/update_engine
BUG=b/23084607
diff --git a/action.h b/action.h
index 53786d1..3cbc26b 100644
--- a/action.h
+++ b/action.h
@@ -30,12 +30,12 @@
// How it works:
//
// First off, there is only one thread and all I/O should be asynchronous.
-// A glib main loop blocks whenever there is no work to be done. This happens
+// A message loop blocks whenever there is no work to be done. This happens
// where there is no CPU work to be done and no I/O ready to transfer in or
-// out. Two kinds of events can wake up the main loop: timer alarm or file
-// descriptors. If either of these happens, glib finds out the owner of what
-// fired and calls the appropriate code to handle it. As such, all the code
-// in the Action* classes and the code that is calls is non-blocking.
+// out. Two kinds of events can wake up the message loop: timer alarm or file
+// descriptors. If either of these happens, the message loop finds out the owner
+// of what fired and calls the appropriate code to handle it. As such, all the
+// code in the Action* classes and the code that is calls is non-blocking.
//
// An ActionProcessor contains a queue of Actions to perform. When
// ActionProcessor::StartProcessing() is called, it executes the first action.
diff --git a/chrome_browser_proxy_resolver.cc b/chrome_browser_proxy_resolver.cc
index 116c67a..932fb9c 100644
--- a/chrome_browser_proxy_resolver.cc
+++ b/chrome_browser_proxy_resolver.cc
@@ -12,11 +12,7 @@
#include <base/bind.h>
#include <base/strings/string_tokenizer.h>
#include <base/strings/string_util.h>
-#include <dbus/dbus-glib-lowlevel.h>
-#include <dbus/dbus-glib.h>
-#include "update_engine/dbus_constants.h"
-#include "update_engine/glib_utils.h"
#include "update_engine/utils.h"
namespace chromeos_update_engine {
@@ -29,22 +25,10 @@
using std::pair;
using std::string;
-#define LIB_CROS_PROXY_RESOLVE_NAME "ProxyResolved"
-#define LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE \
- "org.chromium.UpdateEngineLibcrosProxyResolvedInterface"
const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
-const char kLibCrosServicePath[] = "/org/chromium/LibCrosService";
-const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface";
-const char kLibCrosServiceResolveNetworkProxyMethodName[] =
- "ResolveNetworkProxy";
-const char kLibCrosProxyResolveName[] = LIB_CROS_PROXY_RESOLVE_NAME;
+const char kLibCrosProxyResolveName[] = "ProxyResolved";
const char kLibCrosProxyResolveSignalInterface[] =
- LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE;
-const char kLibCrosProxyResolveSignalFilter[] = "type='signal', "
- "interface='" LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE "', "
- "member='" LIB_CROS_PROXY_RESOLVE_NAME "'";
-#undef LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE
-#undef LIB_CROS_PROXY_RESOLVE_NAME
+ "org.chromium.UpdateEngineLibcrosProxyResolvedInterface";
namespace {
@@ -53,59 +37,21 @@
} // namespace
ChromeBrowserProxyResolver::ChromeBrowserProxyResolver(
- DBusWrapperInterface* dbus)
- : dbus_(dbus), timeout_(kTimeout) {}
+ LibCrosProxy* libcros_proxy)
+ : libcros_proxy_(libcros_proxy), timeout_(kTimeout) {}
bool ChromeBrowserProxyResolver::Init() {
- if (proxy_)
- return true; // Already initialized.
-
- // Set up signal handler. Code lifted from libcros.
- GError* g_error = nullptr;
- DBusGConnection* bus = dbus_->BusGet(DBUS_BUS_SYSTEM, &g_error);
- TEST_AND_RETURN_FALSE(bus);
- DBusConnection* connection = dbus_->ConnectionGetConnection(bus);
- TEST_AND_RETURN_FALSE(connection);
-
- DBusError dbus_error;
- dbus_error_init(&dbus_error);
- dbus_->DBusBusAddMatch(connection, kLibCrosProxyResolveSignalFilter,
- &dbus_error);
- TEST_AND_RETURN_FALSE(!dbus_error_is_set(&dbus_error));
- TEST_AND_RETURN_FALSE(dbus_->DBusConnectionAddFilter(
- connection,
- &ChromeBrowserProxyResolver::StaticFilterMessage,
- this,
- nullptr));
-
- proxy_ = dbus_->ProxyNewForName(bus, kLibCrosServiceName, kLibCrosServicePath,
- kLibCrosServiceInterface);
- if (!proxy_) {
- dbus_->DBusConnectionRemoveFilter(
- connection,
- &ChromeBrowserProxyResolver::StaticFilterMessage,
- this);
- }
- TEST_AND_RETURN_FALSE(proxy_); // For the error log
+ libcros_proxy_->ue_proxy_resolved_interface()
+ ->RegisterProxyResolvedSignalHandler(
+ base::Bind(&ChromeBrowserProxyResolver::OnProxyResolvedSignal,
+ base::Unretained(this)),
+ base::Bind(&ChromeBrowserProxyResolver::OnSignalConnected,
+ base::Unretained(this)));
return true;
}
ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() {
- // Remove DBus connection filters and Kill proxy object.
- if (proxy_) {
- GError* gerror = nullptr;
- DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
- if (gbus) {
- DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
- dbus_->DBusConnectionRemoveFilter(
- connection,
- &ChromeBrowserProxyResolver::StaticFilterMessage,
- this);
- }
- dbus_->ProxyUnref(proxy_);
- }
-
- // Kill outstanding timers
+ // Kill outstanding timers.
for (auto& timer : timers_) {
MessageLoop::current()->CancelTask(timer.second);
timer.second = MessageLoop::kTaskIdNull;
@@ -115,26 +61,14 @@
bool ChromeBrowserProxyResolver::GetProxiesForUrl(const string& url,
ProxiesResolvedFn callback,
void* data) {
- GError* error = nullptr;
- guint timeout = timeout_;
- if (proxy_) {
- if (!dbus_->ProxyCall_3_0(proxy_,
- kLibCrosServiceResolveNetworkProxyMethodName,
- &error,
- url.c_str(),
- kLibCrosProxyResolveSignalInterface,
- kLibCrosProxyResolveName)) {
- if (error) {
- LOG(WARNING) << "dbus_g_proxy_call failed, continuing with no proxy: "
- << utils::GetAndFreeGError(&error);
- } else {
- LOG(WARNING) << "dbus_g_proxy_call failed with no error string, "
- "continuing with no proxy.";
- }
- timeout = 0;
- }
- } else {
- LOG(WARNING) << "dbus proxy object missing, continuing with no proxy.";
+ int timeout = timeout_;
+ chromeos::ErrorPtr error;
+ if (!libcros_proxy_->service_interface_proxy()->ResolveNetworkProxy(
+ url.c_str(),
+ kLibCrosProxyResolveSignalInterface,
+ kLibCrosProxyResolveName,
+ &error)) {
+ LOG(WARNING) << "Can't resolve the proxy. Continuing with no proxy.";
timeout = 0;
}
@@ -149,36 +83,6 @@
return true;
}
-DBusHandlerResult ChromeBrowserProxyResolver::FilterMessage(
- DBusConnection* connection,
- DBusMessage* message) {
- // Code lifted from libcros.
- if (!dbus_->DBusMessageIsSignal(message,
- kLibCrosProxyResolveSignalInterface,
- kLibCrosProxyResolveName)) {
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
- // Get args
- char* source_url = nullptr;
- char* proxy_list = nullptr;
- char* error = nullptr;
- DBusError arg_error;
- dbus_error_init(&arg_error);
- if (!dbus_->DBusMessageGetArgs_3(message, &arg_error,
- &source_url,
- &proxy_list,
- &error)) {
- LOG(ERROR) << "Error reading dbus signal.";
- return DBUS_HANDLER_RESULT_HANDLED;
- }
- if (!source_url || !proxy_list) {
- LOG(ERROR) << "Error getting url, proxy list from dbus signal.";
- return DBUS_HANDLER_RESULT_HANDLED;
- }
- HandleReply(source_url, proxy_list);
- return DBUS_HANDLER_RESULT_HANDLED;
-}
-
bool ChromeBrowserProxyResolver::DeleteUrlState(
const string& source_url,
bool delete_timer,
@@ -202,11 +106,25 @@
return true;
}
-void ChromeBrowserProxyResolver::HandleReply(const string& source_url,
- const string& proxy_list) {
+void ChromeBrowserProxyResolver::OnSignalConnected(const string& interface_name,
+ const string& signal_name,
+ bool successful) {
+ if (!successful) {
+ LOG(ERROR) << "Couldn't connect to the signal " << interface_name << "."
+ << signal_name;
+ }
+}
+
+void ChromeBrowserProxyResolver::OnProxyResolvedSignal(
+ const string& source_url,
+ const string& proxy_info,
+ const string& error_message) {
pair<ProxiesResolvedFn, void*> callback;
TEST_AND_RETURN(DeleteUrlState(source_url, true, &callback));
- (*callback.first)(ParseProxyString(proxy_list), callback.second);
+ if (!error_message.empty()) {
+ LOG(WARNING) << "ProxyResolved error: " << error_message;
+ }
+ (*callback.first)(ParseProxyString(proxy_info), callback.second);
}
void ChromeBrowserProxyResolver::HandleTimeout(string source_url) {
diff --git a/chrome_browser_proxy_resolver.h b/chrome_browser_proxy_resolver.h
index ab0a92c..aef87ee 100644
--- a/chrome_browser_proxy_resolver.h
+++ b/chrome_browser_proxy_resolver.h
@@ -10,44 +10,31 @@
#include <string>
#include <utility>
-#include <dbus/dbus-glib.h>
-#include <dbus/dbus-glib-lowlevel.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
#include <chromeos/message_loops/message_loop.h>
-#include "update_engine/dbus_wrapper_interface.h"
+#include "update_engine/dbus_proxies.h"
+#include "update_engine/libcros_proxy.h"
#include "update_engine/proxy_resolver.h"
namespace chromeos_update_engine {
extern const char kLibCrosServiceName[];
-extern const char kLibCrosServicePath[];
-extern const char kLibCrosServiceInterface[];
-extern const char kLibCrosServiceResolveNetworkProxyMethodName[];
extern const char kLibCrosProxyResolveName[];
extern const char kLibCrosProxyResolveSignalInterface[];
-extern const char kLibCrosProxyResolveSignalFilter[];
class ChromeBrowserProxyResolver : public ProxyResolver {
public:
- explicit ChromeBrowserProxyResolver(DBusWrapperInterface* dbus);
+ explicit ChromeBrowserProxyResolver(LibCrosProxy* libcros_proxy);
~ChromeBrowserProxyResolver() override;
+
+ // Initialize the ProxyResolver using the provided DBus proxies.
bool Init();
bool GetProxiesForUrl(const std::string& url,
ProxiesResolvedFn callback,
void* data) override;
- void set_timeout(int seconds) { timeout_ = seconds; }
-
- // Public for test
- static DBusHandlerResult StaticFilterMessage(
- DBusConnection* connection,
- DBusMessage* message,
- void* data) {
- return reinterpret_cast<ChromeBrowserProxyResolver*>(data)->FilterMessage(
- connection, message);
- }
private:
FRIEND_TEST(ChromeBrowserProxyResolverTest, ParseTest);
@@ -56,12 +43,17 @@
CallbacksMap;
typedef std::multimap<std::string, chromeos::MessageLoop::TaskId> TimeoutsMap;
+ // Called when the signal in UpdateEngineLibcrosProxyResolvedInterface is
+ // connected.
+ void OnSignalConnected(const std::string& interface_name,
+ const std::string& signal_name,
+ bool successful);
+
// Handle a reply from Chrome:
- void HandleReply(const std::string& source_url,
- const std::string& proxy_list);
- DBusHandlerResult FilterMessage(
- DBusConnection* connection,
- DBusMessage* message);
+ void OnProxyResolvedSignal(const std::string& source_url,
+ const std::string& proxy_info,
+ const std::string& error_message);
+
// Handle no reply:
void HandleTimeout(std::string source_url);
@@ -79,9 +71,11 @@
// Shutdown the dbus proxy object.
void Shutdown();
- DBusWrapperInterface* dbus_;
- DBusGProxy* proxy_{nullptr};
- DBusGProxy* peer_proxy_{nullptr};
+ // DBus proxies to request a HTTP proxy resolution. The request is done in the
+ // service_interface_proxy() interface and the response is received as a
+ // signal in the ue_proxy_resolved_interface().
+ LibCrosProxy* libcros_proxy_;
+
int timeout_;
TimeoutsMap timers_;
CallbacksMap callbacks_;
diff --git a/chrome_browser_proxy_resolver_unittest.cc b/chrome_browser_proxy_resolver_unittest.cc
index 3866358..62ee2e0 100644
--- a/chrome_browser_proxy_resolver_unittest.cc
+++ b/chrome_browser_proxy_resolver_unittest.cc
@@ -6,81 +6,171 @@
#include <deque>
#include <string>
+#include <vector>
#include <gtest/gtest.h>
#include <base/bind.h>
+#include <chromeos/make_unique_ptr.h>
#include <chromeos/message_loops/fake_message_loop.h>
-#include "update_engine/mock_dbus_wrapper.h"
+#include "update_engine/dbus_mocks.h"
+#include "update_engine/dbus_test_utils.h"
using ::testing::Return;
-using ::testing::SetArgPointee;
using ::testing::StrEq;
using ::testing::_;
using chromeos::MessageLoop;
+using org::chromium::LibCrosServiceInterfaceProxyMock;
+using org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxyMock;
using std::deque;
using std::string;
+using std::vector;
namespace chromeos_update_engine {
class ChromeBrowserProxyResolverTest : public ::testing::Test {
protected:
+ ChromeBrowserProxyResolverTest()
+ : service_interface_mock_(new LibCrosServiceInterfaceProxyMock()),
+ ue_proxy_resolved_interface_mock_(
+ new UpdateEngineLibcrosProxyResolvedInterfaceProxyMock()),
+ libcros_proxy_(
+ chromeos::make_unique_ptr(service_interface_mock_),
+ chromeos::make_unique_ptr(ue_proxy_resolved_interface_mock_)) {}
+
void SetUp() override {
loop_.SetAsCurrent();
+ // The ProxyResolved signal should be subscribed to.
+ MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER(
+ ue_proxy_resolved_signal_,
+ *ue_proxy_resolved_interface_mock_,
+ ProxyResolved);
+
+ EXPECT_TRUE(resolver_.Init());
+ // Run the loop once to dispatch the successfully registered signal handler.
+ EXPECT_TRUE(loop_.RunOnce(false));
}
void TearDown() override {
EXPECT_FALSE(loop_.PendingTasks());
}
+ // Send the signal to the callback passed during registration of the
+ // ProxyResolved.
+ void SendReplySignal(const string& source_url,
+ const string& proxy_info,
+ const string& error_message);
+
+ void RunTest(bool chrome_replies, bool chrome_alive);
+
private:
chromeos::FakeMessageLoop loop_{nullptr};
+
+ // Local pointers to the mocks. The instances are owned by the
+ // |libcros_proxy_|.
+ LibCrosServiceInterfaceProxyMock* service_interface_mock_;
+ UpdateEngineLibcrosProxyResolvedInterfaceProxyMock*
+ ue_proxy_resolved_interface_mock_;
+
+ // The registered signal handler for the signal
+ // UpdateEngineLibcrosProxyResolvedInterface.ProxyResolved.
+ chromeos_update_engine::dbus_test_utils::MockSignalHandler<
+ void(const string&, const string&, const string&)>
+ ue_proxy_resolved_signal_;
+
+ LibCrosProxy libcros_proxy_;
+ ChromeBrowserProxyResolver resolver_{&libcros_proxy_};
};
+
+void ChromeBrowserProxyResolverTest::SendReplySignal(
+ const string& source_url,
+ const string& proxy_info,
+ const string& error_message) {
+ ASSERT_TRUE(ue_proxy_resolved_signal_.IsHandlerRegistered());
+ ue_proxy_resolved_signal_.signal_callback().Run(
+ source_url, proxy_info, error_message);
+}
+
+namespace {
+void CheckResponseResolved(const deque<string>& proxies,
+ void* /* pirv_data */) {
+ EXPECT_EQ(2, proxies.size());
+ EXPECT_EQ("socks5://192.168.52.83:5555", proxies[0]);
+ EXPECT_EQ(kNoProxy, proxies[1]);
+ MessageLoop::current()->BreakLoop();
+}
+
+void CheckResponseNoReply(const deque<string>& proxies, void* /* pirv_data */) {
+ EXPECT_EQ(1, proxies.size());
+ EXPECT_EQ(kNoProxy, proxies[0]);
+ MessageLoop::current()->BreakLoop();
+}
+} // namespace
+
+// chrome_replies should be set to whether or not we fake a reply from
+// chrome. If there's no reply, the resolver should time out.
+// If chrome_alive is false, assume that sending to chrome fails.
+void ChromeBrowserProxyResolverTest::RunTest(bool chrome_replies,
+ bool chrome_alive) {
+ char kUrl[] = "http://example.com/blah";
+ char kProxyConfig[] = "SOCKS5 192.168.52.83:5555;DIRECT";
+
+ EXPECT_CALL(*service_interface_mock_,
+ ResolveNetworkProxy(StrEq(kUrl),
+ StrEq(kLibCrosProxyResolveSignalInterface),
+ StrEq(kLibCrosProxyResolveName),
+ _,
+ _))
+ .WillOnce(Return(chrome_alive));
+
+ ProxiesResolvedFn get_proxies_response = &CheckResponseNoReply;
+ if (chrome_replies) {
+ get_proxies_response = &CheckResponseResolved;
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&ChromeBrowserProxyResolverTest::SendReplySignal,
+ base::Unretained(this),
+ kUrl,
+ kProxyConfig,
+ ""),
+ base::TimeDelta::FromSeconds(1));
+ }
+
+ EXPECT_TRUE(resolver_.GetProxiesForUrl(kUrl, get_proxies_response, nullptr));
+ MessageLoop::current()->Run();
+}
+
+
TEST_F(ChromeBrowserProxyResolverTest, ParseTest) {
// Test ideas from
// http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list_unittest.cc
- const char* inputs[] = {
- "PROXY foopy:10",
- " DIRECT", // leading space.
- "PROXY foopy1 ; proxy foopy2;\t DIRECT",
- "proxy foopy1 ; SOCKS foopy2",
- "DIRECT ; proxy foopy1 ; DIRECT ; SOCKS5 foopy2;DIRECT ",
- "DIRECT ; proxy foopy1:80; DIRECT ; DIRECT",
- "PROXY-foopy:10",
- "PROXY",
- "PROXY foopy1 ; JUNK ; JUNK ; SOCKS5 foopy2 ; ;",
- "HTTP foopy1; SOCKS5 foopy2"
- };
- deque<string> outputs[arraysize(inputs)];
- outputs[0].push_back("http://foopy:10");
- outputs[0].push_back(kNoProxy);
- outputs[1].push_back(kNoProxy);
- outputs[2].push_back("http://foopy1");
- outputs[2].push_back("http://foopy2");
- outputs[2].push_back(kNoProxy);
- outputs[3].push_back("http://foopy1");
- outputs[3].push_back("socks4://foopy2");
- outputs[3].push_back(kNoProxy);
- outputs[4].push_back(kNoProxy);
- outputs[4].push_back("http://foopy1");
- outputs[4].push_back(kNoProxy);
- outputs[4].push_back("socks5://foopy2");
- outputs[4].push_back(kNoProxy);
- outputs[5].push_back(kNoProxy);
- outputs[5].push_back("http://foopy1:80");
- outputs[5].push_back(kNoProxy);
- outputs[5].push_back(kNoProxy);
- outputs[6].push_back(kNoProxy);
- outputs[7].push_back(kNoProxy);
- outputs[8].push_back("http://foopy1");
- outputs[8].push_back("socks5://foopy2");
- outputs[8].push_back(kNoProxy);
- outputs[9].push_back("socks5://foopy2");
- outputs[9].push_back(kNoProxy);
+ vector<string> inputs = {
+ "PROXY foopy:10",
+ " DIRECT", // leading space.
+ "PROXY foopy1 ; proxy foopy2;\t DIRECT",
+ "proxy foopy1 ; SOCKS foopy2",
+ "DIRECT ; proxy foopy1 ; DIRECT ; SOCKS5 foopy2;DIRECT ",
+ "DIRECT ; proxy foopy1:80; DIRECT ; DIRECT",
+ "PROXY-foopy:10",
+ "PROXY",
+ "PROXY foopy1 ; JUNK ; JUNK ; SOCKS5 foopy2 ; ;",
+ "HTTP foopy1; SOCKS5 foopy2"};
+ vector<deque<string>> outputs = {
+ {"http://foopy:10", kNoProxy},
+ {kNoProxy},
+ {"http://foopy1", "http://foopy2", kNoProxy},
+ {"http://foopy1", "socks4://foopy2", kNoProxy},
+ {kNoProxy, "http://foopy1", kNoProxy, "socks5://foopy2", kNoProxy},
+ {kNoProxy, "http://foopy1:80", kNoProxy, kNoProxy},
+ {kNoProxy},
+ {kNoProxy},
+ {"http://foopy1", "socks5://foopy2", kNoProxy},
+ {"socks5://foopy2", kNoProxy}};
+ ASSERT_EQ(inputs.size(), outputs.size());
- for (size_t i = 0; i < arraysize(inputs); i++) {
+ for (size_t i = 0; i < inputs.size(); i++) {
deque<string> results =
ChromeBrowserProxyResolver::ParseProxyString(inputs[i]);
deque<string>& expected = outputs[i];
@@ -93,110 +183,6 @@
}
}
-namespace {
-void DBusWrapperTestResolved(const deque<string>& proxies,
- void* /* pirv_data */) {
- EXPECT_EQ(2, proxies.size());
- EXPECT_EQ("socks5://192.168.52.83:5555", proxies[0]);
- EXPECT_EQ(kNoProxy, proxies[1]);
- MessageLoop::current()->BreakLoop();
-}
-void DBusWrapperTestResolvedNoReply(const deque<string>& proxies,
- void* /* pirv_data */) {
- EXPECT_EQ(1, proxies.size());
- EXPECT_EQ(kNoProxy, proxies[0]);
- MessageLoop::current()->BreakLoop();
-}
-
-void SendReply(DBusConnection* connection,
- DBusMessage* message,
- ChromeBrowserProxyResolver* resolver) {
- LOG(INFO) << "Calling SendReply";
- ChromeBrowserProxyResolver::StaticFilterMessage(connection,
- message,
- resolver);
-}
-
-// chrome_replies should be set to whether or not we fake a reply from
-// chrome. If there's no reply, the resolver should time out.
-// If chrome_alive is false, assume that sending to chrome fails.
-void RunTest(bool chrome_replies, bool chrome_alive) {
- intptr_t number = 1;
- DBusGConnection* kMockSystemGBus =
- reinterpret_cast<DBusGConnection*>(number++);
- DBusConnection* kMockSystemBus =
- reinterpret_cast<DBusConnection*>(number++);
- DBusGProxy* kMockDbusProxy =
- reinterpret_cast<DBusGProxy*>(number++);
- DBusMessage* kMockDbusMessage =
- reinterpret_cast<DBusMessage*>(number++);
-
- char kUrl[] = "http://example.com/blah";
- char kProxyConfig[] = "SOCKS5 192.168.52.83:5555;DIRECT";
-
- testing::StrictMock<MockDBusWrapper> dbus_iface;
-
- EXPECT_CALL(dbus_iface, BusGet(_, _))
- .Times(2)
- .WillRepeatedly(Return(kMockSystemGBus));
- EXPECT_CALL(dbus_iface,
- ConnectionGetConnection(kMockSystemGBus))
- .Times(2)
- .WillRepeatedly(Return(kMockSystemBus));
- EXPECT_CALL(dbus_iface, DBusBusAddMatch(kMockSystemBus, _, _));
- EXPECT_CALL(dbus_iface,
- DBusConnectionAddFilter(kMockSystemBus, _, _, _))
- .WillOnce(Return(1));
- EXPECT_CALL(dbus_iface,
- ProxyNewForName(kMockSystemGBus,
- StrEq(kLibCrosServiceName),
- StrEq(kLibCrosServicePath),
- StrEq(kLibCrosServiceInterface)))
- .WillOnce(Return(kMockDbusProxy));
- EXPECT_CALL(dbus_iface, ProxyUnref(kMockDbusProxy));
-
- EXPECT_CALL(dbus_iface, ProxyCall_3_0(
- kMockDbusProxy,
- StrEq(kLibCrosServiceResolveNetworkProxyMethodName),
- _,
- StrEq(kUrl),
- StrEq(kLibCrosProxyResolveSignalInterface),
- StrEq(kLibCrosProxyResolveName)))
- .WillOnce(Return(chrome_alive ? TRUE : FALSE));
-
- EXPECT_CALL(dbus_iface,
- DBusConnectionRemoveFilter(kMockSystemBus, _, _));
-
- if (chrome_replies) {
- EXPECT_CALL(dbus_iface,
- DBusMessageIsSignal(kMockDbusMessage,
- kLibCrosProxyResolveSignalInterface,
- kLibCrosProxyResolveName))
- .WillOnce(Return(1));
- EXPECT_CALL(dbus_iface,
- DBusMessageGetArgs_3(kMockDbusMessage, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<2>(static_cast<char*>(kUrl)),
- SetArgPointee<3>(static_cast<char*>(kProxyConfig)),
- Return(TRUE)));
- }
-
- ChromeBrowserProxyResolver resolver(&dbus_iface);
- EXPECT_EQ(true, resolver.Init());
- resolver.set_timeout(1);
- ProxiesResolvedFn get_proxies_response = &DBusWrapperTestResolvedNoReply;
-
- if (chrome_replies) {
- get_proxies_response = &DBusWrapperTestResolved;
- MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&SendReply, kMockSystemBus, kMockDbusMessage, &resolver));
- }
-
- EXPECT_TRUE(resolver.GetProxiesForUrl(kUrl, get_proxies_response, nullptr));
- MessageLoop::current()->Run();
-}
-} // namespace
-
TEST_F(ChromeBrowserProxyResolverTest, SuccessTest) {
RunTest(true, true);
}
diff --git a/connection_manager.cc b/connection_manager.cc
index f6c0217..004eba0 100644
--- a/connection_manager.cc
+++ b/connection_manager.cc
@@ -10,14 +10,14 @@
#include <base/stl_util.h>
#include <base/strings/string_util.h>
#include <chromeos/dbus/service_constants.h>
-#include <dbus/dbus-glib.h>
-#include <glib.h>
#include <policy/device_policy.h>
#include "update_engine/prefs.h"
#include "update_engine/system_state.h"
#include "update_engine/utils.h"
+using org::chromium::flimflam::ManagerProxyInterface;
+using org::chromium::flimflam::ServiceProxyInterface;
using std::set;
using std::string;
@@ -25,171 +25,46 @@
namespace {
-// Gets the DbusGProxy for FlimFlam. Must be free'd with ProxyUnref()
-bool GetFlimFlamProxy(DBusWrapperInterface* dbus_iface,
- const char* path,
- const char* interface,
- DBusGProxy** out_proxy) {
- DBusGConnection* bus;
- DBusGProxy* proxy;
- GError* error = nullptr;
-
- bus = dbus_iface->BusGet(DBUS_BUS_SYSTEM, &error);
- if (!bus) {
- LOG(ERROR) << "Failed to get system bus";
- return false;
+NetworkConnectionType ParseConnectionType(const string& type_str) {
+ if (type_str == shill::kTypeEthernet) {
+ return NetworkConnectionType::kEthernet;
+ } else if (type_str == shill::kTypeWifi) {
+ return NetworkConnectionType::kWifi;
+ } else if (type_str == shill::kTypeWimax) {
+ return NetworkConnectionType::kWimax;
+ } else if (type_str == shill::kTypeBluetooth) {
+ return NetworkConnectionType::kBluetooth;
+ } else if (type_str == shill::kTypeCellular) {
+ return NetworkConnectionType::kCellular;
}
- proxy = dbus_iface->ProxyNewForName(bus, shill::kFlimflamServiceName, path,
- interface);
- *out_proxy = proxy;
- return true;
+ return NetworkConnectionType::kUnknown;
}
-// On success, caller owns the GHashTable at out_hash_table.
-// Returns true on success.
-bool GetProperties(DBusWrapperInterface* dbus_iface,
- const char* path,
- const char* interface,
- GHashTable** out_hash_table) {
- DBusGProxy* proxy;
- GError* error = nullptr;
-
- TEST_AND_RETURN_FALSE(GetFlimFlamProxy(dbus_iface,
- path,
- interface,
- &proxy));
-
- gboolean rc = dbus_iface->ProxyCall_0_1(proxy,
- "GetProperties",
- &error,
- out_hash_table);
- dbus_iface->ProxyUnref(proxy);
- if (rc == FALSE) {
- LOG(ERROR) << "dbus_g_proxy_call failed";
- return false;
- }
-
- return true;
-}
-
-// Returns (via out_path) the default network path, or empty string if
-// there's no network up.
-// Returns true on success.
-bool GetDefaultServicePath(DBusWrapperInterface* dbus_iface, string* out_path) {
- GHashTable* hash_table = nullptr;
-
- TEST_AND_RETURN_FALSE(GetProperties(dbus_iface,
- shill::kFlimflamServicePath,
- shill::kFlimflamManagerInterface,
- &hash_table));
-
- GValue* value = reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table,
- "Services"));
- GPtrArray* array = nullptr;
- bool success = false;
- if (G_VALUE_HOLDS(value, DBUS_TYPE_G_OBJECT_PATH_ARRAY) &&
- (array = reinterpret_cast<GPtrArray*>(g_value_get_boxed(value))) &&
- (array->len > 0)) {
- *out_path = static_cast<const char*>(g_ptr_array_index(array, 0));
- success = true;
- }
-
- g_hash_table_unref(hash_table);
- return success;
-}
-
-NetworkConnectionType ParseConnectionType(const char* type_str) {
- if (!strcmp(type_str, shill::kTypeEthernet)) {
- return kNetEthernet;
- } else if (!strcmp(type_str, shill::kTypeWifi)) {
- return kNetWifi;
- } else if (!strcmp(type_str, shill::kTypeWimax)) {
- return kNetWimax;
- } else if (!strcmp(type_str, shill::kTypeBluetooth)) {
- return kNetBluetooth;
- } else if (!strcmp(type_str, shill::kTypeCellular)) {
- return kNetCellular;
- }
- return kNetUnknown;
-}
-
-NetworkTethering ParseTethering(const char* tethering_str) {
- if (!strcmp(tethering_str, shill::kTetheringNotDetectedState)) {
+NetworkTethering ParseTethering(const string& tethering_str) {
+ if (tethering_str == shill::kTetheringNotDetectedState) {
return NetworkTethering::kNotDetected;
- } else if (!strcmp(tethering_str, shill::kTetheringSuspectedState)) {
+ } else if (tethering_str == shill::kTetheringSuspectedState) {
return NetworkTethering::kSuspected;
- } else if (!strcmp(tethering_str, shill::kTetheringConfirmedState)) {
+ } else if (tethering_str == shill::kTetheringConfirmedState) {
return NetworkTethering::kConfirmed;
}
LOG(WARNING) << "Unknown Tethering value: " << tethering_str;
return NetworkTethering::kUnknown;
}
-bool GetServicePathProperties(DBusWrapperInterface* dbus_iface,
- const string& path,
- NetworkConnectionType* out_type,
- NetworkTethering* out_tethering) {
- GHashTable* hash_table = nullptr;
-
- TEST_AND_RETURN_FALSE(GetProperties(dbus_iface,
- path.c_str(),
- shill::kFlimflamServiceInterface,
- &hash_table));
-
- // Populate the out_tethering.
- GValue* value =
- reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table,
- shill::kTetheringProperty));
- const char* tethering_str = nullptr;
-
- if (value != nullptr)
- tethering_str = g_value_get_string(value);
- if (tethering_str != nullptr) {
- *out_tethering = ParseTethering(tethering_str);
- } else {
- // Set to Unknown if not present.
- *out_tethering = NetworkTethering::kUnknown;
- }
-
- // Populate the out_type property.
- value = reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table,
- shill::kTypeProperty));
- const char* type_str = nullptr;
- bool success = false;
- if (value != nullptr && (type_str = g_value_get_string(value)) != nullptr) {
- success = true;
- if (!strcmp(type_str, shill::kTypeVPN)) {
- value = reinterpret_cast<GValue*>(
- g_hash_table_lookup(hash_table, shill::kPhysicalTechnologyProperty));
- if (value != nullptr &&
- (type_str = g_value_get_string(value)) != nullptr) {
- *out_type = ParseConnectionType(type_str);
- } else {
- LOG(ERROR) << "No PhysicalTechnology property found for a VPN"
- << " connection (service: " << path << "). Returning default"
- << " kNetUnknown value.";
- *out_type = kNetUnknown;
- }
- } else {
- *out_type = ParseConnectionType(type_str);
- }
- }
- g_hash_table_unref(hash_table);
- return success;
-}
-
} // namespace
-ConnectionManager::ConnectionManager(SystemState *system_state)
- : system_state_(system_state) {}
+ConnectionManager::ConnectionManager(ShillProxyInterface* shill_proxy,
+ SystemState* system_state)
+ : shill_proxy_(shill_proxy), system_state_(system_state) {}
bool ConnectionManager::IsUpdateAllowedOver(NetworkConnectionType type,
NetworkTethering tethering) const {
switch (type) {
- case kNetBluetooth:
+ case NetworkConnectionType::kBluetooth:
return false;
- case kNetCellular: {
+ case NetworkConnectionType::kCellular: {
set<string> allowed_types;
const policy::DevicePolicy* device_policy =
system_state_->device_policy();
@@ -246,52 +121,108 @@
// Treat this connection as if it is a cellular connection.
LOG(INFO) << "Current connection is confirmed tethered, using Cellular "
"setting.";
- return IsUpdateAllowedOver(kNetCellular, NetworkTethering::kUnknown);
+ return IsUpdateAllowedOver(NetworkConnectionType::kCellular,
+ NetworkTethering::kUnknown);
}
return true;
}
}
+// static
const char* ConnectionManager::StringForConnectionType(
- NetworkConnectionType type) const {
- static const char* const kValues[] = {shill::kTypeEthernet,
- shill::kTypeWifi,
- shill::kTypeWimax,
- shill::kTypeBluetooth,
- shill::kTypeCellular};
- if (type < 0 || type >= static_cast<int>(arraysize(kValues))) {
- return "Unknown";
- }
- return kValues[type];
-}
-
-const char* ConnectionManager::StringForTethering(
- NetworkTethering tethering) const {
- switch (tethering) {
- case NetworkTethering::kNotDetected:
- return shill::kTetheringNotDetectedState;
- case NetworkTethering::kSuspected:
- return shill::kTetheringSuspectedState;
- case NetworkTethering::kConfirmed:
- return shill::kTetheringConfirmedState;
- case NetworkTethering::kUnknown:
+ NetworkConnectionType type) {
+ switch (type) {
+ case NetworkConnectionType::kEthernet:
+ return shill::kTypeEthernet;
+ case NetworkConnectionType::kWifi:
+ return shill::kTypeWifi;
+ case NetworkConnectionType::kWimax:
+ return shill::kTypeWimax;
+ case NetworkConnectionType::kBluetooth:
+ return shill::kTypeBluetooth;
+ case NetworkConnectionType::kCellular:
+ return shill::kTypeCellular;
+ case NetworkConnectionType::kUnknown:
return "Unknown";
}
- // The program shouldn't reach this point, but the compiler isn't smart
- // enough to infer that.
return "Unknown";
}
bool ConnectionManager::GetConnectionProperties(
- DBusWrapperInterface* dbus_iface,
NetworkConnectionType* out_type,
- NetworkTethering* out_tethering) const {
+ NetworkTethering* out_tethering) {
string default_service_path;
- TEST_AND_RETURN_FALSE(GetDefaultServicePath(dbus_iface,
- &default_service_path));
- TEST_AND_RETURN_FALSE(GetServicePathProperties(dbus_iface,
- default_service_path,
- out_type, out_tethering));
+ TEST_AND_RETURN_FALSE(GetDefaultServicePath(&default_service_path));
+ if (default_service_path.empty())
+ return false;
+ TEST_AND_RETURN_FALSE(
+ GetServicePathProperties(default_service_path, out_type, out_tethering));
+ return true;
+}
+
+bool ConnectionManager::GetDefaultServicePath(string* out_path) {
+ chromeos::VariantDictionary properties;
+ chromeos::ErrorPtr error;
+ ManagerProxyInterface* manager_proxy = shill_proxy_->GetManagerProxy();
+ if (!manager_proxy)
+ return false;
+ TEST_AND_RETURN_FALSE(manager_proxy->GetProperties(&properties, &error));
+
+ const auto& prop_default_service =
+ properties.find(shill::kDefaultServiceProperty);
+ if (prop_default_service == properties.end())
+ return false;
+
+ *out_path = prop_default_service->second.TryGet<dbus::ObjectPath>().value();
+ return !out_path->empty();
+}
+
+bool ConnectionManager::GetServicePathProperties(
+ const string& path,
+ NetworkConnectionType* out_type,
+ NetworkTethering* out_tethering) {
+ // We create and dispose the ServiceProxyInterface on every request.
+ std::unique_ptr<ServiceProxyInterface> service =
+ shill_proxy_->GetServiceForPath(path);
+
+ chromeos::VariantDictionary properties;
+ chromeos::ErrorPtr error;
+ TEST_AND_RETURN_FALSE(service->GetProperties(&properties, &error));
+
+ // Populate the out_tethering.
+ const auto& prop_tethering = properties.find(shill::kTetheringProperty);
+ if (prop_tethering == properties.end()) {
+ // Set to Unknown if not present.
+ *out_tethering = NetworkTethering::kUnknown;
+ } else {
+ // If the property doesn't contain a string value, the empty string will
+ // become kUnknown.
+ *out_tethering = ParseTethering(prop_tethering->second.TryGet<string>());
+ }
+
+ // Populate the out_type property.
+ const auto& prop_type = properties.find(shill::kTypeProperty);
+ if (prop_type == properties.end()) {
+ // Set to Unknown if not present.
+ *out_type = NetworkConnectionType::kUnknown;
+ return false;
+ }
+
+ string type_str = prop_type->second.TryGet<string>();
+ if (type_str == shill::kTypeVPN) {
+ const auto& prop_physical =
+ properties.find(shill::kPhysicalTechnologyProperty);
+ if (prop_physical == properties.end()) {
+ LOG(ERROR) << "No PhysicalTechnology property found for a VPN"
+ << " connection (service: " << path << "). Returning default"
+ << " kUnknown value.";
+ *out_type = NetworkConnectionType::kUnknown;
+ } else {
+ *out_type = ParseConnectionType(prop_physical->second.TryGet<string>());
+ }
+ } else {
+ *out_type = ParseConnectionType(type_str);
+ }
return true;
}
diff --git a/connection_manager.h b/connection_manager.h
index f9973a3..870b088 100644
--- a/connection_manager.h
+++ b/connection_manager.h
@@ -5,64 +5,52 @@
#ifndef UPDATE_ENGINE_CONNECTION_MANAGER_H_
#define UPDATE_ENGINE_CONNECTION_MANAGER_H_
+#include <string>
+
#include <base/macros.h>
-#include "update_engine/dbus_wrapper_interface.h"
+#include "update_engine/connection_manager_interface.h"
+#include "update_engine/dbus_proxies.h"
+#include "update_engine/shill_proxy_interface.h"
namespace chromeos_update_engine {
-enum NetworkConnectionType {
- kNetEthernet = 0,
- kNetWifi,
- kNetWimax,
- kNetBluetooth,
- kNetCellular,
- kNetUnknown
-};
-
-enum class NetworkTethering {
- kNotDetected = 0,
- kSuspected,
- kConfirmed,
- kUnknown
-};
-
class SystemState;
-// This class exposes a generic interface to the connection manager
-// (e.g FlimFlam, Shill, etc.) to consolidate all connection-related
-// logic in update_engine.
-class ConnectionManager {
+// This class implements the concrete class that talks with the connection
+// manager (shill) over DBus.
+// TODO(deymo): Remove this class and use ShillProvider from the UpdateManager.
+class ConnectionManager : public ConnectionManagerInterface {
public:
- // Constructs a new ConnectionManager object initialized with the
- // given system state.
- explicit ConnectionManager(SystemState* system_state);
- virtual ~ConnectionManager() = default;
-
- // Populates |out_type| with the type of the network connection
- // that we are currently connected and |out_tethering| with the estimate of
- // whether that network is being tethered. The dbus_iface is used to query
- // the real connection manager (e.g shill).
- virtual bool GetConnectionProperties(DBusWrapperInterface* dbus_iface,
- NetworkConnectionType* out_type,
- NetworkTethering* out_tethering) const;
-
- // Returns true if we're allowed to update the system when we're
- // connected to the internet through the given network connection type and the
- // given tethering state.
- virtual bool IsUpdateAllowedOver(NetworkConnectionType type,
- NetworkTethering tethering) const;
-
// Returns the string representation corresponding to the given
// connection type.
- virtual const char* StringForConnectionType(NetworkConnectionType type) const;
+ static const char* StringForConnectionType(NetworkConnectionType type);
- // Returns the string representation corresponding to the given tethering
- // state.
- virtual const char* StringForTethering(NetworkTethering tethering) const;
+ // Constructs a new ConnectionManager object initialized with the
+ // given system state.
+ ConnectionManager(ShillProxyInterface* shill_proxy,
+ SystemState* system_state);
+ ~ConnectionManager() override = default;
+
+ // ConnectionManagerInterface overrides.
+ bool GetConnectionProperties(NetworkConnectionType* out_type,
+ NetworkTethering* out_tethering) override;
+ bool IsUpdateAllowedOver(NetworkConnectionType type,
+ NetworkTethering tethering) const override;
private:
- // The global context for update_engine
+ // Returns (via out_path) the default network path, or empty string if
+ // there's no network up. Returns true on success.
+ bool GetDefaultServicePath(std::string* out_path);
+
+ bool GetServicePathProperties(const std::string& path,
+ NetworkConnectionType* out_type,
+ NetworkTethering* out_tethering);
+
+ // The mockable interface to access the shill DBus proxies.
+ ShillProxyInterface* shill_proxy_;
+
+ // The global context for update_engine.
SystemState* system_state_;
DISALLOW_COPY_AND_ASSIGN(ConnectionManager);
diff --git a/connection_manager_interface.h b/connection_manager_interface.h
new file mode 100644
index 0000000..642f60f
--- /dev/null
+++ b/connection_manager_interface.h
@@ -0,0 +1,56 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UPDATE_ENGINE_CONNECTION_MANAGER_INTERFACE_H_
+#define UPDATE_ENGINE_CONNECTION_MANAGER_INTERFACE_H_
+
+#include <base/macros.h>
+
+namespace chromeos_update_engine {
+
+enum class NetworkConnectionType {
+ kEthernet,
+ kWifi,
+ kWimax,
+ kBluetooth,
+ kCellular,
+ kUnknown
+};
+
+enum class NetworkTethering {
+ kNotDetected,
+ kSuspected,
+ kConfirmed,
+ kUnknown
+};
+
+// This class exposes a generic interface to the connection manager
+// (e.g FlimFlam, Shill, etc.) to consolidate all connection-related
+// logic in update_engine.
+class ConnectionManagerInterface {
+ public:
+ virtual ~ConnectionManagerInterface() = default;
+
+ // Populates |out_type| with the type of the network connection
+ // that we are currently connected and |out_tethering| with the estimate of
+ // whether that network is being tethered.
+ virtual bool GetConnectionProperties(NetworkConnectionType* out_type,
+ NetworkTethering* out_tethering) = 0;
+
+ // Returns true if we're allowed to update the system when we're
+ // connected to the internet through the given network connection type and the
+ // given tethering state.
+ virtual bool IsUpdateAllowedOver(NetworkConnectionType type,
+ NetworkTethering tethering) const = 0;
+
+ protected:
+ ConnectionManagerInterface() = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ConnectionManagerInterface);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_CONNECTION_MANAGER_INTERFACE_H_
diff --git a/connection_manager_unittest.cc b/connection_manager_unittest.cc
index 8ca1a00..1867108 100644
--- a/connection_manager_unittest.cc
+++ b/connection_manager_unittest.cc
@@ -8,45 +8,51 @@
#include <string>
#include <base/logging.h>
+#include <chromeos/any.h>
#include <chromeos/dbus/service_constants.h>
+#include <chromeos/make_unique_ptr.h>
+#include <chromeos/message_loops/fake_message_loop.h>
+#include <chromeos/variant_dictionary.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include "update_engine/fake_shill_proxy.h"
#include "update_engine/fake_system_state.h"
-#include "update_engine/mock_dbus_wrapper.h"
#include "update_engine/test_utils.h"
+using org::chromium::flimflam::ManagerProxyMock;
+using org::chromium::flimflam::ServiceProxyMock;
using std::set;
using std::string;
-using testing::A;
-using testing::AnyNumber;
using testing::Return;
-using testing::SetArgumentPointee;
-using testing::StrEq;
+using testing::SetArgPointee;
using testing::_;
namespace chromeos_update_engine {
class ConnectionManagerTest : public ::testing::Test {
public:
- ConnectionManagerTest()
- : kMockFlimFlamManagerProxy_(nullptr),
- kMockFlimFlamServiceProxy_(nullptr),
- kServicePath_(nullptr),
- cmut_(&fake_system_state_) {
+ void SetUp() override {
+ loop_.SetAsCurrent();
fake_system_state_.set_connection_manager(&cmut_);
}
- protected:
- void SetupMocks(const char* service_path);
- void SetManagerReply(const char* reply_value, const GType& reply_type);
+ void TearDown() override { EXPECT_FALSE(loop_.PendingTasks()); }
- // Sets the |service_type| Type and the |physical_technology|
- // PhysicalTechnology properties in the mocked service. If a null
- // |physical_technology| is passed, the property is not set (not present).
- void SetServiceReply(const char* service_type,
+ protected:
+ // Sets the default_service object path in the response from the
+ // ManagerProxyMock instance.
+ void SetManagerReply(const char* default_service, bool reply_succeeds);
+
+ // Sets the |service_type|, |physical_technology| and |service_tethering|
+ // properties in the mocked service |service_path|. If any of the three
+ // const char* is a nullptr, the corresponding property will not be included
+ // in the response.
+ void SetServiceReply(const string& service_path,
+ const char* service_type,
const char* physical_technology,
const char* service_tethering);
+
void TestWithServiceType(
const char* service_type,
const char* physical_technology,
@@ -55,171 +61,119 @@
const char* service_tethering,
NetworkTethering expected_tethering);
- static const char* kGetPropertiesMethod;
- DBusGProxy* kMockFlimFlamManagerProxy_;
- DBusGProxy* kMockFlimFlamServiceProxy_;
- DBusGConnection* kMockSystemBus_;
- const char* kServicePath_;
- testing::StrictMock<MockDBusWrapper> dbus_iface_;
- ConnectionManager cmut_; // ConnectionManager under test.
+ chromeos::FakeMessageLoop loop_{nullptr};
FakeSystemState fake_system_state_;
+ FakeShillProxy fake_shill_proxy_;
+
+ // ConnectionManager under test.
+ ConnectionManager cmut_{&fake_shill_proxy_, &fake_system_state_};
};
-// static
-const char* ConnectionManagerTest::kGetPropertiesMethod = "GetProperties";
+void ConnectionManagerTest::SetManagerReply(const char* default_service,
+ bool reply_succeeds) {
+ ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_.GetManagerProxy();
+ if (!reply_succeeds) {
+ EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _))
+ .WillOnce(Return(false));
+ return;
+ }
-void ConnectionManagerTest::SetupMocks(const char* service_path) {
- int number = 1;
- kMockSystemBus_ = reinterpret_cast<DBusGConnection*>(number++);
- kMockFlimFlamManagerProxy_ = reinterpret_cast<DBusGProxy*>(number++);
- kMockFlimFlamServiceProxy_ = reinterpret_cast<DBusGProxy*>(number++);
- ASSERT_NE(kMockSystemBus_, static_cast<DBusGConnection*>(nullptr));
+ // Create a dictionary of properties and optionally include the default
+ // service.
+ chromeos::VariantDictionary reply_dict;
+ reply_dict["SomeOtherProperty"] = 0xC0FFEE;
- kServicePath_ = service_path;
-
- ON_CALL(dbus_iface_, BusGet(DBUS_BUS_SYSTEM, _))
- .WillByDefault(Return(kMockSystemBus_));
- EXPECT_CALL(dbus_iface_, BusGet(DBUS_BUS_SYSTEM, _))
- .Times(AnyNumber());
+ if (default_service) {
+ reply_dict[shill::kDefaultServiceProperty] =
+ dbus::ObjectPath(default_service);
+ }
+ EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _))
+ .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true)));
}
-void ConnectionManagerTest::SetManagerReply(const char *reply_value,
- const GType& reply_type) {
- ASSERT_TRUE(dbus_g_type_is_collection(reply_type));
-
- // Create the GPtrArray array holding the |reply_value| pointer. The
- // |reply_value| string is duplicated because it should be mutable on the
- // interface and is because dbus-glib collections will g_free() each element
- // of the GPtrArray automatically when the |array_as_value| GValue is unset.
- // The g_strdup() is not being leaked.
- GPtrArray* array = g_ptr_array_new();
- ASSERT_NE(nullptr, array);
- g_ptr_array_add(array, g_strdup(reply_value));
-
- GValue* array_as_value = g_new0(GValue, 1);
- EXPECT_EQ(array_as_value, g_value_init(array_as_value, reply_type));
- g_value_take_boxed(array_as_value, array);
-
- // Initialize return value for D-Bus call to Manager object, which is a
- // hash table of static strings (char*) in GValue* containing a single array.
- GHashTable* manager_hash_table = g_hash_table_new_full(
- g_str_hash, g_str_equal,
- nullptr, // no key_destroy_func because keys are static.
- test_utils::GValueFree); // value_destroy_func
- g_hash_table_insert(manager_hash_table,
- const_cast<char*>("Services"),
- array_as_value);
-
- // Plumb return value into mock object.
- EXPECT_CALL(dbus_iface_, ProxyCall_0_1(kMockFlimFlamManagerProxy_,
- StrEq(kGetPropertiesMethod),
- _, A<GHashTable**>()))
- .WillOnce(DoAll(SetArgumentPointee<3>(manager_hash_table), Return(TRUE)));
-
- // Set other expectations.
- EXPECT_CALL(dbus_iface_,
- ProxyNewForName(kMockSystemBus_,
- StrEq(shill::kFlimflamServiceName),
- StrEq(shill::kFlimflamServicePath),
- StrEq(shill::kFlimflamManagerInterface)))
- .WillOnce(Return(kMockFlimFlamManagerProxy_));
- EXPECT_CALL(dbus_iface_, ProxyUnref(kMockFlimFlamManagerProxy_));
- EXPECT_CALL(dbus_iface_, BusGet(DBUS_BUS_SYSTEM, _))
- .RetiresOnSaturation();
-}
-
-void ConnectionManagerTest::SetServiceReply(const char* service_type,
+void ConnectionManagerTest::SetServiceReply(const string& service_path,
+ const char* service_type,
const char* physical_technology,
const char* service_tethering) {
- // Initialize return value for D-Bus call to Service object, which is a
- // hash table of static strings (char*) in GValue*.
- GHashTable* service_hash_table = g_hash_table_new_full(
- g_str_hash, g_str_equal,
- nullptr, // no key_destroy_func because keys are static.
- test_utils::GValueFree); // value_destroy_func
- GValue* service_type_value = test_utils::GValueNewString(service_type);
- g_hash_table_insert(service_hash_table,
- const_cast<char*>("Type"),
- service_type_value);
+ chromeos::VariantDictionary reply_dict;
+ reply_dict["SomeOtherProperty"] = 0xC0FFEE;
+
+ if (service_type)
+ reply_dict[shill::kTypeProperty] = string(service_type);
if (physical_technology) {
- GValue* physical_technology_value =
- test_utils::GValueNewString(physical_technology);
- g_hash_table_insert(service_hash_table,
- const_cast<char*>("PhysicalTechnology"),
- physical_technology_value);
+ reply_dict[shill::kPhysicalTechnologyProperty] =
+ string(physical_technology);
}
- if (service_tethering) {
- GValue* service_tethering_value =
- test_utils::GValueNewString(service_tethering);
- g_hash_table_insert(service_hash_table,
- const_cast<char*>("Tethering"),
- service_tethering_value);
- }
+ if (service_tethering)
+ reply_dict[shill::kTetheringProperty] = string(service_tethering);
+
+ std::unique_ptr<ServiceProxyMock> service_proxy_mock(new ServiceProxyMock());
// Plumb return value into mock object.
- EXPECT_CALL(dbus_iface_, ProxyCall_0_1(kMockFlimFlamServiceProxy_,
- StrEq(kGetPropertiesMethod),
- _, A<GHashTable**>()))
- .WillOnce(DoAll(SetArgumentPointee<3>(service_hash_table), Return(TRUE)));
+ EXPECT_CALL(*service_proxy_mock.get(), GetProperties(_, _, _))
+ .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true)));
- // Set other expectations.
- EXPECT_CALL(dbus_iface_,
- ProxyNewForName(kMockSystemBus_,
- StrEq(shill::kFlimflamServiceName),
- StrEq(kServicePath_),
- StrEq(shill::kFlimflamServiceInterface)))
- .WillOnce(Return(kMockFlimFlamServiceProxy_));
- EXPECT_CALL(dbus_iface_, ProxyUnref(kMockFlimFlamServiceProxy_));
- EXPECT_CALL(dbus_iface_, BusGet(DBUS_BUS_SYSTEM, _))
- .RetiresOnSaturation();
+ fake_shill_proxy_.SetServiceForPath(service_path,
+ std::move(service_proxy_mock));
}
void ConnectionManagerTest::TestWithServiceType(
const char* service_type,
const char* physical_technology,
NetworkConnectionType expected_type) {
-
- SetupMocks("/service/guest-network");
- SetManagerReply(kServicePath_, DBUS_TYPE_G_OBJECT_PATH_ARRAY);
- SetServiceReply(service_type, physical_technology,
+ SetManagerReply("/service/guest-network", true);
+ SetServiceReply("/service/guest-network",
+ service_type,
+ physical_technology,
shill::kTetheringNotDetectedState);
NetworkConnectionType type;
NetworkTethering tethering;
- EXPECT_TRUE(cmut_.GetConnectionProperties(&dbus_iface_, &type, &tethering));
+ EXPECT_TRUE(cmut_.GetConnectionProperties(&type, &tethering));
EXPECT_EQ(expected_type, type);
- testing::Mock::VerifyAndClearExpectations(&dbus_iface_);
+ testing::Mock::VerifyAndClearExpectations(
+ fake_shill_proxy_.GetManagerProxy());
}
void ConnectionManagerTest::TestWithServiceTethering(
const char* service_tethering,
NetworkTethering expected_tethering) {
-
- SetupMocks("/service/guest-network");
- SetManagerReply(kServicePath_, DBUS_TYPE_G_OBJECT_PATH_ARRAY);
- SetServiceReply(shill::kTypeWifi, nullptr, service_tethering);
+ SetManagerReply("/service/guest-network", true);
+ SetServiceReply(
+ "/service/guest-network", shill::kTypeWifi, nullptr, service_tethering);
NetworkConnectionType type;
NetworkTethering tethering;
- EXPECT_TRUE(cmut_.GetConnectionProperties(&dbus_iface_, &type, &tethering));
+ EXPECT_TRUE(cmut_.GetConnectionProperties(&type, &tethering));
EXPECT_EQ(expected_tethering, tethering);
+ testing::Mock::VerifyAndClearExpectations(
+ fake_shill_proxy_.GetManagerProxy());
}
TEST_F(ConnectionManagerTest, SimpleTest) {
- TestWithServiceType(shill::kTypeEthernet, nullptr, kNetEthernet);
- TestWithServiceType(shill::kTypeWifi, nullptr, kNetWifi);
- TestWithServiceType(shill::kTypeWimax, nullptr, kNetWimax);
- TestWithServiceType(shill::kTypeBluetooth, nullptr, kNetBluetooth);
- TestWithServiceType(shill::kTypeCellular, nullptr, kNetCellular);
+ TestWithServiceType(shill::kTypeEthernet, nullptr,
+ NetworkConnectionType::kEthernet);
+ TestWithServiceType(shill::kTypeWifi, nullptr,
+ NetworkConnectionType::kWifi);
+ TestWithServiceType(shill::kTypeWimax, nullptr,
+ NetworkConnectionType::kWimax);
+ TestWithServiceType(shill::kTypeBluetooth, nullptr,
+ NetworkConnectionType::kBluetooth);
+ TestWithServiceType(shill::kTypeCellular, nullptr,
+ NetworkConnectionType::kCellular);
}
TEST_F(ConnectionManagerTest, PhysicalTechnologyTest) {
- TestWithServiceType(shill::kTypeVPN, nullptr, kNetUnknown);
- TestWithServiceType(shill::kTypeVPN, shill::kTypeVPN, kNetUnknown);
- TestWithServiceType(shill::kTypeVPN, shill::kTypeWifi, kNetWifi);
- TestWithServiceType(shill::kTypeVPN, shill::kTypeWimax, kNetWimax);
+ TestWithServiceType(shill::kTypeVPN, nullptr,
+ NetworkConnectionType::kUnknown);
+ TestWithServiceType(shill::kTypeVPN, shill::kTypeVPN,
+ NetworkConnectionType::kUnknown);
+ TestWithServiceType(shill::kTypeVPN, shill::kTypeWifi,
+ NetworkConnectionType::kWifi);
+ TestWithServiceType(shill::kTypeVPN, shill::kTypeWimax,
+ NetworkConnectionType::kWimax);
}
TEST_F(ConnectionManagerTest, TetheringTest) {
@@ -234,26 +188,27 @@
}
TEST_F(ConnectionManagerTest, UnknownTest) {
- TestWithServiceType("foo", nullptr, kNetUnknown);
+ TestWithServiceType("foo", nullptr, NetworkConnectionType::kUnknown);
}
TEST_F(ConnectionManagerTest, AllowUpdatesOverEthernetTest) {
// Updates over Ethernet are allowed even if there's no policy.
- EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetEthernet,
+ EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet,
NetworkTethering::kUnknown));
}
TEST_F(ConnectionManagerTest, AllowUpdatesOverWifiTest) {
- EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetWifi, NetworkTethering::kUnknown));
+ EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi,
+ NetworkTethering::kUnknown));
}
TEST_F(ConnectionManagerTest, AllowUpdatesOverWimaxTest) {
- EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetWimax,
+ EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWimax,
NetworkTethering::kUnknown));
}
TEST_F(ConnectionManagerTest, BlockUpdatesOverBluetoothTest) {
- EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetBluetooth,
+ EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kBluetooth,
NetworkTethering::kUnknown));
}
@@ -264,13 +219,14 @@
// This test tests cellular (3G) being the only connection type being allowed.
set<string> allowed_set;
- allowed_set.insert(cmut_.StringForConnectionType(kNetCellular));
+ allowed_set.insert(
+ cmut_.StringForConnectionType(NetworkConnectionType::kCellular));
EXPECT_CALL(allow_3g_policy, GetAllowedConnectionTypesForUpdate(_))
.Times(1)
- .WillOnce(DoAll(SetArgumentPointee<0>(allowed_set), Return(true)));
+ .WillOnce(DoAll(SetArgPointee<0>(allowed_set), Return(true)));
- EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetCellular,
+ EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
NetworkTethering::kUnknown));
}
@@ -283,43 +239,47 @@
// 3G one among them. Only Cellular is currently enforced by the policy
// setting, the others are ignored (see Bluetooth for example).
set<string> allowed_set;
- allowed_set.insert(cmut_.StringForConnectionType(kNetCellular));
- allowed_set.insert(cmut_.StringForConnectionType(kNetBluetooth));
+ allowed_set.insert(
+ cmut_.StringForConnectionType(NetworkConnectionType::kCellular));
+ allowed_set.insert(
+ cmut_.StringForConnectionType(NetworkConnectionType::kBluetooth));
EXPECT_CALL(allow_3g_policy, GetAllowedConnectionTypesForUpdate(_))
.Times(3)
- .WillRepeatedly(DoAll(SetArgumentPointee<0>(allowed_set), Return(true)));
+ .WillRepeatedly(DoAll(SetArgPointee<0>(allowed_set), Return(true)));
- EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetEthernet,
+ EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet,
NetworkTethering::kUnknown));
- EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetEthernet,
+ EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet,
NetworkTethering::kNotDetected));
- EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetCellular,
+ EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
NetworkTethering::kUnknown));
- EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetWifi, NetworkTethering::kUnknown));
- EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetWimax, NetworkTethering::kUnknown));
- EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetBluetooth,
+ EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi,
+ NetworkTethering::kUnknown));
+ EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWimax,
+ NetworkTethering::kUnknown));
+ EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kBluetooth,
NetworkTethering::kUnknown));
// Tethered networks are treated in the same way as Cellular networks and
// thus allowed.
- EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetEthernet,
+ EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet,
NetworkTethering::kConfirmed));
- EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetWifi,
+ EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi,
NetworkTethering::kConfirmed));
}
TEST_F(ConnectionManagerTest, BlockUpdatesOverCellularByDefaultTest) {
- EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetCellular,
+ EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
NetworkTethering::kUnknown));
}
TEST_F(ConnectionManagerTest, BlockUpdatesOverTetheredNetworkByDefaultTest) {
- EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetWifi,
+ EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi,
NetworkTethering::kConfirmed));
- EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetEthernet,
+ EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet,
NetworkTethering::kConfirmed));
- EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetWifi,
+ EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi,
NetworkTethering::kSuspected));
}
@@ -331,15 +291,18 @@
// Test that updates for 3G are blocked while updates are allowed
// over several other types.
set<string> allowed_set;
- allowed_set.insert(cmut_.StringForConnectionType(kNetEthernet));
- allowed_set.insert(cmut_.StringForConnectionType(kNetWifi));
- allowed_set.insert(cmut_.StringForConnectionType(kNetWimax));
+ allowed_set.insert(
+ cmut_.StringForConnectionType(NetworkConnectionType::kEthernet));
+ allowed_set.insert(
+ cmut_.StringForConnectionType(NetworkConnectionType::kWifi));
+ allowed_set.insert(
+ cmut_.StringForConnectionType(NetworkConnectionType::kWimax));
EXPECT_CALL(block_3g_policy, GetAllowedConnectionTypesForUpdate(_))
.Times(1)
- .WillOnce(DoAll(SetArgumentPointee<0>(allowed_set), Return(true)));
+ .WillOnce(DoAll(SetArgPointee<0>(allowed_set), Return(true)));
- EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetCellular,
+ EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
NetworkTethering::kUnknown));
}
@@ -349,16 +312,17 @@
fake_system_state_.set_device_policy(&allow_3g_policy);
set<string> allowed_set;
- allowed_set.insert(cmut_.StringForConnectionType(kNetCellular));
+ allowed_set.insert(
+ cmut_.StringForConnectionType(NetworkConnectionType::kCellular));
// Return false for GetAllowedConnectionTypesForUpdate and see
// that updates are still blocked for 3G despite the value being in
// the string set above.
EXPECT_CALL(allow_3g_policy, GetAllowedConnectionTypesForUpdate(_))
.Times(1)
- .WillOnce(DoAll(SetArgumentPointee<0>(allowed_set), Return(false)));
+ .WillOnce(DoAll(SetArgPointee<0>(allowed_set), Return(false)));
- EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetCellular,
+ EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
NetworkTethering::kUnknown));
}
@@ -377,7 +341,7 @@
EXPECT_CALL(*prefs, Exists(kPrefsUpdateOverCellularPermission))
.Times(1)
.WillOnce(Return(false));
- EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetCellular,
+ EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
NetworkTethering::kUnknown));
// Allow per user pref.
@@ -386,8 +350,8 @@
.WillOnce(Return(true));
EXPECT_CALL(*prefs, GetBoolean(kPrefsUpdateOverCellularPermission, _))
.Times(1)
- .WillOnce(DoAll(SetArgumentPointee<1>(true), Return(true)));
- EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetCellular,
+ .WillOnce(DoAll(SetArgPointee<1>(true), Return(true)));
+ EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
NetworkTethering::kUnknown));
// Block per user pref.
@@ -396,36 +360,36 @@
.WillOnce(Return(true));
EXPECT_CALL(*prefs, GetBoolean(kPrefsUpdateOverCellularPermission, _))
.Times(1)
- .WillOnce(DoAll(SetArgumentPointee<1>(false), Return(true)));
- EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetCellular,
+ .WillOnce(DoAll(SetArgPointee<1>(false), Return(true)));
+ EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
NetworkTethering::kUnknown));
}
TEST_F(ConnectionManagerTest, StringForConnectionTypeTest) {
EXPECT_STREQ(shill::kTypeEthernet,
- cmut_.StringForConnectionType(kNetEthernet));
+ cmut_.StringForConnectionType(NetworkConnectionType::kEthernet));
EXPECT_STREQ(shill::kTypeWifi,
- cmut_.StringForConnectionType(kNetWifi));
+ cmut_.StringForConnectionType(NetworkConnectionType::kWifi));
EXPECT_STREQ(shill::kTypeWimax,
- cmut_.StringForConnectionType(kNetWimax));
+ cmut_.StringForConnectionType(NetworkConnectionType::kWimax));
EXPECT_STREQ(shill::kTypeBluetooth,
- cmut_.StringForConnectionType(kNetBluetooth));
+ cmut_.StringForConnectionType(
+ NetworkConnectionType::kBluetooth));
EXPECT_STREQ(shill::kTypeCellular,
- cmut_.StringForConnectionType(kNetCellular));
+ cmut_.StringForConnectionType(NetworkConnectionType::kCellular));
EXPECT_STREQ("Unknown",
- cmut_.StringForConnectionType(kNetUnknown));
+ cmut_.StringForConnectionType(NetworkConnectionType::kUnknown));
EXPECT_STREQ("Unknown",
cmut_.StringForConnectionType(
static_cast<NetworkConnectionType>(999999)));
}
TEST_F(ConnectionManagerTest, MalformedServiceList) {
- SetupMocks("/service/guest-network");
- SetManagerReply(kServicePath_, DBUS_TYPE_G_STRING_ARRAY);
+ SetManagerReply("/service/guest-network", false);
NetworkConnectionType type;
NetworkTethering tethering;
- EXPECT_FALSE(cmut_.GetConnectionProperties(&dbus_iface_, &type, &tethering));
+ EXPECT_FALSE(cmut_.GetConnectionProperties(&type, &tethering));
}
} // namespace chromeos_update_engine
diff --git a/daemon.cc b/daemon.cc
new file mode 100644
index 0000000..bc221b5
--- /dev/null
+++ b/daemon.cc
@@ -0,0 +1,137 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/daemon.h"
+
+#include <sysexits.h>
+
+#include <base/bind.h>
+#include <base/location.h>
+#include <base/time/time.h>
+#include <chromeos/message_loops/message_loop.h>
+
+#include "update_engine/clock.h"
+#include "update_engine/update_attempter.h"
+
+using chromeos::MessageLoop;
+
+namespace {
+const int kDBusSystemMaxWaitSeconds = 2 * 60;
+} // namespace
+
+namespace chromeos_update_engine {
+
+namespace {
+// Wait for passed |bus| DBus to be connected by attempting to connect it up to
+// |timeout| time. Returns whether the Connect() eventually succeeded.
+bool WaitForDBusSystem(dbus::Bus* bus, base::TimeDelta timeout) {
+ Clock clock;
+ base::Time deadline = clock.GetMonotonicTime() + timeout;
+
+ while (clock.GetMonotonicTime() < deadline) {
+ if (bus->Connect())
+ return true;
+ LOG(WARNING) << "Failed to get system bus, waiting.";
+ // Wait 1 second.
+ sleep(1);
+ }
+ LOG(ERROR) << "Failed to get system bus after " << timeout.InSeconds()
+ << " seconds.";
+ return false;
+}
+} // namespace
+
+int UpdateEngineDaemon::OnInit() {
+ // Register the |subprocess_| singleton with this Daemon as the signal
+ // handler.
+ subprocess_.Init(this);
+
+ // We use Daemon::OnInit() and not DBusDaemon::OnInit() to gracefully wait for
+ // the D-Bus connection for up two minutes to avoid re-spawning the daemon
+ // too fast causing thrashing if dbus-daemon is not running.
+ int exit_code = Daemon::OnInit();
+ if (exit_code != EX_OK)
+ return exit_code;
+
+ dbus::Bus::Options options;
+ options.bus_type = dbus::Bus::SYSTEM;
+ bus_ = new dbus::Bus(options);
+
+ // Wait for DBus to be ready and exit if it doesn't become available after
+ // the timeout.
+ if (!WaitForDBusSystem(
+ bus_.get(),
+ base::TimeDelta::FromSeconds(kDBusSystemMaxWaitSeconds))) {
+ // TODO(deymo): Make it possible to run update_engine even if dbus-daemon
+ // is not running or constantly crashing.
+ LOG(ERROR) << "Failed to initialize DBus, aborting.";
+ return 1;
+ }
+
+ CHECK(bus_->SetUpAsyncOperations());
+
+ // Initialize update engine global state but continue if something fails.
+ real_system_state_.reset(new RealSystemState(bus_));
+ LOG_IF(ERROR, !real_system_state_->Initialize())
+ << "Failed to initialize system state.";
+ UpdateAttempter* update_attempter = real_system_state_->update_attempter();
+ CHECK(update_attempter);
+
+ // Sets static members for the certificate checker.
+ CertificateChecker::set_system_state(real_system_state_.get());
+ CertificateChecker::set_openssl_wrapper(&openssl_wrapper_);
+
+ // Create the DBus service.
+ dbus_adaptor_.reset(new UpdateEngineAdaptor(real_system_state_.get(), bus_));
+ update_attempter->set_dbus_adaptor(dbus_adaptor_.get());
+
+ dbus_adaptor_->RegisterAsync(base::Bind(&UpdateEngineDaemon::OnDBusRegistered,
+ base::Unretained(this)));
+ LOG(INFO) << "Waiting for DBus object to be registered.";
+ return EX_OK;
+}
+
+void UpdateEngineDaemon::OnDBusRegistered(bool succeeded) {
+ if (!succeeded) {
+ LOG(ERROR) << "Registering the UpdateEngineAdaptor";
+ QuitWithExitCode(1);
+ return;
+ }
+
+ // Take ownership of the service now that everything is initialized. We need
+ // to this now and not before to avoid exposing a well known DBus service
+ // path that doesn't have the service it is supposed to implement.
+ if (!dbus_adaptor_->RequestOwnership()) {
+ LOG(ERROR) << "Unable to take ownership of the DBus service, is there "
+ << "other update_engine daemon running?";
+ QuitWithExitCode(1);
+ return;
+ }
+
+ // Initiate update checks.
+ UpdateAttempter* update_attempter = real_system_state_->update_attempter();
+ update_attempter->ScheduleUpdates();
+
+ // Update boot flags after 45 seconds.
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&UpdateAttempter::UpdateBootFlags,
+ base::Unretained(update_attempter)),
+ base::TimeDelta::FromSeconds(45));
+
+ // Broadcast the update engine status on startup to ensure consistent system
+ // state on crashes.
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &UpdateAttempter::BroadcastStatus,
+ base::Unretained(update_attempter)));
+
+ // Run the UpdateEngineStarted() method on |update_attempter|.
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &UpdateAttempter::UpdateEngineStarted,
+ base::Unretained(update_attempter)));
+
+ LOG(INFO) << "Finished initialization. Now running the loop.";
+}
+
+} // namespace chromeos_update_engine
diff --git a/daemon.h b/daemon.h
new file mode 100644
index 0000000..4a7fe18
--- /dev/null
+++ b/daemon.h
@@ -0,0 +1,47 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UPDATE_ENGINE_DAEMON_H_
+#define UPDATE_ENGINE_DAEMON_H_
+
+#include <memory>
+#include <string>
+
+#include <chromeos/daemons/dbus_daemon.h>
+
+#include "update_engine/certificate_checker.h"
+#include "update_engine/dbus_service.h"
+#include "update_engine/real_system_state.h"
+#include "update_engine/subprocess.h"
+
+namespace chromeos_update_engine {
+
+class UpdateEngineDaemon : public chromeos::DBusDaemon {
+ public:
+ UpdateEngineDaemon() = default;
+
+ protected:
+ int OnInit() override;
+
+ private:
+ // Run from the main loop when the |dbus_adaptor_| object is registered. At
+ // this point we can request ownership of the DBus service name and continue
+ // initialization.
+ void OnDBusRegistered(bool succeeded);
+
+ // The Subprocess singleton class requires a chromeos::MessageLoop in the
+ // current thread, so we need to initialize it from this class instead of
+ // the main() function.
+ Subprocess subprocess_;
+
+ std::unique_ptr<RealSystemState> real_system_state_;
+ OpenSSLWrapper openssl_wrapper_;
+ std::unique_ptr<UpdateEngineAdaptor> dbus_adaptor_;
+
+ DISALLOW_COPY_AND_ASSIGN(UpdateEngineDaemon);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_DAEMON_H_
diff --git a/dbus_bindings/org.chromium.LibCrosService.xml b/dbus_bindings/org.chromium.LibCrosService.xml
new file mode 100644
index 0000000..2ea8313
--- /dev/null
+++ b/dbus_bindings/org.chromium.LibCrosService.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/org/chromium/LibCrosService"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <interface name="org.chromium.LibCrosServiceInterface">
+ <method name="ResolveNetworkProxy">
+ <arg name="source_url" type="s" direction="in" />
+ <arg name="signal_interface" type="s" direction="in" />
+ <arg name="signal_name" type="s" direction="in" />
+ <annotation name="org.chromium.DBus.Method.Kind" value="simple" />
+ </method>
+ </interface>
+ <interface name="org.chromium.UpdateEngineLibcrosProxyResolvedInterface">
+ <signal name="ProxyResolved">
+ <arg name="source_url" type="s" direction="out" />
+ <arg name="proxy_info" type="s" direction="out" />
+ <arg name="error_message" type="s" direction="out" />
+ </signal>
+ </interface>
+</node>
diff --git a/dbus_bindings/org.chromium.UpdateEngineInterface.xml b/dbus_bindings/org.chromium.UpdateEngineInterface.xml
index dda972d..a632608 100644
--- a/dbus_bindings/org.chromium.UpdateEngineInterface.xml
+++ b/dbus_bindings/org.chromium.UpdateEngineInterface.xml
@@ -1,8 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
-<!-- COPYRIGHT HERE
- dbus-binding-tool -mode=glib-server -prefix=update_engine update_engine.xml
- > glib_server.h
--->
<node name="/org/chromium/UpdateEngine">
<interface name="org.chromium.UpdateEngineInterface">
<annotation name="org.freedesktop.DBus.GLib.CSymbol"
diff --git a/dbus_constants.h b/dbus_constants.h
index d51e6fd..f8494ff 100644
--- a/dbus_constants.h
+++ b/dbus_constants.h
@@ -13,6 +13,10 @@
static const char* const kUpdateEngineServiceInterface =
"org.chromium.UpdateEngineInterface";
+// Generic UpdateEngine D-Bus error.
+static const char* const kUpdateEngineServiceErrorFailed =
+ "org.chromium.UpdateEngine.Error.Failed";
+
// Flags used in the AttemptUpdateWithFlags() D-Bus method.
typedef enum {
kAttemptUpdateFlagNonInteractive = (1<<0)
diff --git a/dbus_service.cc b/dbus_service.cc
index a7ec259..7c9d0c2 100644
--- a/dbus_service.cc
+++ b/dbus_service.cc
@@ -7,13 +7,16 @@
#include <set>
#include <string>
+#include <base/location.h>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
+#include <chromeos/bind_lambda.h>
+#include <chromeos/message_loops/message_loop.h>
#include <chromeos/strings/string_utils.h>
#include <policy/device_policy.h>
#include "update_engine/clock_interface.h"
-#include "update_engine/connection_manager.h"
+#include "update_engine/connection_manager_interface.h"
#include "update_engine/dbus_constants.h"
#include "update_engine/hardware_interface.h"
#include "update_engine/omaha_request_params.h"
@@ -23,400 +26,232 @@
#include "update_engine/utils.h"
using base::StringPrintf;
+using chromeos::ErrorPtr;
using chromeos::string_utils::ToString;
using chromeos_update_engine::AttemptUpdateFlags;
using chromeos_update_engine::kAttemptUpdateFlagNonInteractive;
using std::set;
using std::string;
-#define UPDATE_ENGINE_SERVICE_ERROR update_engine_service_error_quark ()
-#define UPDATE_ENGINE_SERVICE_TYPE_ERROR \
- (update_engine_service_error_get_type())
+namespace {
+// Log and set the error on the passed ErrorPtr.
+void LogAndSetError(ErrorPtr *error,
+ const tracked_objects::Location& location,
+ const string& reason) {
+ chromeos::Error::AddTo(
+ error, location,
+ chromeos::errors::dbus::kDomain,
+ chromeos_update_engine::kUpdateEngineServiceErrorFailed, reason);
+ LOG(ERROR) << "Sending DBus Failure: " << location.ToString() << ": "
+ << reason;
+}
+} // namespace
-enum UpdateEngineServiceError {
- UPDATE_ENGINE_SERVICE_ERROR_FAILED,
- UPDATE_ENGINE_SERVICE_NUM_ERRORS
-};
+namespace chromeos_update_engine {
-static GQuark update_engine_service_error_quark(void) {
- static GQuark ret = 0;
+UpdateEngineService::UpdateEngineService(SystemState* system_state)
+ : system_state_(system_state) {}
- if (ret == 0)
- ret = g_quark_from_static_string("update_engine_service_error");
+// org::chromium::UpdateEngineInterfaceInterface methods implementation.
- return ret;
+bool UpdateEngineService::AttemptUpdate(ErrorPtr* error,
+ const string& in_app_version,
+ const string& in_omaha_url) {
+ return AttemptUpdateWithFlags(
+ error, in_app_version, in_omaha_url, 0 /* no flags */);
}
-#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
-
-static GType update_engine_service_error_get_type(void) {
- static GType etype = 0;
-
- if (etype == 0) {
- static const GEnumValue values[] = {
- ENUM_ENTRY(UPDATE_ENGINE_SERVICE_ERROR_FAILED, "Failed"),
- { 0, 0, 0 }
- };
- G_STATIC_ASSERT(UPDATE_ENGINE_SERVICE_NUM_ERRORS ==
- G_N_ELEMENTS(values) - 1);
- etype = g_enum_register_static("UpdateEngineServiceError", values);
- }
-
- return etype;
-}
-
-G_DEFINE_TYPE(UpdateEngineService, update_engine_service, G_TYPE_OBJECT)
-
-static void update_engine_service_finalize(GObject* object) {
- G_OBJECT_CLASS(update_engine_service_parent_class)->finalize(object);
-}
-
-static void log_and_set_response_error(GError** error,
- UpdateEngineServiceError error_code,
- const string& reason) {
- LOG(ERROR) << "Sending DBus Failure: " << reason;
- g_set_error_literal(error, UPDATE_ENGINE_SERVICE_ERROR,
- error_code, reason.c_str());
-}
-
-static guint status_update_signal = 0;
-
-static void update_engine_service_class_init(UpdateEngineServiceClass* klass) {
- GObjectClass *object_class;
- object_class = G_OBJECT_CLASS(klass);
- object_class->finalize = update_engine_service_finalize;
-
- status_update_signal = g_signal_new(
- "status_update",
- G_OBJECT_CLASS_TYPE(klass),
- G_SIGNAL_RUN_LAST,
- 0, // 0 == no class method associated
- nullptr, // Accumulator
- nullptr, // Accumulator data
- nullptr, // Marshaller
- G_TYPE_NONE, // Return type
- 5, // param count:
- G_TYPE_INT64,
- G_TYPE_DOUBLE,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_INT64);
-}
-
-static void update_engine_service_init(UpdateEngineService* object) {
- dbus_g_error_domain_register(UPDATE_ENGINE_SERVICE_ERROR,
- "org.chromium.UpdateEngine.Error",
- UPDATE_ENGINE_SERVICE_TYPE_ERROR);
-}
-
-UpdateEngineService* update_engine_service_new(void) {
- return reinterpret_cast<UpdateEngineService*>(
- g_object_new(UPDATE_ENGINE_TYPE_SERVICE, nullptr));
-}
-
-gboolean update_engine_service_attempt_update(UpdateEngineService* self,
- gchar* app_version,
- gchar* omaha_url,
- GError **error) {
- return update_engine_service_attempt_update_with_flags(self,
- app_version,
- omaha_url,
- 0, // No flags set.
- error);
-}
-
-gboolean update_engine_service_attempt_update_with_flags(
- UpdateEngineService* self,
- gchar* app_version,
- gchar* omaha_url,
- gint flags_as_int,
- GError **error) {
- string app_version_string, omaha_url_string;
- AttemptUpdateFlags flags = static_cast<AttemptUpdateFlags>(flags_as_int);
+bool UpdateEngineService::AttemptUpdateWithFlags(ErrorPtr* /* error */,
+ const string& in_app_version,
+ const string& in_omaha_url,
+ int32_t in_flags_as_int) {
+ AttemptUpdateFlags flags = static_cast<AttemptUpdateFlags>(in_flags_as_int);
bool interactive = !(flags & kAttemptUpdateFlagNonInteractive);
- if (app_version)
- app_version_string = app_version;
- if (omaha_url)
- omaha_url_string = omaha_url;
-
- LOG(INFO) << "Attempt update: app_version=\"" << app_version_string << "\" "
- << "omaha_url=\"" << omaha_url_string << "\" "
+ LOG(INFO) << "Attempt update: app_version=\"" << in_app_version << "\" "
+ << "omaha_url=\"" << in_omaha_url << "\" "
<< "flags=0x" << std::hex << flags << " "
<< "interactive=" << (interactive? "yes" : "no");
- self->system_state_->update_attempter()->CheckForUpdate(app_version_string,
- omaha_url_string,
- interactive);
- return TRUE;
+ system_state_->update_attempter()->CheckForUpdate(
+ in_app_version, in_omaha_url, interactive);
+ return true;
}
-gboolean update_engine_service_attempt_rollback(UpdateEngineService* self,
- gboolean powerwash,
- GError **error) {
+bool UpdateEngineService::AttemptRollback(ErrorPtr* error,
+ bool in_powerwash) {
LOG(INFO) << "Attempting rollback to non-active partitions.";
- if (!self->system_state_->update_attempter()->Rollback(powerwash)) {
+ if (!system_state_->update_attempter()->Rollback(in_powerwash)) {
// TODO(dgarrett): Give a more specific error code/reason.
- log_and_set_response_error(error,
- UPDATE_ENGINE_SERVICE_ERROR_FAILED,
- "Rollback attempt failed.");
- return FALSE;
+ LogAndSetError(error, FROM_HERE, "Rollback attempt failed.");
+ return false;
}
-
- return TRUE;
+ return true;
}
-gboolean update_engine_service_can_rollback(UpdateEngineService* self,
- gboolean* out_can_rollback,
- GError **error) {
- bool can_rollback = self->system_state_->update_attempter()->CanRollback();
+bool UpdateEngineService::CanRollback(ErrorPtr* /* error */,
+ bool* out_can_rollback) {
+ bool can_rollback = system_state_->update_attempter()->CanRollback();
LOG(INFO) << "Checking to see if we can rollback . Result: " << can_rollback;
*out_can_rollback = can_rollback;
- return TRUE;
+ return true;
}
-gboolean update_engine_service_get_rollback_partition(
- UpdateEngineService* self,
- gchar** out_rollback_partition_name,
- GError **error) {
- auto name = self->system_state_->update_attempter()->GetRollbackPartition();
- LOG(INFO) << "Getting rollback partition name. Result: " << name;
- *out_rollback_partition_name = g_strdup(name.c_str());
- return TRUE;
-}
-
-gboolean update_engine_service_get_kernel_devices(UpdateEngineService* self,
- gchar** out_kernel_devices,
- GError **error) {
- auto devices = self->system_state_->update_attempter()->GetKernelDevices();
- string info;
- for (const auto& device : devices) {
- base::StringAppendF(&info, "%d:%s\n",
- device.second ? 1 : 0, device.first.c_str());
- }
- LOG(INFO) << "Available kernel devices: " << info;
- *out_kernel_devices = g_strdup(info.c_str());
- return TRUE;
-}
-
-
-gboolean update_engine_service_reset_status(UpdateEngineService* self,
- GError **error) {
- if (!self->system_state_->update_attempter()->ResetStatus()) {
+bool UpdateEngineService::ResetStatus(ErrorPtr* error) {
+ if (!system_state_->update_attempter()->ResetStatus()) {
// TODO(dgarrett): Give a more specific error code/reason.
- log_and_set_response_error(error,
- UPDATE_ENGINE_SERVICE_ERROR_FAILED,
- "ResetStatus failed.");
- return FALSE;
+ LogAndSetError(error, FROM_HERE, "ResetStatus failed.");
+ return false;
}
-
- return TRUE;
+ return true;
}
-gboolean update_engine_service_get_status(UpdateEngineService* self,
- int64_t* last_checked_time,
- double* progress,
- gchar** current_operation,
- gchar** new_version,
- int64_t* new_size,
- GError **error) {
- string current_op;
- string new_version_str;
-
- CHECK(self->system_state_->update_attempter()->GetStatus(last_checked_time,
- progress,
- ¤t_op,
- &new_version_str,
- new_size));
- *current_operation = g_strdup(current_op.c_str());
- *new_version = g_strdup(new_version_str.c_str());
-
- if (!*current_operation) {
- log_and_set_response_error(error,
- UPDATE_ENGINE_SERVICE_ERROR_FAILED,
- "Unable to find current_operation.");
- return FALSE;
+bool UpdateEngineService::GetStatus(ErrorPtr* error,
+ int64_t* out_last_checked_time,
+ double* out_progress,
+ string* out_current_operation,
+ string* out_new_version,
+ int64_t* out_new_size) {
+ if (!system_state_->update_attempter()->GetStatus(out_last_checked_time,
+ out_progress,
+ out_current_operation,
+ out_new_version,
+ out_new_size)) {
+ LogAndSetError(error, FROM_HERE, "GetStatus failed.");
+ return false;
}
-
- if (!*new_version) {
- log_and_set_response_error(error,
- UPDATE_ENGINE_SERVICE_ERROR_FAILED,
- "Unable to find vew_version.");
- return FALSE;
- }
-
- return TRUE;
+ return true;
}
-gboolean update_engine_service_reboot_if_needed(UpdateEngineService* self,
- GError **error) {
- if (!self->system_state_->update_attempter()->RebootIfNeeded()) {
+bool UpdateEngineService::RebootIfNeeded(ErrorPtr* error) {
+ if (!system_state_->update_attempter()->RebootIfNeeded()) {
// TODO(dgarrett): Give a more specific error code/reason.
- log_and_set_response_error(error,
- UPDATE_ENGINE_SERVICE_ERROR_FAILED,
- "Reboot not needed, or attempt failed.");
- return FALSE;
+ LogAndSetError(error, FROM_HERE, "Reboot not needed, or attempt failed.");
+ return false;
}
- return TRUE;
+ return true;
}
-gboolean update_engine_service_set_channel(UpdateEngineService* self,
- gchar* target_channel,
- gboolean is_powerwash_allowed,
- GError **error) {
- if (!target_channel) {
- log_and_set_response_error(error,
- UPDATE_ENGINE_SERVICE_ERROR_FAILED,
- "Target channel to set not specified.");
- return FALSE;
- }
-
- const policy::DevicePolicy* device_policy =
- self->system_state_->device_policy();
+bool UpdateEngineService::SetChannel(ErrorPtr* error,
+ const string& in_target_channel,
+ bool in_is_powerwash_allowed) {
+ const policy::DevicePolicy* device_policy = system_state_->device_policy();
// The device_policy is loaded in a lazy way before an update check. Load it
// now from the libchromeos cache if it wasn't already loaded.
if (!device_policy) {
- chromeos_update_engine::UpdateAttempter* update_attempter =
- self->system_state_->update_attempter();
+ UpdateAttempter* update_attempter = system_state_->update_attempter();
if (update_attempter) {
update_attempter->RefreshDevicePolicy();
- device_policy = self->system_state_->device_policy();
+ device_policy = system_state_->device_policy();
}
}
bool delegated = false;
if (device_policy &&
device_policy->GetReleaseChannelDelegated(&delegated) && !delegated) {
- log_and_set_response_error(
- error, UPDATE_ENGINE_SERVICE_ERROR_FAILED,
+ LogAndSetError(
+ error, FROM_HERE,
"Cannot set target channel explicitly when channel "
"policy/settings is not delegated");
- return FALSE;
+ return false;
}
- LOG(INFO) << "Setting destination channel to: " << target_channel;
- if (!self->system_state_->request_params()->SetTargetChannel(
- target_channel, is_powerwash_allowed)) {
+ LOG(INFO) << "Setting destination channel to: " << in_target_channel;
+ if (!system_state_->request_params()->SetTargetChannel(
+ in_target_channel, in_is_powerwash_allowed)) {
// TODO(dgarrett): Give a more specific error code/reason.
- log_and_set_response_error(error,
- UPDATE_ENGINE_SERVICE_ERROR_FAILED,
- "Setting channel failed.");
- return FALSE;
+ LogAndSetError(error, FROM_HERE, "Setting channel failed.");
+ return false;
}
-
- return TRUE;
+ return true;
}
-gboolean update_engine_service_get_channel(UpdateEngineService* self,
- gboolean get_current_channel,
- gchar** channel,
- GError **error) {
- chromeos_update_engine::OmahaRequestParams* rp =
- self->system_state_->request_params();
-
- string channel_str = get_current_channel ?
- rp->current_channel() : rp->target_channel();
-
- *channel = g_strdup(channel_str.c_str());
- return TRUE;
+bool UpdateEngineService::GetChannel(ErrorPtr* /* error */,
+ bool in_get_current_channel,
+ string* out_channel) {
+ OmahaRequestParams* rp = system_state_->request_params();
+ *out_channel = (in_get_current_channel ?
+ rp->current_channel() : rp->target_channel());
+ return true;
}
-gboolean update_engine_service_set_p2p_update_permission(
- UpdateEngineService* self,
- gboolean enabled,
- GError **error) {
- chromeos_update_engine::PrefsInterface* prefs = self->system_state_->prefs();
+bool UpdateEngineService::SetP2PUpdatePermission(ErrorPtr* error,
+ bool in_enabled) {
+ PrefsInterface* prefs = system_state_->prefs();
- if (!prefs->SetBoolean(chromeos_update_engine::kPrefsP2PEnabled, enabled)) {
- log_and_set_response_error(
- error, UPDATE_ENGINE_SERVICE_ERROR_FAILED,
+ if (!prefs->SetBoolean(kPrefsP2PEnabled, in_enabled)) {
+ LogAndSetError(
+ error, FROM_HERE,
StringPrintf("Error setting the update via p2p permission to %s.",
- ToString(enabled).c_str()));
- return FALSE;
+ ToString(in_enabled).c_str()));
+ return false;
}
-
- return TRUE;
+ return true;
}
-gboolean update_engine_service_get_p2p_update_permission(
- UpdateEngineService* self,
- gboolean* enabled,
- GError **error) {
- chromeos_update_engine::PrefsInterface* prefs = self->system_state_->prefs();
+bool UpdateEngineService::GetP2PUpdatePermission(ErrorPtr* error,
+ bool* out_enabled) {
+ PrefsInterface* prefs = system_state_->prefs();
bool p2p_pref = false; // Default if no setting is present.
- if (prefs->Exists(chromeos_update_engine::kPrefsP2PEnabled) &&
- !prefs->GetBoolean(chromeos_update_engine::kPrefsP2PEnabled, &p2p_pref)) {
- log_and_set_response_error(error, UPDATE_ENGINE_SERVICE_ERROR_FAILED,
- "Error getting the P2PEnabled setting.");
- return FALSE;
+ if (prefs->Exists(kPrefsP2PEnabled) &&
+ !prefs->GetBoolean(kPrefsP2PEnabled, &p2p_pref)) {
+ LogAndSetError(error, FROM_HERE, "Error getting the P2PEnabled setting.");
+ return false;
}
- *enabled = p2p_pref;
- return TRUE;
+ *out_enabled = p2p_pref;
+ return true;
}
-gboolean update_engine_service_set_update_over_cellular_permission(
- UpdateEngineService* self,
- gboolean allowed,
- GError **error) {
+bool UpdateEngineService::SetUpdateOverCellularPermission(ErrorPtr* error,
+ bool in_allowed) {
set<string> allowed_types;
- const policy::DevicePolicy* device_policy =
- self->system_state_->device_policy();
+ const policy::DevicePolicy* device_policy = system_state_->device_policy();
// The device_policy is loaded in a lazy way before an update check. Load it
// now from the libchromeos cache if it wasn't already loaded.
if (!device_policy) {
- chromeos_update_engine::UpdateAttempter* update_attempter =
- self->system_state_->update_attempter();
+ UpdateAttempter* update_attempter = system_state_->update_attempter();
if (update_attempter) {
update_attempter->RefreshDevicePolicy();
- device_policy = self->system_state_->device_policy();
+ device_policy = system_state_->device_policy();
}
}
// Check if this setting is allowed by the device policy.
if (device_policy &&
device_policy->GetAllowedConnectionTypesForUpdate(&allowed_types)) {
- log_and_set_response_error(
- error, UPDATE_ENGINE_SERVICE_ERROR_FAILED,
- "Ignoring the update over cellular setting since there's "
- "a device policy enforcing this setting.");
- return FALSE;
+ LogAndSetError(error, FROM_HERE,
+ "Ignoring the update over cellular setting since there's "
+ "a device policy enforcing this setting.");
+ return false;
}
// If the policy wasn't loaded yet, then it is still OK to change the local
// setting because the policy will be checked again during the update check.
- chromeos_update_engine::PrefsInterface* prefs = self->system_state_->prefs();
+ PrefsInterface* prefs = system_state_->prefs();
- if (!prefs->SetBoolean(
- chromeos_update_engine::kPrefsUpdateOverCellularPermission,
- allowed)) {
- log_and_set_response_error(
- error, UPDATE_ENGINE_SERVICE_ERROR_FAILED,
- string("Error setting the update over cellular to ") +
- (allowed ? "true" : "false"));
- return FALSE;
+ if (!prefs->SetBoolean(kPrefsUpdateOverCellularPermission, in_allowed)) {
+ LogAndSetError(error, FROM_HERE,
+ string("Error setting the update over cellular to ") +
+ (in_allowed ? "true" : "false"));
+ return false;
}
-
- return TRUE;
+ return true;
}
-gboolean update_engine_service_get_update_over_cellular_permission(
- UpdateEngineService* self,
- gboolean* allowed,
- GError **error) {
- chromeos_update_engine::ConnectionManager* cm =
- self->system_state_->connection_manager();
+bool UpdateEngineService::GetUpdateOverCellularPermission(ErrorPtr* /* error */,
+ bool* out_allowed) {
+ ConnectionManagerInterface* cm = system_state_->connection_manager();
// The device_policy is loaded in a lazy way before an update check and is
// used to determine if an update is allowed over cellular. Load the device
// policy now from the libchromeos cache if it wasn't already loaded.
- if (!self->system_state_->device_policy()) {
- chromeos_update_engine::UpdateAttempter* update_attempter =
- self->system_state_->update_attempter();
+ if (!system_state_->device_policy()) {
+ UpdateAttempter* update_attempter = system_state_->update_attempter();
if (update_attempter)
update_attempter->RefreshDevicePolicy();
}
@@ -424,53 +259,70 @@
// Return the current setting based on the same logic used while checking for
// updates. A log message could be printed as the result of this test.
LOG(INFO) << "Checking if updates over cellular networks are allowed:";
- *allowed = cm->IsUpdateAllowedOver(
- chromeos_update_engine::kNetCellular,
+ *out_allowed = cm->IsUpdateAllowedOver(
+ chromeos_update_engine::NetworkConnectionType::kCellular,
chromeos_update_engine::NetworkTethering::kUnknown);
-
- return TRUE;
+ return true;
}
-gboolean update_engine_service_get_duration_since_update(
- UpdateEngineService* self,
- gint64* out_usec_wallclock,
- GError **error) {
-
+bool UpdateEngineService::GetDurationSinceUpdate(ErrorPtr* error,
+ int64_t* out_usec_wallclock) {
base::Time time;
- if (!self->system_state_->update_attempter()->GetBootTimeAtUpdate(&time)) {
- log_and_set_response_error(error, UPDATE_ENGINE_SERVICE_ERROR_FAILED,
- "No pending update.");
- return FALSE;
+ if (!system_state_->update_attempter()->GetBootTimeAtUpdate(&time)) {
+ LogAndSetError(error, FROM_HERE, "No pending update.");
+ return false;
}
- chromeos_update_engine::ClockInterface *clock = self->system_state_->clock();
+ ClockInterface* clock = system_state_->clock();
*out_usec_wallclock = (clock->GetBootTime() - time).InMicroseconds();
- return TRUE;
+ return true;
}
-gboolean update_engine_service_emit_status_update(
- UpdateEngineService* self,
- gint64 last_checked_time,
- gdouble progress,
- const gchar* current_operation,
- const gchar* new_version,
- gint64 new_size) {
- g_signal_emit(self,
- status_update_signal,
- 0,
- last_checked_time,
- progress,
- current_operation,
- new_version,
- new_size);
- return TRUE;
+bool UpdateEngineService::GetPrevVersion(ErrorPtr* /* error */,
+ string* out_prev_version) {
+ *out_prev_version = system_state_->update_attempter()->GetPrevVersion();
+ return true;
}
-gboolean update_engine_service_get_prev_version(
- UpdateEngineService* self,
- gchar** prev_version,
- GError **error) {
- string ver = self->system_state_->update_attempter()->GetPrevVersion();
- *prev_version = g_strdup(ver.c_str());
- return TRUE;
+bool UpdateEngineService::GetKernelDevices(ErrorPtr* /* error */,
+ string* out_kernel_devices) {
+ auto devices = system_state_->update_attempter()->GetKernelDevices();
+ string info;
+ for (const auto& device : devices) {
+ base::StringAppendF(&info, "%d:%s\n",
+ device.second ? 1 : 0, device.first.c_str());
+ }
+ LOG(INFO) << "Available kernel devices: " << info;
+ *out_kernel_devices = info;
+ return true;
}
+
+bool UpdateEngineService::GetRollbackPartition(
+ ErrorPtr* /* error */,
+ string* out_rollback_partition_name) {
+ string name = system_state_->update_attempter()->GetRollbackPartition();
+ LOG(INFO) << "Getting rollback partition name. Result: " << name;
+ *out_rollback_partition_name = name;
+ return true;
+}
+
+
+UpdateEngineAdaptor::UpdateEngineAdaptor(SystemState* system_state,
+ const scoped_refptr<dbus::Bus>& bus)
+ : org::chromium::UpdateEngineInterfaceAdaptor(&dbus_service_),
+ bus_(bus),
+ dbus_service_(system_state),
+ dbus_object_(nullptr, bus, dbus::ObjectPath(kUpdateEngineServicePath)) {}
+
+void UpdateEngineAdaptor::RegisterAsync(
+ const base::Callback<void(bool)>& completion_callback) {
+ RegisterWithDBusObject(&dbus_object_);
+ dbus_object_.RegisterAsync(completion_callback);
+}
+
+bool UpdateEngineAdaptor::RequestOwnership() {
+ return bus_->RequestOwnershipAndBlock(kUpdateEngineServiceName,
+ dbus::Bus::REQUIRE_PRIMARY);
+}
+
+} // namespace chromeos_update_engine
diff --git a/dbus_service.h b/dbus_service.h
index d61e3b5..6429240 100644
--- a/dbus_service.h
+++ b/dbus_service.h
@@ -7,171 +7,153 @@
#include <inttypes.h>
-#include <dbus/dbus-glib.h>
-#include <dbus/dbus-glib-bindings.h>
-#include <dbus/dbus-glib-lowlevel.h>
-#include <glib-object.h>
+#include <string>
+
+#include <base/memory/ref_counted.h>
+#include <chromeos/errors/error.h>
#include "update_engine/update_attempter.h"
-// Type macros:
-#define UPDATE_ENGINE_TYPE_SERVICE (update_engine_service_get_type())
-#define UPDATE_ENGINE_SERVICE(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj), UPDATE_ENGINE_TYPE_SERVICE, \
- UpdateEngineService))
-#define UPDATE_ENGINE_IS_SERVICE(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj), UPDATE_ENGINE_TYPE_SERVICE))
-#define UPDATE_ENGINE_SERVICE_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass), UPDATE_ENGINE_TYPE_SERVICE, \
- UpdateEngineService))
-#define UPDATE_ENGINE_IS_SERVICE_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE((klass), UPDATE_ENGINE_TYPE_SERVICE))
-#define UPDATE_ENGINE_SERVICE_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS((obj), UPDATE_ENGINE_TYPE_SERVICE, \
- UpdateEngineService))
+#include "update_engine/dbus_adaptor/org.chromium.UpdateEngineInterface.h"
-G_BEGIN_DECLS
+namespace chromeos {
+namespace dbus {
+class Bus;
+} // namespace dbus
+} // namespace chromeos
-struct UpdateEngineService {
- GObject parent_instance;
+namespace chromeos_update_engine {
- chromeos_update_engine::SystemState* system_state_;
+class UpdateEngineService
+ : public org::chromium::UpdateEngineInterfaceInterface {
+ public:
+ explicit UpdateEngineService(SystemState* system_state);
+ virtual ~UpdateEngineService() = default;
+
+ // Implementation of org::chromium::UpdateEngineInterfaceInterface.
+ bool AttemptUpdate(chromeos::ErrorPtr* error,
+ const std::string& in_app_version,
+ const std::string& in_omaha_url) override;
+
+ bool AttemptUpdateWithFlags(chromeos::ErrorPtr* error,
+ const std::string& in_app_version,
+ const std::string& in_omaha_url,
+ int32_t in_flags_as_int) override;
+
+ bool AttemptRollback(chromeos::ErrorPtr* error, bool in_powerwash) override;
+
+ // Checks if the system rollback is available by verifying if the secondary
+ // system partition is valid and bootable.
+ bool CanRollback(chromeos::ErrorPtr* error, bool* out_can_rollback) override;
+
+ // Resets the status of the update_engine to idle, ignoring any applied
+ // update. This is used for development only.
+ bool ResetStatus(chromeos::ErrorPtr* error) override;
+
+ // Returns the current status of the Update Engine. If an update is in
+ // progress, the number of operations, size to download and overall progress
+ // is reported.
+ bool GetStatus(chromeos::ErrorPtr* error,
+ int64_t* out_last_checked_time,
+ double* out_progress,
+ std::string* out_current_operation,
+ std::string* out_new_version,
+ int64_t* out_new_size) override;
+
+ // Reboots the device if an update is applied and a reboot is required.
+ bool RebootIfNeeded(chromeos::ErrorPtr* error) override;
+
+ // Changes the current channel of the device to the target channel. If the
+ // target channel is a less stable channel than the current channel, then the
+ // channel change happens immediately (at the next update check). If the
+ // target channel is a more stable channel, then if is_powerwash_allowed is
+ // set to true, then also the change happens immediately but with a powerwash
+ // if required. Otherwise, the change takes effect eventually (when the
+ // version on the target channel goes above the version number of what the
+ // device currently has).
+ bool SetChannel(chromeos::ErrorPtr* error,
+ const std::string& in_target_channel,
+ bool in_is_powerwash_allowed) override;
+
+ // If get_current_channel is set to true, populates |channel| with the name of
+ // the channel that the device is currently on. Otherwise, it populates it
+ // with the name of the channel the device is supposed to be (in case of a
+ // pending channel change).
+ bool GetChannel(chromeos::ErrorPtr* error,
+ bool in_get_current_channel,
+ std::string* out_channel) override;
+
+ // Enables or disables the sharing and consuming updates over P2P feature
+ // according to the |enabled| argument passed.
+ bool SetP2PUpdatePermission(chromeos::ErrorPtr* error,
+ bool in_enabled) override;
+
+ // Returns the current value for the P2P enabled setting. This involves both
+ // sharing and consuming updates over P2P.
+ bool GetP2PUpdatePermission(chromeos::ErrorPtr* error,
+ bool* out_enabled) override;
+
+ // If there's no device policy installed, sets the update over cellular
+ // networks permission to the |allowed| value. Otherwise, this method returns
+ // with an error since this setting is overridden by the applied policy.
+ bool SetUpdateOverCellularPermission(chromeos::ErrorPtr* error,
+ bool in_allowed) override;
+
+ // Returns the current value of the update over cellular network setting,
+ // either forced by the device policy if the device is enrolled or the current
+ // user preference otherwise.
+ bool GetUpdateOverCellularPermission(chromeos::ErrorPtr* error,
+ bool* out_allowed) override;
+
+ // Returns the duration since the last successful update, as the
+ // duration on the wallclock. Returns an error if the device has not
+ // updated.
+ bool GetDurationSinceUpdate(chromeos::ErrorPtr* error,
+ int64_t* out_usec_wallclock) override;
+
+ // Returns the version string of OS that was used before the last reboot
+ // into an updated version. This is available only when rebooting into an
+ // update from previous version, otherwise an empty string is returned.
+ bool GetPrevVersion(chromeos::ErrorPtr* error,
+ std::string* out_prev_version) override;
+
+ // Returns a list of available kernel partitions and whether each of them
+ // can be booted from or not.
+ bool GetKernelDevices(chromeos::ErrorPtr* error,
+ std::string* out_kernel_devices) override;
+
+ // Returns the name of kernel partition that can be rolled back into.
+ bool GetRollbackPartition(chromeos::ErrorPtr* error,
+ std::string* out_rollback_partition_name) override;
+
+ private:
+ SystemState* system_state_;
};
-struct UpdateEngineServiceClass {
- GObjectClass parent_class;
+// The UpdateEngineAdaptor class runs the UpdateEngineInterface in the fixed
+// object path, without an ObjectManager notifying the interfaces, since it is
+// all static and clients don't expect it to be implemented.
+class UpdateEngineAdaptor : public org::chromium::UpdateEngineInterfaceAdaptor {
+ public:
+ UpdateEngineAdaptor(SystemState* system_state,
+ const scoped_refptr<dbus::Bus>& bus);
+ ~UpdateEngineAdaptor() = default;
+
+ // Register the DBus object with the update engine service asynchronously.
+ // Calls |copmletion_callback| when done passing a boolean indicating if the
+ // registration succeeded.
+ void RegisterAsync(const base::Callback<void(bool)>& completion_callback);
+
+ // Takes ownership of the well-known DBus name and returns whether it
+ // succeeded.
+ bool RequestOwnership();
+
+ private:
+ scoped_refptr<dbus::Bus> bus_;
+ UpdateEngineService dbus_service_;
+ chromeos::dbus_utils::DBusObject dbus_object_;
};
-UpdateEngineService* update_engine_service_new(void);
-GType update_engine_service_get_type(void);
-
-// Methods
-
-gboolean update_engine_service_attempt_update(UpdateEngineService* self,
- gchar* app_version,
- gchar* omaha_url,
- GError **error);
-
-gboolean update_engine_service_attempt_update_with_flags(
- UpdateEngineService* self,
- gchar* app_version,
- gchar* omaha_url,
- gint flags_as_int,
- GError **error);
-
-gboolean update_engine_service_attempt_rollback(UpdateEngineService* self,
- gboolean powerwash,
- GError **error);
-
-// Checks if the system rollback is available by verifying if the secondary
-// system partition is valid and bootable.
-gboolean update_engine_service_can_rollback(
- UpdateEngineService* self,
- gboolean* out_can_rollback,
- GError **error);
-
-// Returns the name of kernel partition that can be rolled back into.
-gboolean update_engine_service_get_rollback_partition(
- UpdateEngineService* self,
- gchar** out_rollback_partition_name,
- GError **error);
-
-// Returns a list of available kernel partitions and whether each of them
-// can be booted from or not.
-gboolean update_engine_service_get_kernel_devices(UpdateEngineService* self,
- gchar** out_kernel_devices,
- GError **error);
-
-gboolean update_engine_service_reset_status(UpdateEngineService* self,
- GError **error);
-
-gboolean update_engine_service_get_status(UpdateEngineService* self,
- int64_t* last_checked_time,
- double* progress,
- gchar** current_operation,
- gchar** new_version,
- int64_t* new_size,
- GError **error);
-
-gboolean update_engine_service_reboot_if_needed(UpdateEngineService* self,
- GError **error);
-
-// Changes the current channel of the device to the target channel. If the
-// target channel is a less stable channel than the current channel, then the
-// channel change happens immediately (at the next update check). If the
-// target channel is a more stable channel, then if is_powerwash_allowed is set
-// to true, then also the change happens immediately but with a powerwash if
-// required. Otherwise, the change takes effect eventually (when the version on
-// the target channel goes above the version number of what the device
-// currently has).
-gboolean update_engine_service_set_channel(UpdateEngineService* self,
- gchar* target_channel,
- gboolean is_powerwash_allowed,
- GError **error);
-
-// If get_current_channel is set to true, populates |channel| with the name of
-// the channel that the device is currently on. Otherwise, it populates it with
-// the name of the channel the device is supposed to be (in case of a pending
-// channel change).
-gboolean update_engine_service_get_channel(UpdateEngineService* self,
- gboolean get_current_channel,
- gchar** channel,
- GError **error);
-
-// Enables or disables the sharing and consuming updates over P2P feature
-// according to the |enabled| argument passed.
-gboolean update_engine_service_set_p2p_update_permission(
- UpdateEngineService* self,
- gboolean enabled,
- GError **error);
-
-// Returns in |enabled| the current value for the P2P enabled setting. This
-// involves both sharing and consuming updates over P2P.
-gboolean update_engine_service_get_p2p_update_permission(
- UpdateEngineService* self,
- gboolean* enabled,
- GError **error);
-
-// If there's no device policy installed, sets the update over cellular networks
-// permission to the |allowed| value. Otherwise, this method returns with an
-// error since this setting is overridden by the applied policy.
-gboolean update_engine_service_set_update_over_cellular_permission(
- UpdateEngineService* self,
- gboolean allowed,
- GError **error);
-
-// Returns the current value of the update over cellular network setting, either
-// forced by the device policy if the device is enrolled or the current user
-// preference otherwise.
-gboolean update_engine_service_get_update_over_cellular_permission(
- UpdateEngineService* self,
- gboolean* allowed,
- GError **error);
-
-// Returns the duration since the last successful update, as the
-// duration on the wallclock. Returns an error if the device has not
-// updated.
-gboolean update_engine_service_get_duration_since_update(
- UpdateEngineService* self,
- gint64* out_usec_wallclock,
- GError **error);
-
-gboolean update_engine_service_emit_status_update(
- UpdateEngineService* self,
- gint64 last_checked_time,
- gdouble progress,
- const gchar* current_operation,
- const gchar* new_version,
- gint64 new_size);
-
-// Returns the version string of OS that was used before the last reboot
-// into an updated version. This is available only when rebooting into an
-// update from previous version, otherwise an empty string is returned.
-gboolean update_engine_service_get_prev_version(
- UpdateEngineService* self,
- gchar** prev_version,
- GError **error);
-
-G_END_DECLS
+} // namespace chromeos_update_engine
#endif // UPDATE_ENGINE_DBUS_SERVICE_H_
diff --git a/dbus_service_unittest.cc b/dbus_service_unittest.cc
new file mode 100644
index 0000000..441f89d
--- /dev/null
+++ b/dbus_service_unittest.cc
@@ -0,0 +1,130 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/dbus_service.h"
+
+#include <gtest/gtest.h>
+#include <string>
+
+#include <chromeos/errors/error.h>
+#include <policy/libpolicy.h>
+#include <policy/mock_device_policy.h>
+
+#include "update_engine/dbus_constants.h"
+#include "update_engine/fake_system_state.h"
+
+using std::string;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::_;
+
+using chromeos::errors::dbus::kDomain;
+
+namespace chromeos_update_engine {
+
+class UpdateEngineServiceTest : public ::testing::Test {
+ protected:
+ UpdateEngineServiceTest()
+ : mock_update_attempter_(fake_system_state_.mock_update_attempter()),
+ dbus_service_(&fake_system_state_) {}
+
+ void SetUp() override {
+ fake_system_state_.set_device_policy(nullptr);
+ }
+
+ // Fake/mock infrastructure.
+ FakeSystemState fake_system_state_;
+ policy::MockDevicePolicy mock_device_policy_;
+
+ // Shortcut for fake_system_state_.mock_update_attempter().
+ MockUpdateAttempter* mock_update_attempter_;
+
+ chromeos::ErrorPtr error_;
+ UpdateEngineService dbus_service_;
+};
+
+TEST_F(UpdateEngineServiceTest, AttemptUpdate) {
+ // Simple test to ensure that the default is an interactive check.
+ EXPECT_CALL(*mock_update_attempter_,
+ CheckForUpdate("app_ver", "url", true /* interactive */));
+ EXPECT_TRUE(dbus_service_.AttemptUpdate(&error_, "app_ver", "url"));
+ EXPECT_EQ(nullptr, error_);
+}
+
+TEST_F(UpdateEngineServiceTest, AttemptUpdateWithFlags) {
+ EXPECT_CALL(*mock_update_attempter_, CheckForUpdate(
+ "app_ver", "url", false /* interactive */));
+ // The update is non-interactive when we pass the non-interactive flag.
+ EXPECT_TRUE(dbus_service_.AttemptUpdateWithFlags(
+ &error_, "app_ver", "url", kAttemptUpdateFlagNonInteractive));
+ EXPECT_EQ(nullptr, error_);
+}
+
+// SetChannel is allowed when there's no device policy (the device is not
+// enterprise enrolled).
+TEST_F(UpdateEngineServiceTest, SetChannelWithNoPolicy) {
+ EXPECT_CALL(*mock_update_attempter_, RefreshDevicePolicy());
+ // If SetTargetChannel is called it means the policy check passed.
+ EXPECT_CALL(*fake_system_state_.mock_request_params(),
+ SetTargetChannel("stable-channel", true))
+ .WillOnce(Return(true));
+ EXPECT_TRUE(dbus_service_.SetChannel(&error_, "stable-channel", true));
+ ASSERT_EQ(nullptr, error_);
+}
+
+// When the policy is present, the delegated value should be checked.
+TEST_F(UpdateEngineServiceTest, SetChannelWithDelegatedPolicy) {
+ policy::MockDevicePolicy mock_device_policy;
+ fake_system_state_.set_device_policy(&mock_device_policy);
+ EXPECT_CALL(mock_device_policy, GetReleaseChannelDelegated(_))
+ .WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true)));
+ EXPECT_CALL(*fake_system_state_.mock_request_params(),
+ SetTargetChannel("beta-channel", true))
+ .WillOnce(Return(true));
+
+ EXPECT_TRUE(dbus_service_.SetChannel(&error_, "beta-channel", true));
+ ASSERT_EQ(nullptr, error_);
+}
+
+// When passing an invalid value (SetTargetChannel fails) an error should be
+// raised.
+TEST_F(UpdateEngineServiceTest, SetChannelWithInvalidChannel) {
+ EXPECT_CALL(*mock_update_attempter_, RefreshDevicePolicy());
+ EXPECT_CALL(*fake_system_state_.mock_request_params(),
+ SetTargetChannel("foo-channel", true)).WillOnce(Return(false));
+
+ EXPECT_FALSE(dbus_service_.SetChannel(&error_, "foo-channel", true));
+ ASSERT_NE(nullptr, error_);
+ EXPECT_TRUE(error_->HasError(kDomain, kUpdateEngineServiceErrorFailed));
+}
+
+TEST_F(UpdateEngineServiceTest, GetChannel) {
+ fake_system_state_.mock_request_params()->set_current_channel("current");
+ fake_system_state_.mock_request_params()->set_target_channel("target");
+ string channel;
+ EXPECT_TRUE(dbus_service_.GetChannel(
+ &error_, true /* get_current_channel */, &channel));
+ EXPECT_EQ(nullptr, error_);
+ EXPECT_EQ("current", channel);
+
+ EXPECT_TRUE(dbus_service_.GetChannel(
+ &error_, false /* get_current_channel */, &channel));
+ EXPECT_EQ(nullptr, error_);
+ EXPECT_EQ("target", channel);
+}
+
+TEST_F(UpdateEngineServiceTest, ResetStatusSucceeds) {
+ EXPECT_CALL(*mock_update_attempter_, ResetStatus()).WillOnce(Return(true));
+ EXPECT_TRUE(dbus_service_.ResetStatus(&error_));
+ EXPECT_EQ(nullptr, error_);
+}
+
+TEST_F(UpdateEngineServiceTest, ResetStatusFails) {
+ EXPECT_CALL(*mock_update_attempter_, ResetStatus()).WillOnce(Return(false));
+ EXPECT_FALSE(dbus_service_.ResetStatus(&error_));
+ ASSERT_NE(nullptr, error_);
+ EXPECT_TRUE(error_->HasError(kDomain, kUpdateEngineServiceErrorFailed));
+}
+
+} // namespace chromeos_update_engine
diff --git a/dbus_test_utils.h b/dbus_test_utils.h
new file mode 100644
index 0000000..99e6d5e
--- /dev/null
+++ b/dbus_test_utils.h
@@ -0,0 +1,77 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UPDATE_ENGINE_DBUS_TEST_UTILS_H_
+#define UPDATE_ENGINE_DBUS_TEST_UTILS_H_
+
+#include <set>
+#include <string>
+
+#include <base/bind.h>
+#include <chromeos/message_loops/message_loop.h>
+#include <gmock/gmock.h>
+
+namespace chromeos_update_engine {
+namespace dbus_test_utils {
+
+#define MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER( \
+ mock_signal_handler, mock_proxy, signal) \
+ do { \
+ EXPECT_CALL((mock_proxy), \
+ Register##signal##SignalHandler(::testing::_, ::testing::_)) \
+ .WillOnce(::chromeos_update_engine::dbus_test_utils::GrabCallbacks( \
+ &(mock_signal_handler))); \
+ } while (false)
+
+template <typename T>
+class MockSignalHandler {
+ public:
+ MockSignalHandler() = default;
+ ~MockSignalHandler() {
+ if (callback_connected_task_ != chromeos::MessageLoop::kTaskIdNull)
+ chromeos::MessageLoop::current()->CancelTask(callback_connected_task_);
+ }
+
+ // Returns whether the signal handler is registered.
+ bool IsHandlerRegistered() const { return signal_callback_ != nullptr; }
+
+ const base::Callback<T>& signal_callback() { return *signal_callback_.get(); }
+
+ void GrabCallbacks(
+ const base::Callback<T>& signal_callback,
+ dbus::ObjectProxy::OnConnectedCallback on_connected_callback) {
+ signal_callback_.reset(new base::Callback<T>(signal_callback));
+ on_connected_callback_.reset(
+ new dbus::ObjectProxy::OnConnectedCallback(on_connected_callback));
+ // Notify from the main loop that the callback was connected.
+ callback_connected_task_ = chromeos::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MockSignalHandler<T>::OnCallbackConnected,
+ base::Unretained(this)));
+ }
+
+ private:
+ void OnCallbackConnected() {
+ callback_connected_task_ = chromeos::MessageLoop::kTaskIdNull;
+ on_connected_callback_->Run("", "", true);
+ }
+
+ chromeos::MessageLoop::TaskId callback_connected_task_{
+ chromeos::MessageLoop::kTaskIdNull};
+
+ std::unique_ptr<base::Callback<T>> signal_callback_;
+ std::unique_ptr<dbus::ObjectProxy::OnConnectedCallback>
+ on_connected_callback_;
+};
+
+// Defines the action that will call MockSignalHandler<T>::GrabCallbacks for the
+// right type.
+ACTION_P(GrabCallbacks, mock_signal_handler) {
+ mock_signal_handler->GrabCallbacks(arg0, arg1);
+}
+
+} // namespace dbus_test_utils
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_DBUS_TEST_UTILS_H_
diff --git a/dbus_wrapper_interface.h b/dbus_wrapper_interface.h
deleted file mode 100644
index 57211c5..0000000
--- a/dbus_wrapper_interface.h
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UPDATE_ENGINE_DBUS_WRAPPER_INTERFACE_H_
-#define UPDATE_ENGINE_DBUS_WRAPPER_INTERFACE_H_
-
-// A mockable interface for DBus.
-
-#include <dbus/dbus.h>
-#include <dbus/dbus-glib.h>
-
-#ifndef DBUS_TYPE_G_OBJECT_PATH_ARRAY
-#define DBUS_TYPE_G_OBJECT_PATH_ARRAY \
- (dbus_g_type_get_collection("GPtrArray", DBUS_TYPE_G_OBJECT_PATH))
-#endif
-
-#ifndef DBUS_TYPE_G_STRING_ARRAY
-#define DBUS_TYPE_G_STRING_ARRAY \
- (dbus_g_type_get_collection("GPtrArray", G_TYPE_STRING))
-#endif
-
-namespace chromeos_update_engine {
-
-class DBusWrapperInterface {
- public:
- virtual ~DBusWrapperInterface() = default;
-
- // Wraps dbus_g_proxy_new_for_name().
- virtual DBusGProxy* ProxyNewForName(DBusGConnection* connection,
- const char* name,
- const char* path,
- const char* interface) = 0;
-
- // Wraps g_object_unref().
- virtual void ProxyUnref(DBusGProxy* proxy) = 0;
-
- // Wraps dbus_g_bus_get().
- virtual DBusGConnection* BusGet(DBusBusType type, GError** error) = 0;
-
- // Wraps dbus_g_proxy_call(). Since this is a variadic function without a
- // va_list equivalent, we have to list specific wrappers depending on the
- // number of input and output arguments, based on the required usage. Note,
- // however, that we do rely on automatic signature overriding to facilitate
- // different types of input/output arguments.
- virtual gboolean ProxyCall_0_1(DBusGProxy* proxy,
- const char* method,
- GError** error,
- GHashTable** out1) = 0;
-
- virtual gboolean ProxyCall_0_1(DBusGProxy* proxy,
- const char* method,
- GError** error,
- gint* out1) = 0;
-
- virtual gboolean ProxyCall_1_0(DBusGProxy* proxy,
- const char* method,
- GError** error,
- gint in1) = 0;
-
- virtual gboolean ProxyCall_3_0(DBusGProxy* proxy,
- const char* method,
- GError** error,
- const char* in1,
- const char* in2,
- const char* in3) = 0;
-
- // Wrappers for dbus_g_proxy_add_signal() (variadic).
- virtual void ProxyAddSignal_1(DBusGProxy* proxy,
- const char* signal_name,
- GType type1) = 0;
-
- virtual void ProxyAddSignal_2(DBusGProxy* proxy,
- const char* signal_name,
- GType type1,
- GType type2) = 0;
-
- // Wrapper for dbus_g_proxy_{connect,disconnect}_signal().
- virtual void ProxyConnectSignal(DBusGProxy* proxy,
- const char* signal_name,
- GCallback handler,
- void* data,
- GClosureNotify free_data_func) = 0;
- virtual void ProxyDisconnectSignal(DBusGProxy* proxy,
- const char* signal_name,
- GCallback handler,
- void* data) = 0;
-
- // Wraps dbus_g_connection_get_connection().
- virtual DBusConnection* ConnectionGetConnection(DBusGConnection* gbus) = 0;
-
- // Wraps dbus_bus_add_match().
- virtual void DBusBusAddMatch(DBusConnection* connection,
- const char* rule,
- DBusError* error) = 0;
-
- // Wraps dbus_connection_add_filter().
- virtual dbus_bool_t DBusConnectionAddFilter(
- DBusConnection* connection,
- DBusHandleMessageFunction function,
- void* user_data,
- DBusFreeFunction free_data_function) = 0;
-
- // Wraps dbus_connection_remove_filter().
- virtual void DBusConnectionRemoveFilter(DBusConnection* connection,
- DBusHandleMessageFunction function,
- void* user_data) = 0;
-
- // Wraps dbus_message_is_signal().
- virtual dbus_bool_t DBusMessageIsSignal(DBusMessage* message,
- const char* interface,
- const char* signal_name) = 0;
-
- // Wraps dbus_message_get_args(). Deploys the same approach for handling
- // variadic arguments as ProxyCall above.
- virtual dbus_bool_t DBusMessageGetArgs_3(DBusMessage* message,
- DBusError* error,
- char** out1,
- char** out2,
- char** out3) = 0;
-};
-
-} // namespace chromeos_update_engine
-
-#endif // UPDATE_ENGINE_DBUS_WRAPPER_INTERFACE_H_
diff --git a/delta_performer.cc b/delta_performer.cc
index 1006f17..bf6972f 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -58,7 +58,6 @@
const uint32_t kInPlaceMinorPayloadVersion = 1;
const uint32_t kSourceMinorPayloadVersion = 2;
-const size_t kDefaultChunkSize = 1024 * 1024;
namespace {
const int kUpdateStateOperationInvalid = -1;
@@ -129,12 +128,12 @@
string total_operations_str("?");
string completed_percentage_str("");
if (num_total_operations_) {
- total_operations_str = base::StringPrintf("%zu", num_total_operations_);
+ total_operations_str = std::to_string(num_total_operations_);
// Upcasting to 64-bit to avoid overflow, back to size_t for formatting.
completed_percentage_str =
base::StringPrintf(" (%" PRIu64 "%%)",
- IntRatio(next_operation_num_, num_total_operations_,
- 100));
+ IntRatio(next_operation_num_, num_total_operations_,
+ 100));
}
// Format download total count and percentage.
@@ -142,11 +141,11 @@
string payload_size_str("?");
string downloaded_percentage_str("");
if (payload_size) {
- payload_size_str = base::StringPrintf("%zu", payload_size);
+ payload_size_str = std::to_string(payload_size);
// Upcasting to 64-bit to avoid overflow, back to size_t for formatting.
downloaded_percentage_str =
base::StringPrintf(" (%" PRIu64 "%%)",
- IntRatio(total_bytes_received_, payload_size, 100));
+ IntRatio(total_bytes_received_, payload_size, 100));
}
LOG(INFO) << (message_prefix ? message_prefix : "") << next_operation_num_
@@ -551,11 +550,11 @@
const bool is_kernel_partition =
(next_operation_num_ >= num_rootfs_operations_);
- const DeltaArchiveManifest_InstallOperation &op =
+ const InstallOperation& op =
is_kernel_partition ?
- manifest_.kernel_install_operations(
- next_operation_num_ - num_rootfs_operations_) :
- manifest_.install_operations(next_operation_num_);
+ manifest_.kernel_install_operations(next_operation_num_ -
+ num_rootfs_operations_) :
+ manifest_.install_operations(next_operation_num_);
CopyDataToBuffer(&c_bytes, &count, op.data_length());
@@ -592,23 +591,21 @@
ScopedTerminatorExitUnblocker(); // Avoids a compiler unused var bug.
bool op_result;
- if (op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
- op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ)
+ if (op.type() == InstallOperation::REPLACE ||
+ op.type() == InstallOperation::REPLACE_BZ)
op_result = HandleOpResult(
PerformReplaceOperation(op, is_kernel_partition), "replace", error);
- else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE)
+ else if (op.type() == InstallOperation::MOVE)
op_result = HandleOpResult(
PerformMoveOperation(op, is_kernel_partition), "move", error);
- else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_BSDIFF)
+ else if (op.type() == InstallOperation::BSDIFF)
op_result = HandleOpResult(
PerformBsdiffOperation(op, is_kernel_partition), "bsdiff", error);
- else if (op.type() ==
- DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY)
+ else if (op.type() == InstallOperation::SOURCE_COPY)
op_result =
HandleOpResult(PerformSourceCopyOperation(op, is_kernel_partition),
"source_copy", error);
- else if (op.type() ==
- DeltaArchiveManifest_InstallOperation_Type_SOURCE_BSDIFF)
+ else if (op.type() == InstallOperation::SOURCE_BSDIFF)
op_result =
HandleOpResult(PerformSourceBsdiffOperation(op, is_kernel_partition),
"source_bsdiff", error);
@@ -630,13 +627,11 @@
}
bool DeltaPerformer::CanPerformInstallOperation(
- const chromeos_update_engine::DeltaArchiveManifest_InstallOperation&
- operation) {
+ const chromeos_update_engine::InstallOperation& operation) {
// Move and source_copy operations don't require any data blob, so they can
// always be performed.
- if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE ||
- operation.type() ==
- DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY)
+ if (operation.type() == InstallOperation::MOVE ||
+ operation.type() == InstallOperation::SOURCE_COPY)
return true;
// See if we have the entire data blob in the buffer
@@ -649,13 +644,10 @@
buffer_offset_ + buffer_.size());
}
-bool DeltaPerformer::PerformReplaceOperation(
- const DeltaArchiveManifest_InstallOperation& operation,
- bool is_kernel_partition) {
- CHECK(operation.type() == \
- DeltaArchiveManifest_InstallOperation_Type_REPLACE || \
- operation.type() == \
- DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
+bool DeltaPerformer::PerformReplaceOperation(const InstallOperation& operation,
+ bool is_kernel_partition) {
+ CHECK(operation.type() == InstallOperation::REPLACE ||
+ operation.type() == InstallOperation::REPLACE_BZ);
// Since we delete data off the beginning of the buffer as we use it,
// the data we need should be exactly at the beginning of the buffer.
@@ -672,10 +664,9 @@
// Since bzip decompression is optional, we have a variable writer that will
// point to one of the ExtentWriter objects above.
ExtentWriter* writer = nullptr;
- if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
+ if (operation.type() == InstallOperation::REPLACE) {
writer = &zero_pad_writer;
- } else if (operation.type() ==
- DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
+ } else if (operation.type() == InstallOperation::REPLACE_BZ) {
bzip_writer.reset(new BzipExtentWriter(&zero_pad_writer));
writer = bzip_writer.get();
} else {
@@ -699,9 +690,8 @@
return true;
}
-bool DeltaPerformer::PerformMoveOperation(
- const DeltaArchiveManifest_InstallOperation& operation,
- bool is_kernel_partition) {
+bool DeltaPerformer::PerformMoveOperation(const InstallOperation& operation,
+ bool is_kernel_partition) {
// Calculate buffer size. Note, this function doesn't do a sliding
// window to copy in case the source and destination blocks overlap.
// If we wanted to do a sliding window, we could program the server
@@ -778,7 +768,7 @@
} // namespace
bool DeltaPerformer::PerformSourceCopyOperation(
- const DeltaArchiveManifest_InstallOperation& operation,
+ const InstallOperation& operation,
bool is_kernel_partition) {
if (operation.has_src_length())
TEST_AND_RETURN_FALSE(operation.src_length() % block_size_ == 0);
@@ -854,9 +844,8 @@
return true;
}
-bool DeltaPerformer::PerformBsdiffOperation(
- const DeltaArchiveManifest_InstallOperation& operation,
- bool is_kernel_partition) {
+bool DeltaPerformer::PerformBsdiffOperation(const InstallOperation& operation,
+ bool is_kernel_partition) {
// Since we delete data off the beginning of the buffer as we use it,
// the data we need should be exactly at the beginning of the buffer.
TEST_AND_RETURN_FALSE(buffer_offset_ == operation.data_offset());
@@ -895,10 +884,8 @@
int return_code = 0;
TEST_AND_RETURN_FALSE(
- Subprocess::SynchronousExecFlags(cmd,
- G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
- &return_code,
- nullptr));
+ Subprocess::SynchronousExecFlags(cmd, Subprocess::kSearchPath,
+ &return_code, nullptr));
TEST_AND_RETURN_FALSE(return_code == 0);
if (operation.dst_length() % block_size_) {
@@ -919,7 +906,7 @@
}
bool DeltaPerformer::PerformSourceBsdiffOperation(
- const DeltaArchiveManifest_InstallOperation& operation,
+ const InstallOperation& operation,
bool is_kernel_partition) {
// Since we delete data off the beginning of the buffer as we use it,
// the data we need should be exactly at the beginning of the buffer.
@@ -966,17 +953,15 @@
int return_code = 0;
TEST_AND_RETURN_FALSE(
- Subprocess::SynchronousExecFlags(cmd,
- G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
- &return_code,
- nullptr));
+ Subprocess::SynchronousExecFlags(cmd, Subprocess::kSearchPath,
+ &return_code, nullptr));
TEST_AND_RETURN_FALSE(return_code == 0);
return true;
}
bool DeltaPerformer::ExtractSignatureMessage(
- const DeltaArchiveManifest_InstallOperation& operation) {
- if (operation.type() != DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
+ const InstallOperation& operation) {
+ if (operation.type() != InstallOperation::REPLACE ||
!manifest_.has_signatures_offset() ||
manifest_.signatures_offset() != operation.data_offset()) {
return false;
@@ -1142,8 +1127,7 @@
}
ErrorCode DeltaPerformer::ValidateOperationHash(
- const DeltaArchiveManifest_InstallOperation& operation) {
-
+ const InstallOperation& operation) {
if (!operation.data_sha256_hash().size()) {
if (!operation.data_length()) {
// Operations that do not have any data blob won't have any operation hash
@@ -1468,11 +1452,11 @@
if (next_operation_num_ < num_total_operations_) {
const bool is_kernel_partition =
next_operation_num_ >= num_rootfs_operations_;
- const DeltaArchiveManifest_InstallOperation &op =
- is_kernel_partition ?
- manifest_.kernel_install_operations(
- next_operation_num_ - num_rootfs_operations_) :
- manifest_.install_operations(next_operation_num_);
+ const InstallOperation& op =
+ is_kernel_partition
+ ? manifest_.kernel_install_operations(next_operation_num_ -
+ num_rootfs_operations_)
+ : manifest_.install_operations(next_operation_num_);
TEST_AND_RETURN_FALSE(prefs_->SetInt64(kPrefsUpdateStateNextDataLength,
op.data_length()));
} else {
diff --git a/delta_performer.h b/delta_performer.h
index ba479af..bc48f5d 100644
--- a/delta_performer.h
+++ b/delta_performer.h
@@ -30,9 +30,6 @@
// The minor version used by the A to B delta generator algorithm.
extern const uint32_t kSourceMinorPayloadVersion;
-// Chunk size used for payloads during test.
-extern const size_t kDefaultChunkSize;
-
class PrefsInterface;
// This class performs the actions in a delta update synchronously. The delta
@@ -238,8 +235,7 @@
// Returns true if enough of the delta file has been passed via Write()
// to be able to perform a given install operation.
- bool CanPerformInstallOperation(
- const DeltaArchiveManifest_InstallOperation& operation);
+ bool CanPerformInstallOperation(const InstallOperation& operation);
// Checks the integrity of the payload manifest. Returns true upon success,
// false otherwise.
@@ -248,8 +244,7 @@
// Validates that the hash of the blobs corresponding to the given |operation|
// matches what's specified in the manifest in the payload.
// Returns ErrorCode::kSuccess on match or a suitable error code otherwise.
- ErrorCode ValidateOperationHash(
- const DeltaArchiveManifest_InstallOperation& operation);
+ ErrorCode ValidateOperationHash(const InstallOperation& operation);
// Interprets the given |protobuf| as a DeltaArchiveManifest protocol buffer
// of the given protobuf_length and verifies that the signed hash of the
@@ -263,30 +258,23 @@
uint64_t protobuf_length);
// Returns true on success.
- bool PerformInstallOperation(
- const DeltaArchiveManifest_InstallOperation& operation);
+ bool PerformInstallOperation(const InstallOperation& operation);
// These perform a specific type of operation and return true on success.
- bool PerformReplaceOperation(
- const DeltaArchiveManifest_InstallOperation& operation,
- bool is_kernel_partition);
- bool PerformMoveOperation(
- const DeltaArchiveManifest_InstallOperation& operation,
- bool is_kernel_partition);
- bool PerformBsdiffOperation(
- const DeltaArchiveManifest_InstallOperation& operation,
- bool is_kernel_partition);
- bool PerformSourceCopyOperation(
- const DeltaArchiveManifest_InstallOperation& operation,
- bool is_kernel_partition);
- bool PerformSourceBsdiffOperation(
- const DeltaArchiveManifest_InstallOperation& operation,
- bool is_kernel_partition);
+ bool PerformReplaceOperation(const InstallOperation& operation,
+ bool is_kernel_partition);
+ bool PerformMoveOperation(const InstallOperation& operation,
+ bool is_kernel_partition);
+ bool PerformBsdiffOperation(const InstallOperation& operation,
+ bool is_kernel_partition);
+ bool PerformSourceCopyOperation(const InstallOperation& operation,
+ bool is_kernel_partition);
+ bool PerformSourceBsdiffOperation(const InstallOperation& operation,
+ bool is_kernel_partition);
// Returns true if the payload signature message has been extracted from
// |operation|, false otherwise.
- bool ExtractSignatureMessage(
- const DeltaArchiveManifest_InstallOperation& operation);
+ bool ExtractSignatureMessage(const InstallOperation& operation);
// Updates the hash calculator with the bytes in |buffer_|. Then discard the
// content, ensuring that memory is being deallocated. If |do_advance_offset|,
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index 5737422..a0cb72f 100644
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -62,7 +62,7 @@
string a_img;
string b_img;
string result_img;
- int image_size;
+ size_t image_size;
string delta_path;
uint64_t metadata_size;
@@ -75,6 +75,7 @@
string result_kernel;
chromeos::Blob result_kernel_data;
+ size_t kernel_size;
// The in-memory copy of delta file.
chromeos::Blob delta;
@@ -138,19 +139,27 @@
}
};
-static void CompareFilesByBlock(const string& a_file, const string& b_file) {
+static void CompareFilesByBlock(const string& a_file, const string& b_file,
+ size_t image_size) {
+ EXPECT_EQ(0, image_size % kBlockSize);
+
chromeos::Blob a_data, b_data;
EXPECT_TRUE(utils::ReadFile(a_file, &a_data)) << "file failed: " << a_file;
EXPECT_TRUE(utils::ReadFile(b_file, &b_data)) << "file failed: " << b_file;
- EXPECT_EQ(a_data.size(), b_data.size());
- EXPECT_EQ(0, a_data.size() % kBlockSize);
- for (size_t i = 0; i < a_data.size(); i += kBlockSize) {
+ EXPECT_GE(a_data.size(), image_size);
+ EXPECT_GE(b_data.size(), image_size);
+ for (size_t i = 0; i < image_size; i += kBlockSize) {
EXPECT_EQ(0, i % kBlockSize);
chromeos::Blob a_sub(&a_data[i], &a_data[i + kBlockSize]);
chromeos::Blob b_sub(&b_data[i], &b_data[i + kBlockSize]);
EXPECT_TRUE(a_sub == b_sub) << "Block " << (i/kBlockSize) << " differs";
}
+ if (::testing::Test::HasNonfatalFailure()) {
+ LOG(INFO) << "Compared filesystems with size " << image_size
+ << ", partition A " << a_file << " size: " << a_data.size()
+ << ", partition B " << b_file << " size: " << b_data.size();
+ }
}
static bool WriteSparseFile(const string& path, off_t size) {
@@ -305,7 +314,7 @@
static void GenerateDeltaFile(bool full_kernel,
bool full_rootfs,
bool noop,
- off_t chunk_size,
+ ssize_t chunk_size,
SignatureTest signature_test,
DeltaState *state,
uint32_t minor_version) {
@@ -318,13 +327,11 @@
utils::MakeTempFile("result_img.XXXXXX", &state->result_img, nullptr));
test_utils::CreateExtImageAtPath(state->a_img, nullptr);
- state->image_size = static_cast<int>(utils::FileSize(state->a_img));
+ state->image_size = utils::FileSize(state->a_img);
// Extend the "partitions" holding the file system a bit.
- EXPECT_EQ(0, System(base::StringPrintf(
- "dd if=/dev/zero of=%s seek=%d bs=1 count=1 status=none",
- state->a_img.c_str(),
- state->image_size + 1024 * 1024 - 1)));
+ EXPECT_EQ(0, HANDLE_EINTR(truncate(state->a_img.c_str(),
+ state->image_size + 1024 * 1024)));
EXPECT_EQ(state->image_size + 1024 * 1024, utils::FileSize(state->a_img));
// Create ImageInfo A & B
@@ -405,10 +412,8 @@
}
test_utils::CreateExtImageAtPath(state->b_img, nullptr);
- EXPECT_EQ(0, System(base::StringPrintf(
- "dd if=/dev/zero of=%s seek=%d bs=1 count=1 status=none",
- state->b_img.c_str(),
- state->image_size + 1024 * 1024 - 1)));
+ EXPECT_EQ(0, HANDLE_EINTR(truncate(state->b_img.c_str(),
+ state->image_size + 1024 * 1024)));
EXPECT_EQ(state->image_size + 1024 * 1024, utils::FileSize(state->b_img));
// Make some changes to the B image.
@@ -487,6 +492,7 @@
&state->result_kernel,
nullptr));
+ state->kernel_size = kDefaultKernelSize;
state->old_kernel_data.resize(kDefaultKernelSize);
state->new_kernel_data.resize(state->old_kernel_data.size());
state->result_kernel_data.resize(state->old_kernel_data.size());
@@ -523,7 +529,7 @@
PayloadGenerationConfig payload_config;
payload_config.is_delta = !full_rootfs;
- payload_config.chunk_size = chunk_size;
+ payload_config.hard_chunk_size = chunk_size;
payload_config.rootfs_partition_size = kRootFSPartitionSize;
payload_config.minor_version = minor_version;
if (!full_rootfs) {
@@ -535,8 +541,9 @@
EXPECT_TRUE(payload_config.source.rootfs.OpenFilesystem());
EXPECT_TRUE(payload_config.source.kernel.OpenFilesystem());
} else {
- if (payload_config.chunk_size == -1)
- payload_config.chunk_size = kDefaultChunkSize;
+ if (payload_config.hard_chunk_size == -1)
+ // Use 1 MiB chunk size for the full unittests.
+ payload_config.hard_chunk_size = 1024 * 1024;
}
payload_config.target.rootfs.path = state->b_img;
payload_config.target.kernel.path = state->new_kernel;
@@ -841,13 +848,17 @@
chromeos::Blob updated_kernel_partition;
if (minor_version == kSourceMinorPayloadVersion) {
- CompareFilesByBlock(state->result_kernel, state->new_kernel);
- CompareFilesByBlock(state->result_img, state->b_img);
+ CompareFilesByBlock(state->result_kernel, state->new_kernel,
+ state->kernel_size);
+ CompareFilesByBlock(state->result_img, state->b_img,
+ state->image_size);
EXPECT_TRUE(utils::ReadFile(state->result_kernel,
&updated_kernel_partition));
} else {
- CompareFilesByBlock(state->old_kernel, state->new_kernel);
- CompareFilesByBlock(state->a_img, state->b_img);
+ CompareFilesByBlock(state->old_kernel, state->new_kernel,
+ state->kernel_size);
+ CompareFilesByBlock(state->a_img, state->b_img,
+ state->image_size);
EXPECT_TRUE(utils::ReadFile(state->old_kernel, &updated_kernel_partition));
}
@@ -896,7 +907,7 @@
}
void DoSmallImageTest(bool full_kernel, bool full_rootfs, bool noop,
- off_t chunk_size,
+ ssize_t chunk_size,
SignatureTest signature_test,
bool hash_checks_mandatory, uint32_t minor_version) {
DeltaState state;
diff --git a/download_action_unittest.cc b/download_action_unittest.cc
index 254dbd7..a8306f3 100644
--- a/download_action_unittest.cc
+++ b/download_action_unittest.cc
@@ -4,7 +4,6 @@
#include "update_engine/download_action.h"
-#include <glib.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
diff --git a/fake_p2p_manager_configuration.h b/fake_p2p_manager_configuration.h
index 1dc7c6e..806206c 100644
--- a/fake_p2p_manager_configuration.h
+++ b/fake_p2p_manager_configuration.h
@@ -47,7 +47,7 @@
size_t minimum_size) override {
std::vector<std::string> formatted_command = p2p_client_cmd_format_;
// Replace {variable} on the passed string.
- std::string str_minimum_size = base::SizeTToString(minimum_size);
+ std::string str_minimum_size = std::to_string(minimum_size);
for (std::string& arg : formatted_command) {
ReplaceSubstringsAfterOffset(&arg, 0, "{file_id}", file_id);
ReplaceSubstringsAfterOffset(&arg, 0, "{minsize}", str_minimum_size);
diff --git a/fake_shill_proxy.cc b/fake_shill_proxy.cc
new file mode 100644
index 0000000..e38784f
--- /dev/null
+++ b/fake_shill_proxy.cc
@@ -0,0 +1,37 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/fake_shill_proxy.h"
+
+#include "update_engine/dbus_proxies.h"
+
+using org::chromium::flimflam::ManagerProxyMock;
+using org::chromium::flimflam::ServiceProxyInterface;
+
+namespace chromeos_update_engine {
+
+FakeShillProxy::FakeShillProxy()
+ : manager_proxy_mock_(new ManagerProxyMock()) {}
+
+ManagerProxyMock* FakeShillProxy::GetManagerProxy() {
+ return manager_proxy_mock_.get();
+}
+
+std::unique_ptr<ServiceProxyInterface> FakeShillProxy::GetServiceForPath(
+ const std::string& path) {
+ auto it = service_proxy_mocks_.find(path);
+ CHECK(it != service_proxy_mocks_.end()) << "No ServiceProxyMock set for "
+ << path;
+ std::unique_ptr<ServiceProxyInterface> result = std::move(it->second);
+ service_proxy_mocks_.erase(it);
+ return result;
+}
+
+void FakeShillProxy::SetServiceForPath(
+ const std::string& path,
+ std::unique_ptr<ServiceProxyInterface> service_proxy) {
+ service_proxy_mocks_[path] = std::move(service_proxy);
+}
+
+} // namespace chromeos_update_engine
diff --git a/fake_shill_proxy.h b/fake_shill_proxy.h
new file mode 100644
index 0000000..a60e996
--- /dev/null
+++ b/fake_shill_proxy.h
@@ -0,0 +1,54 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UPDATE_ENGINE_FAKE_SHILL_PROXY_H_
+#define UPDATE_ENGINE_FAKE_SHILL_PROXY_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include <base/macros.h>
+
+#include "update_engine/dbus_mocks.h"
+#include "update_engine/dbus_proxies.h"
+#include "update_engine/shill_proxy_interface.h"
+
+namespace chromeos_update_engine {
+
+// This class implements the connection to shill using real DBus calls.
+class FakeShillProxy : public ShillProxyInterface {
+ public:
+ FakeShillProxy();
+ ~FakeShillProxy() override = default;
+
+ // ShillProxyInterface overrides.
+
+ // GetManagerProxy returns the subclass ManagerProxyMock so tests can easily
+ // use it. Mocks for the return value of GetServiceForPath() can be provided
+ // with SetServiceForPath().
+ org::chromium::flimflam::ManagerProxyMock* GetManagerProxy() override;
+ std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface>
+ GetServiceForPath(const std::string& path) override;
+
+ // Sets the service_proxy that will be returned by GetServiceForPath().
+ void SetServiceForPath(
+ const std::string& path,
+ std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface>
+ service_proxy);
+
+ private:
+ std::unique_ptr<org::chromium::flimflam::ManagerProxyMock>
+ manager_proxy_mock_;
+
+ std::map<std::string,
+ std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface>>
+ service_proxy_mocks_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeShillProxy);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_FAKE_SHILL_PROXY_H_
diff --git a/fake_system_state.cc b/fake_system_state.cc
index 8747a59..6f1bd02 100644
--- a/fake_system_state.cc
+++ b/fake_system_state.cc
@@ -9,23 +9,22 @@
// Mock the SystemStateInterface so that we could lie that
// OOBE is completed even when there's no such marker file, etc.
FakeSystemState::FakeSystemState()
- : mock_connection_manager_(this),
- mock_update_attempter_(this, &dbus_),
- mock_request_params_(this),
- fake_update_manager_(&fake_clock_),
- clock_(&fake_clock_),
- connection_manager_(&mock_connection_manager_),
- hardware_(&fake_hardware_),
- metrics_lib_(&mock_metrics_lib_),
- prefs_(&mock_prefs_),
- powerwash_safe_prefs_(&mock_powerwash_safe_prefs_),
- payload_state_(&mock_payload_state_),
- update_attempter_(&mock_update_attempter_),
- request_params_(&mock_request_params_),
- p2p_manager_(&mock_p2p_manager_),
- update_manager_(&fake_update_manager_),
- device_policy_(nullptr),
- fake_system_rebooted_(false) {
+ : mock_update_attempter_(this, nullptr, nullptr),
+ mock_request_params_(this),
+ fake_update_manager_(&fake_clock_),
+ clock_(&fake_clock_),
+ connection_manager_(&mock_connection_manager_),
+ hardware_(&fake_hardware_),
+ metrics_lib_(&mock_metrics_lib_),
+ prefs_(&mock_prefs_),
+ powerwash_safe_prefs_(&mock_powerwash_safe_prefs_),
+ payload_state_(&mock_payload_state_),
+ update_attempter_(&mock_update_attempter_),
+ request_params_(&mock_request_params_),
+ p2p_manager_(&mock_p2p_manager_),
+ update_manager_(&fake_update_manager_),
+ device_policy_(nullptr),
+ fake_system_rebooted_(false) {
mock_payload_state_.Initialize(this);
mock_update_attempter_.Init();
}
diff --git a/fake_system_state.h b/fake_system_state.h
index 84c37fc..47c27e4 100644
--- a/fake_system_state.h
+++ b/fake_system_state.h
@@ -10,10 +10,11 @@
#include <policy/mock_device_policy.h>
#include "metrics/metrics_library_mock.h"
+#include "update_engine/dbus_mocks.h"
+#include "update_engine/dbus_proxies.h"
#include "update_engine/fake_clock.h"
#include "update_engine/fake_hardware.h"
#include "update_engine/mock_connection_manager.h"
-#include "update_engine/mock_dbus_wrapper.h"
#include "update_engine/mock_omaha_request_params.h"
#include "update_engine/mock_p2p_manager.h"
#include "update_engine/mock_payload_state.h"
@@ -45,7 +46,7 @@
return device_policy_;
}
- inline ConnectionManager* connection_manager() override {
+ inline ConnectionManagerInterface* connection_manager() override {
return connection_manager_;
}
@@ -79,6 +80,11 @@
return update_manager_;
}
+ inline org::chromium::PowerManagerProxyInterface* power_manager_proxy()
+ override {
+ return power_manager_proxy_;
+ }
+
inline bool system_rebooted() override { return fake_system_rebooted_; }
// Setters for the various members, can be used for overriding the default
@@ -89,7 +95,8 @@
clock_ = clock ? clock : &fake_clock_;
}
- inline void set_connection_manager(ConnectionManager* connection_manager) {
+ inline void set_connection_manager(
+ ConnectionManagerInterface* connection_manager) {
connection_manager_ = (connection_manager ? connection_manager :
&mock_connection_manager_);
}
@@ -211,11 +218,12 @@
testing::NiceMock<MockOmahaRequestParams> mock_request_params_;
testing::NiceMock<MockP2PManager> mock_p2p_manager_;
chromeos_update_manager::FakeUpdateManager fake_update_manager_;
+ org::chromium::PowerManagerProxyMock mock_power_manager_;
// Pointers to objects that client code can override. They are initialized to
// the default implementations above.
ClockInterface* clock_;
- ConnectionManager* connection_manager_;
+ ConnectionManagerInterface* connection_manager_;
HardwareInterface* hardware_;
MetricsLibraryInterface* metrics_lib_;
PrefsInterface* prefs_;
@@ -225,12 +233,13 @@
OmahaRequestParams* request_params_;
P2PManager* p2p_manager_;
chromeos_update_manager::UpdateManager* update_manager_;
+ org::chromium::PowerManagerProxyInterface* power_manager_proxy_{
+ &mock_power_manager_};
// Other object pointers (not preinitialized).
const policy::DevicePolicy* device_policy_;
// Other data members.
- MockDBusWrapper dbus_;
bool fake_system_rebooted_;
};
diff --git a/filesystem_verifier_action.cc b/filesystem_verifier_action.cc
index 70156d9..d3e9fe3 100644
--- a/filesystem_verifier_action.cc
+++ b/filesystem_verifier_action.cc
@@ -14,14 +14,12 @@
#include <string>
#include <base/bind.h>
-#include <base/posix/eintr_wrapper.h>
+#include <chromeos/streams/file_stream.h>
#include "update_engine/hardware_interface.h"
-#include "update_engine/subprocess.h"
#include "update_engine/system_state.h"
#include "update_engine/utils.h"
-using chromeos::MessageLoop;
using std::string;
namespace chromeos_update_engine {
@@ -98,23 +96,23 @@
break;
}
- src_fd_ = HANDLE_EINTR(open(target_path.c_str(), O_RDONLY));
- if (src_fd_ < 0) {
- PLOG(ERROR) << "Unable to open " << target_path << " for reading";
+ chromeos::ErrorPtr error;
+ src_stream_ = chromeos::FileStream::Open(
+ base::FilePath(target_path),
+ chromeos::Stream::AccessMode::READ,
+ chromeos::FileStream::Disposition::OPEN_EXISTING,
+ &error);
+
+ if (!src_stream_) {
+ LOG(ERROR) << "Unable to open " << target_path << " for reading";
return;
}
- DetermineFilesystemSize(src_fd_);
+ DetermineFilesystemSize(target_path);
buffer_.resize(kReadFileBufferSize);
// Start the first read.
- read_task_ = MessageLoop::current()->WatchFileDescriptor(
- FROM_HERE,
- src_fd_,
- MessageLoop::WatchMode::kWatchRead,
- true, // persistent
- base::Bind(&FilesystemVerifierAction::OnReadReadyCallback,
- base::Unretained(this)));
+ ScheduleRead();
abort_action_completer.set_should_complete(false);
}
@@ -125,19 +123,11 @@
}
bool FilesystemVerifierAction::IsCleanupPending() const {
- return (src_fd_ != -1);
+ return src_stream_ != nullptr;
}
void FilesystemVerifierAction::Cleanup(ErrorCode code) {
- MessageLoop::current()->CancelTask(read_task_);
- read_task_ = MessageLoop::kTaskIdNull;
-
- if (src_fd_ != -1) {
- if (IGNORE_EINTR(close(src_fd_)) != 0) {
- PLOG(ERROR) << "Error closing fd " << src_fd_;
- }
- src_fd_ = -1;
- }
+ src_stream_.reset();
// This memory is not used anymore.
buffer_.clear();
@@ -148,74 +138,97 @@
processor_->ActionComplete(this, code);
}
-void FilesystemVerifierAction::OnReadReadyCallback() {
+void FilesystemVerifierAction::ScheduleRead() {
size_t bytes_to_read = std::min(static_cast<int64_t>(buffer_.size()),
remaining_size_);
-
- ssize_t bytes_read = 0;
- if (bytes_to_read) {
- bytes_read = HANDLE_EINTR(read(src_fd_, buffer_.data(), bytes_to_read));
- }
- if (bytes_read < 0) {
- PLOG(ERROR) << "Read failed";
- failed_ = true;
- } else if (bytes_read == 0) {
- read_done_ = true;
- } else {
- remaining_size_ -= bytes_read;
- }
-
- if (bytes_read > 0) {
- CHECK(!read_done_);
- if (!hasher_.Update(buffer_.data(), bytes_read)) {
- LOG(ERROR) << "Unable to update the hash.";
- failed_ = true;
- }
- }
-
- CheckTerminationConditions();
-}
-
-void FilesystemVerifierAction::CheckTerminationConditions() {
- if (failed_ || cancelled_) {
- Cleanup(ErrorCode::kError);
+ if (!bytes_to_read) {
+ OnReadDoneCallback(0);
return;
}
- if (read_done_) {
- // We're done!
- ErrorCode code = ErrorCode::kSuccess;
- if (hasher_.Finalize()) {
- LOG(INFO) << "Hash: " << hasher_.hash();
- switch (partition_type_) {
- case PartitionType::kRootfs:
- if (install_plan_.rootfs_hash != hasher_.raw_hash()) {
- code = ErrorCode::kNewRootfsVerificationError;
- LOG(ERROR) << "New rootfs verification failed.";
- }
- break;
- case PartitionType::kKernel:
- if (install_plan_.kernel_hash != hasher_.raw_hash()) {
- code = ErrorCode::kNewKernelVerificationError;
- LOG(ERROR) << "New kernel verification failed.";
- }
- break;
- case PartitionType::kSourceRootfs:
- install_plan_.source_rootfs_hash = hasher_.raw_hash();
- break;
- case PartitionType::kSourceKernel:
- install_plan_.source_kernel_hash = hasher_.raw_hash();
- break;
- }
- } else {
- LOG(ERROR) << "Unable to finalize the hash.";
- code = ErrorCode::kError;
- }
- Cleanup(code);
+ bool read_async_ok = src_stream_->ReadAsync(
+ buffer_.data(),
+ bytes_to_read,
+ base::Bind(&FilesystemVerifierAction::OnReadDoneCallback,
+ base::Unretained(this)),
+ base::Bind(&FilesystemVerifierAction::OnReadErrorCallback,
+ base::Unretained(this)),
+ nullptr);
+
+ if (!read_async_ok) {
+ LOG(ERROR) << "Unable to schedule an asynchronous read from the stream.";
+ Cleanup(ErrorCode::kError);
}
}
-void FilesystemVerifierAction::DetermineFilesystemSize(int fd) {
+void FilesystemVerifierAction::OnReadDoneCallback(size_t bytes_read) {
+ if (bytes_read == 0) {
+ read_done_ = true;
+ } else {
+ remaining_size_ -= bytes_read;
+ CHECK(!read_done_);
+ if (!hasher_.Update(buffer_.data(), bytes_read)) {
+ LOG(ERROR) << "Unable to update the hash.";
+ Cleanup(ErrorCode::kError);
+ return;
+ }
+ }
+
+ // We either terminate the action or have more data to read.
+ if (!CheckTerminationConditions())
+ ScheduleRead();
+}
+
+void FilesystemVerifierAction::OnReadErrorCallback(
+ const chromeos::Error* error) {
+ // TODO(deymo): Transform the read-error into an specific ErrorCode.
+ LOG(ERROR) << "Asynchronous read failed.";
+ Cleanup(ErrorCode::kError);
+}
+
+bool FilesystemVerifierAction::CheckTerminationConditions() {
+ if (cancelled_) {
+ Cleanup(ErrorCode::kError);
+ return true;
+ }
+
+ if (!read_done_)
+ return false;
+
+ // We're done!
+ ErrorCode code = ErrorCode::kSuccess;
+ if (hasher_.Finalize()) {
+ LOG(INFO) << "Hash: " << hasher_.hash();
+ switch (partition_type_) {
+ case PartitionType::kRootfs:
+ if (install_plan_.rootfs_hash != hasher_.raw_hash()) {
+ code = ErrorCode::kNewRootfsVerificationError;
+ LOG(ERROR) << "New rootfs verification failed.";
+ }
+ break;
+ case PartitionType::kKernel:
+ if (install_plan_.kernel_hash != hasher_.raw_hash()) {
+ code = ErrorCode::kNewKernelVerificationError;
+ LOG(ERROR) << "New kernel verification failed.";
+ }
+ break;
+ case PartitionType::kSourceRootfs:
+ install_plan_.source_rootfs_hash = hasher_.raw_hash();
+ break;
+ case PartitionType::kSourceKernel:
+ install_plan_.source_kernel_hash = hasher_.raw_hash();
+ break;
+ }
+ } else {
+ LOG(ERROR) << "Unable to finalize the hash.";
+ code = ErrorCode::kError;
+ }
+ Cleanup(code);
+ return true;
+}
+
+void FilesystemVerifierAction::DetermineFilesystemSize(
+ const std::string& path) {
switch (partition_type_) {
case PartitionType::kRootfs:
remaining_size_ = install_plan_.rootfs_size;
@@ -228,7 +241,7 @@
case PartitionType::kSourceRootfs:
{
int block_count = 0, block_size = 0;
- if (utils::GetFilesystemSizeFromFD(fd, &block_count, &block_size)) {
+ if (utils::GetFilesystemSize(path, &block_count, &block_size)) {
remaining_size_ = static_cast<int64_t>(block_count) * block_size;
LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes ("
<< block_count << "x" << block_size << ").";
@@ -238,7 +251,6 @@
default:
break;
}
- return;
}
} // namespace chromeos_update_engine
diff --git a/filesystem_verifier_action.h b/filesystem_verifier_action.h
index 69e4972..e6c1908 100644
--- a/filesystem_verifier_action.h
+++ b/filesystem_verifier_action.h
@@ -11,7 +11,7 @@
#include <string>
#include <vector>
-#include <chromeos/message_loops/message_loop.h>
+#include <chromeos/streams/stream.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
#include "update_engine/action.h"
@@ -44,7 +44,7 @@
// Used for testing. Return true if Cleanup() has not yet been called due
// to a callback upon the completion or cancellation of the verifier action.
// A test should wait until IsCleanupPending() returns false before
- // terminating the glib main loop.
+ // terminating the main loop.
bool IsCleanupPending() const;
// Debugging/logging
@@ -56,13 +56,17 @@
FRIEND_TEST(FilesystemVerifierActionTest,
RunAsRootDetermineFilesystemSizeTest);
- // Callback from the main loop when there's data to read from the file
- // descriptor.
- void OnReadReadyCallback();
+ // Schedules the asynchronous read of the filesystem.
+ void ScheduleRead();
- // Based on the state of the read buffers, terminates read process and the
- // action.
- void CheckTerminationConditions();
+ // Called from the main loop when a single read from |src_stream_| succeeds or
+ // fails, calling OnReadDoneCallback() and OnReadErrorCallback() respectively.
+ void OnReadDoneCallback(size_t bytes_read);
+ void OnReadErrorCallback(const chromeos::Error* error);
+
+ // Based on the state of the read buffer, terminates read process and the
+ // action. Return whether the action was terminated.
+ bool CheckTerminationConditions();
// Cleans up all the variables we use for async operations and tells the
// ActionProcessor we're done w/ |code| as passed in. |cancelled_| should be
@@ -72,23 +76,18 @@
// Determine, if possible, the source file system size to avoid copying the
// whole partition. Currently this supports only the root file system assuming
// it's ext3-compatible.
- void DetermineFilesystemSize(int fd);
+ void DetermineFilesystemSize(const std::string& path);
// The type of the partition that we are verifying.
PartitionType partition_type_;
- // If non-null, this is the GUnixInputStream object for the opened source
- // partition.
- int src_fd_{-1};
+ // If not null, the FileStream used to read from the device.
+ chromeos::StreamPtr src_stream_;
// Buffer for storing data we read.
chromeos::Blob buffer_;
- // The task id for the the in-flight async call.
- chromeos::MessageLoop::TaskId read_task_{chromeos::MessageLoop::kTaskIdNull};
-
bool read_done_{false}; // true if reached EOF on the input stream.
- bool failed_{false}; // true if the action has failed.
bool cancelled_{false}; // true if the action has been cancelled.
// The install plan we're passed in via the input pipe.
diff --git a/filesystem_verifier_action_unittest.cc b/filesystem_verifier_action_unittest.cc
index da0fd95..9ddeb29 100644
--- a/filesystem_verifier_action_unittest.cc
+++ b/filesystem_verifier_action_unittest.cc
@@ -14,7 +14,7 @@
#include <base/posix/eintr_wrapper.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/message_loops/glib_message_loop.h>
+#include <chromeos/message_loops/fake_message_loop.h>
#include <chromeos/message_loops/message_loop_utils.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -47,7 +47,7 @@
bool hash_fail,
PartitionType partition_type);
- chromeos::GlibMessageLoop loop_;
+ chromeos::FakeMessageLoop loop_{nullptr};
FakeSystemState fake_system_state_;
};
@@ -352,6 +352,8 @@
TEST_F(FilesystemVerifierActionTest, RunAsRootTerminateEarlyTest) {
ASSERT_EQ(0, getuid());
EXPECT_TRUE(DoTest(true, false, PartitionType::kKernel));
+ // TerminateEarlyTest may leak some null callbacks from the Stream class.
+ while (loop_.RunOnce(false)) {}
}
TEST_F(FilesystemVerifierActionTest, RunAsRootDetermineFilesystemSizeTest) {
@@ -362,19 +364,18 @@
// Extend the "partition" holding the file system from 10MiB to 20MiB.
EXPECT_EQ(0, truncate(img.c_str(), 20 * 1024 * 1024));
- for (int i = 0; i < 2; ++i) {
- PartitionType fs_type =
- i ? PartitionType::kSourceKernel : PartitionType::kSourceRootfs;
- FilesystemVerifierAction action(&fake_system_state_, fs_type);
+ {
+ FilesystemVerifierAction action(&fake_system_state_,
+ PartitionType::kSourceKernel);
EXPECT_EQ(kint64max, action.remaining_size_);
- {
- int fd = HANDLE_EINTR(open(img.c_str(), O_RDONLY));
- EXPECT_GT(fd, 0);
- ScopedFdCloser fd_closer(&fd);
- action.DetermineFilesystemSize(fd);
- }
- EXPECT_EQ(i ? kint64max : 10 * 1024 * 1024,
- action.remaining_size_);
+ action.DetermineFilesystemSize(img);
+ EXPECT_EQ(kint64max, action.remaining_size_);
+ }
+ {
+ FilesystemVerifierAction action(&fake_system_state_,
+ PartitionType::kSourceRootfs);
+ action.DetermineFilesystemSize(img);
+ EXPECT_EQ(10 * 1024 * 1024, action.remaining_size_);
}
}
diff --git a/generate_image.gypi b/generate_image.gypi
deleted file mode 100644
index 025e925..0000000
--- a/generate_image.gypi
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- 'variables': {
- 'out_dir': '<(SHARED_INTERMEDIATE_DIR)/<(image_out_dir)',
- 'generator': 'sample_images/generate_image.sh',
- },
- 'rules': [
- {
- 'rule_name': 'generate_image',
- 'extension': 'txt',
- 'inputs': [
- '<(generator)',
- '<(RULE_INPUT_PATH)',
- ],
- 'outputs': [
- '<(out_dir)/<(RULE_INPUT_ROOT).img',
- ],
- 'action': [
- '<(generator)',
- '<(RULE_INPUT_PATH)',
- '<(out_dir)',
- ],
- 'msvs_cygwin_shell': 0,
- 'message': 'Generating image from <(RULE_INPUT_PATH)',
- 'process_outputs_as_sources': 1,
- },
- ],
-}
diff --git a/glib_utils.cc b/glib_utils.cc
deleted file mode 100644
index 625d8d1..0000000
--- a/glib_utils.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "update_engine/glib_utils.h"
-
-#include <base/strings/stringprintf.h>
-
-using std::string;
-
-namespace chromeos_update_engine {
-namespace utils {
-
-string GetAndFreeGError(GError** error) {
- if (!*error) {
- return "Unknown GLib error.";
- }
- string message =
- base::StringPrintf("GError(%d): %s",
- (*error)->code,
- (*error)->message ? (*error)->message : "(unknown)");
- g_error_free(*error);
- *error = nullptr;
- return message;
-}
-
-gchar** StringVectorToGStrv(const std::vector<string> &vec_str) {
- GPtrArray *p = g_ptr_array_new();
- for (const string& str : vec_str) {
- g_ptr_array_add(p, g_strdup(str.c_str()));
- }
- g_ptr_array_add(p, nullptr);
- return reinterpret_cast<gchar**>(g_ptr_array_free(p, FALSE));
-}
-
-} // namespace utils
-} // namespace chromeos_update_engine
diff --git a/glib_utils.h b/glib_utils.h
deleted file mode 100644
index 97020cb..0000000
--- a/glib_utils.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UPDATE_ENGINE_GLIB_UTILS_H_
-#define UPDATE_ENGINE_GLIB_UTILS_H_
-
-#include <string>
-#include <vector>
-
-#include <glib.h>
-
-namespace chromeos_update_engine {
-namespace utils {
-
-// Returns the error message, if any, from a GError pointer. Frees the GError
-// object and resets error to null.
-std::string GetAndFreeGError(GError** error);
-
-// Converts a vector of strings to a NUL-terminated array of
-// strings. The resulting array should be freed with g_strfreev()
-// when are you done with it.
-gchar** StringVectorToGStrv(const std::vector<std::string>& vec_str);
-
-// A base::FreeDeleter that frees memory using g_free(). Useful when
-// integrating with GLib since it can be used with std::unique_ptr to
-// automatically free memory when going out of scope.
-struct GLibFreeDeleter {
- inline void operator()(void* ptr) const {
- g_free(static_cast<gpointer>(ptr));
- }
-};
-
-// A base::FreeDeleter that frees memory using g_strfreev(). Useful
-// when integrating with GLib since it can be used with std::unique_ptr to
-// automatically free memory when going out of scope.
-struct GLibStrvFreeDeleter {
- inline void operator()(void* ptr) const {
- g_strfreev(static_cast<gchar**>(ptr));
- }
-};
-
-} // namespace utils
-} // namespace chromeos_update_engine
-
-#endif // UPDATE_ENGINE_GLIB_UTILS_H_
diff --git a/http_fetcher_unittest.cc b/http_fetcher_unittest.cc
index fd520e9..d822e51 100644
--- a/http_fetcher_unittest.cc
+++ b/http_fetcher_unittest.cc
@@ -14,13 +14,17 @@
#include <base/location.h>
#include <base/logging.h>
+#include <base/message_loop/message_loop.h>
+#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <base/time/time.h>
-#include <chromeos/message_loops/glib_message_loop.h>
+#include <chromeos/message_loops/base_message_loop.h>
#include <chromeos/message_loops/message_loop.h>
#include <chromeos/message_loops/message_loop_utils.h>
-#include <glib.h>
+#include <chromeos/process.h>
+#include <chromeos/streams/file_stream.h>
+#include <chromeos/streams/stream.h>
#include <gtest/gtest.h>
#include "update_engine/fake_system_state.h"
@@ -29,6 +33,7 @@
#include "update_engine/mock_http_fetcher.h"
#include "update_engine/multi_range_http_fetcher.h"
#include "update_engine/proxy_resolver.h"
+#include "update_engine/test_utils.h"
#include "update_engine/utils.h"
using chromeos::MessageLoop;
@@ -88,74 +93,68 @@
class PythonHttpServer : public HttpServer {
public:
- PythonHttpServer() : pid_(-1), port_(0) {
+ PythonHttpServer() : port_(0) {
started_ = false;
// Spawn the server process.
- gchar *argv[] = {
- const_cast<gchar*>("./test_http_server"),
- nullptr
- };
- GError *err;
- gint server_stdout = -1;
- if (!g_spawn_async_with_pipes(nullptr, argv, nullptr,
- G_SPAWN_DO_NOT_REAP_CHILD, nullptr, nullptr,
- &pid_, nullptr, &server_stdout, nullptr,
- &err)) {
- LOG(ERROR) << "failed to spawn http server process";
+ unique_ptr<chromeos::Process> http_server(new chromeos::ProcessImpl());
+ base::FilePath test_server_path =
+ test_utils::GetBuildArtifactsPath().Append("test_http_server");
+ http_server->AddArg(test_server_path.value());
+ http_server->RedirectUsingPipe(STDOUT_FILENO, false);
+
+ if (!http_server->Start()) {
+ ADD_FAILURE() << "failed to spawn http server process";
return;
}
- CHECK_GT(pid_, 0);
- CHECK_GE(server_stdout, 0);
- LOG(INFO) << "started http server with pid " << pid_;
+ LOG(INFO) << "started http server with pid " << http_server->pid();
// Wait for server to begin accepting connections, obtain its port.
- char line[80];
- const size_t listening_msg_prefix_len = strlen(kServerListeningMsgPrefix);
- CHECK_GT(sizeof(line), listening_msg_prefix_len);
- int line_len = read(server_stdout, line, sizeof(line) - 1);
- if (line_len <= static_cast<int>(listening_msg_prefix_len)) {
- if (line_len < 0) {
- PLOG(ERROR) << "error reading http server stdout";
- } else {
- LOG(ERROR) << "server output too short";
+ chromeos::StreamPtr stdout = chromeos::FileStream::FromFileDescriptor(
+ http_server->GetPipe(STDOUT_FILENO), false /* own */, nullptr);
+ if (!stdout)
+ return;
+
+ vector<char> buf(128);
+ string line;
+ while (line.find('\n') == string::npos) {
+ size_t read;
+ if (!stdout->ReadBlocking(buf.data(), buf.size(), &read, nullptr)) {
+ ADD_FAILURE() << "error reading http server stdout";
+ return;
}
- Terminate(true);
+ line.append(buf.data(), read);
+ if (read == 0)
+ break;
+ }
+ // Parse the port from the output line.
+ const size_t listening_msg_prefix_len = strlen(kServerListeningMsgPrefix);
+ if (line.size() < listening_msg_prefix_len) {
+ ADD_FAILURE() << "server output too short";
return;
}
- line[line_len] = '\0';
- CHECK_EQ(strstr(line, kServerListeningMsgPrefix), line);
- const char* listening_port_str = line + listening_msg_prefix_len;
- char* end_ptr;
- long raw_port = strtol(listening_port_str, // NOLINT(runtime/int)
- &end_ptr, 10);
- CHECK(!*end_ptr || *end_ptr == '\n');
- port_ = static_cast<in_port_t>(raw_port);
- CHECK_GT(port_, 0);
+ EXPECT_EQ(kServerListeningMsgPrefix,
+ line.substr(0, listening_msg_prefix_len));
+ string port_str = line.substr(listening_msg_prefix_len);
+ port_str.resize(port_str.find('\n'));
+ EXPECT_TRUE(base::StringToUint(port_str, &port_));
+
started_ = true;
LOG(INFO) << "server running, listening on port " << port_;
- LOG(INFO) << "gdb attach now!";
+
+ // Any failure before this point will SIGKILL the test server if started
+ // when the |http_server| goes out of scope.
+ http_server_ = std::move(http_server);
}
~PythonHttpServer() {
// If there's no process, do nothing.
- if (pid_ == -1)
+ if (!http_server_)
return;
-
- // If server is responsive, request that it gracefully terminate.
- bool do_kill = false;
- if (started_) {
- LOG(INFO) << "running wget to exit";
- if (system((string("wget -t 1 --output-document=/dev/null ") +
- LocalServerUrlForPath(port_, "/quitquitquit")).c_str())) {
- LOG(WARNING) << "wget failed, resorting to brute force";
- do_kill = true;
- }
- }
-
- // Server not responding or wget failed, kill the process.
- Terminate(do_kill);
+ // Wait up to 10 seconds for the process to finish. Destroying the process
+ // will kill it with a SIGKILL otherwise.
+ http_server_->Kill(SIGTERM, 10);
}
in_port_t GetPort() const override {
@@ -163,27 +162,10 @@
}
private:
- void Terminate(bool do_kill) {
- ASSERT_GT(pid_, 0);
-
- if (do_kill) {
- LOG(INFO) << "terminating (SIGKILL) server process with pid " << pid_;
- kill(pid_, SIGKILL);
- }
-
- LOG(INFO) << "waiting for http server with pid " << pid_ << " to terminate";
- int status;
- pid_t killed_pid = waitpid(pid_, &status, 0);
- ASSERT_EQ(killed_pid, pid_);
- LOG(INFO) << "http server with pid " << pid_
- << " terminated with status " << status;
- pid_ = -1;
- }
-
static const char* kServerListeningMsgPrefix;
- GPid pid_;
- in_port_t port_;
+ unique_ptr<chromeos::Process> http_server_;
+ unsigned int port_;
};
const char* PythonHttpServer::kServerListeningMsgPrefix = "listening on port ";
@@ -345,10 +327,8 @@
template <typename T>
class HttpFetcherTest : public ::testing::Test {
public:
- // TODO(deymo): Replace this with a FakeMessageLoop. We can't do that yet
- // because these tests use g_spawn_async_with_pipes() to launch the
- // http_test_server.
- chromeos::GlibMessageLoop loop_;
+ base::MessageLoopForIO base_loop_;
+ chromeos::BaseMessageLoop loop_{&base_loop_};
T test_;
diff --git a/libcros_proxy.cc b/libcros_proxy.cc
new file mode 100644
index 0000000..9f76ea9
--- /dev/null
+++ b/libcros_proxy.cc
@@ -0,0 +1,47 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/libcros_proxy.h"
+
+#include "update_engine/dbus_proxies.h"
+
+using org::chromium::LibCrosServiceInterfaceProxy;
+using org::chromium::LibCrosServiceInterfaceProxyInterface;
+using org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxy;
+using org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface;
+
+namespace {
+const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
+} // namespace
+
+namespace chromeos_update_engine {
+
+LibCrosProxy::LibCrosProxy(
+ std::unique_ptr<LibCrosServiceInterfaceProxyInterface>
+ service_interface_proxy,
+ std::unique_ptr<UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface>
+ ue_proxy_resolved_interface)
+ : service_interface_proxy_(std::move(service_interface_proxy)),
+ ue_proxy_resolved_interface_(std::move(ue_proxy_resolved_interface)) {
+}
+
+LibCrosProxy::LibCrosProxy(const scoped_refptr<dbus::Bus>& bus)
+ : service_interface_proxy_(
+ new LibCrosServiceInterfaceProxy(bus, kLibCrosServiceName)),
+ ue_proxy_resolved_interface_(
+ new UpdateEngineLibcrosProxyResolvedInterfaceProxy(
+ bus,
+ kLibCrosServiceName)) {
+}
+
+LibCrosServiceInterfaceProxyInterface* LibCrosProxy::service_interface_proxy() {
+ return service_interface_proxy_.get();
+}
+
+UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface*
+LibCrosProxy::ue_proxy_resolved_interface() {
+ return ue_proxy_resolved_interface_.get();
+}
+
+} // namespace chromeos_update_engine
diff --git a/libcros_proxy.h b/libcros_proxy.h
new file mode 100644
index 0000000..4c0b8bc
--- /dev/null
+++ b/libcros_proxy.h
@@ -0,0 +1,50 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UPDATE_ENGINE_LIBCROS_PROXY_H_
+#define UPDATE_ENGINE_LIBCROS_PROXY_H_
+
+#include <memory>
+
+#include <base/macros.h>
+#include <dbus/bus.h>
+
+#include "update_engine/dbus_proxies.h"
+
+namespace chromeos_update_engine {
+
+// This class handles the DBus connection with chrome to resolve proxies. This
+// is a thin class to just hold the generated proxies (real or mocked ones).
+class LibCrosProxy final {
+ public:
+ explicit LibCrosProxy(const scoped_refptr<dbus::Bus>& bus);
+ LibCrosProxy(
+ std::unique_ptr<org::chromium::LibCrosServiceInterfaceProxyInterface>
+ service_interface_proxy,
+ std::unique_ptr<
+ org::chromium::
+ UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface>
+ ue_proxy_resolved_interface);
+
+ ~LibCrosProxy() = default;
+
+ // Getters for the two proxies.
+ org::chromium::LibCrosServiceInterfaceProxyInterface*
+ service_interface_proxy();
+ org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface*
+ ue_proxy_resolved_interface();
+
+ private:
+ std::unique_ptr<org::chromium::LibCrosServiceInterfaceProxyInterface>
+ service_interface_proxy_;
+ std::unique_ptr<
+ org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface>
+ ue_proxy_resolved_interface_;
+
+ DISALLOW_COPY_AND_ASSIGN(LibCrosProxy);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_LIBCROS_PROXY_H_
diff --git a/libcurl_http_fetcher.cc b/libcurl_http_fetcher.cc
index 9d32293..8010b8c 100644
--- a/libcurl_http_fetcher.cc
+++ b/libcurl_http_fetcher.cc
@@ -8,6 +8,7 @@
#include <string>
#include <base/bind.h>
+#include <base/format_macros.h>
#include <base/location.h>
#include <base/logging.h>
#include <base/strings/string_util.h>
@@ -132,10 +133,10 @@
}
// Create a string representation of the desired range.
- string range_str = (end_offset ?
- base::StringPrintf("%jd-%zu", resume_offset_,
- end_offset) :
- base::StringPrintf("%jd-", resume_offset_));
+ string range_str = base::StringPrintf(
+ "%" PRIu64 "-", static_cast<uint64_t>(resume_offset_));
+ if (end_offset)
+ range_str += std::to_string(end_offset);
CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_RANGE, range_str.c_str()),
CURLE_OK);
}
diff --git a/libcurl_http_fetcher.h b/libcurl_http_fetcher.h
index 4f68a9a..1b8891d 100644
--- a/libcurl_http_fetcher.h
+++ b/libcurl_http_fetcher.h
@@ -129,7 +129,7 @@
// Calls into curl_multi_perform to let libcurl do its work. Returns after
// curl_multi_perform is finished, which may actually be after more than
- // one call to curl_multi_perform. This method will set up the glib run
+ // one call to curl_multi_perform. This method will set up the message
// loop with sources for future work that libcurl will do.
// This method will not block.
// Returns true if we should resume immediately after this call.
diff --git a/main.cc b/main.cc
index 51a04ec..1d82dd6 100644
--- a/main.cc
+++ b/main.cc
@@ -5,101 +5,28 @@
#include <unistd.h>
#include <string>
-#include <vector>
#include <base/at_exit.h>
#include <base/command_line.h>
#include <base/files/file_util.h>
-#include <base/location.h>
#include <base/logging.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <base/time/time.h>
#include <chromeos/flag_helper.h>
-#include <chromeos/message_loops/glib_message_loop.h>
-#include <glib.h>
+#include <chromeos/message_loops/base_message_loop.h>
#include <metrics/metrics_library.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include "update_engine/certificate_checker.h"
-#include "update_engine/dbus_constants.h"
-#include "update_engine/dbus_service.h"
-#include "update_engine/dbus_wrapper_interface.h"
-#include "update_engine/glib_utils.h"
-#include "update_engine/real_system_state.h"
-#include "update_engine/subprocess.h"
+#include "update_engine/daemon.h"
#include "update_engine/terminator.h"
-#include "update_engine/update_attempter.h"
-extern "C" {
-#include "update_engine/org.chromium.UpdateEngineInterface.dbusserver.h"
-}
#include "update_engine/utils.h"
using std::string;
-using std::vector;
-
-namespace {
-const int kDBusSystemMaxWaitSeconds = 2 * 60;
-} // namespace
namespace chromeos_update_engine {
-
namespace {
-// Wait for DBus to be ready by attempting to get the system bus up to
-// |timeout| time. Returns whether it succeeded to get the bus.
-bool WaitForDBusSystem(base::TimeDelta timeout) {
- GError *error = nullptr;
- DBusGConnection *bus = nullptr;
- Clock clock;
- base::Time deadline = clock.GetMonotonicTime() + timeout;
-
- while (clock.GetMonotonicTime() < deadline) {
- bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
- if (bus)
- return true;
- LOG(WARNING) << "Failed to get system bus, waiting: "
- << utils::GetAndFreeGError(&error);
- // Wait 1 second.
- sleep(1);
- }
- LOG(ERROR) << "Failed to get system bus after " << timeout.InSeconds()
- << " seconds.";
- return false;
-}
-
-void SetupDBusService(UpdateEngineService* service) {
- DBusGConnection *bus;
- DBusGProxy *proxy;
- GError *error = nullptr;
-
- bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
- LOG_IF(FATAL, !bus) << "Failed to get bus: "
- << utils::GetAndFreeGError(&error);
- proxy = dbus_g_proxy_new_for_name(bus,
- DBUS_SERVICE_DBUS,
- DBUS_PATH_DBUS,
- DBUS_INTERFACE_DBUS);
- guint32 request_name_ret;
- if (!org_freedesktop_DBus_request_name(proxy,
- kUpdateEngineServiceName,
- 0,
- &request_name_ret,
- &error)) {
- LOG(FATAL) << "Failed to get name: " << utils::GetAndFreeGError(&error);
- }
- if (request_name_ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
- g_warning("Got result code %u from requesting name", request_name_ret);
- LOG(FATAL) << "Got result code " << request_name_ret
- << " from requesting name, but expected "
- << DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
- }
- dbus_g_connection_register_g_object(bus,
- "/org/chromium/UpdateEngine",
- G_OBJECT(service));
-}
-
void SetupLogSymlink(const string& symlink_path, const string& log_path) {
// TODO(petkov): To ensure a smooth transition between non-timestamped and
// timestamped logs, move an existing log to start the first timestamped
@@ -166,11 +93,7 @@
DEFINE_bool(foreground, false,
"Don't daemon()ize; run in foreground.");
- ::g_type_init();
- dbus_threads_init_default();
- base::AtExitManager exit_manager; // Required for base/rand_util.h.
chromeos_update_engine::Terminator::Init();
- chromeos_update_engine::Subprocess::Init();
chromeos::FlagHelper::Init(argc, argv, "Chromium OS Update Engine");
chromeos_update_engine::SetupLogging(FLAGS_logtostderr);
if (!FLAGS_foreground)
@@ -184,70 +107,10 @@
// Done _after_ log file creation.
umask(S_IXUSR | S_IRWXG | S_IRWXO);
- // Create the single Glib main loop. Code accessing directly the glib main
- // loop (such as calling g_timeout_add() or similar functions) will still work
- // since the backend for the message loop is still the Glib main loop.
- // TODO(deymo): Replace this |loop| with one based on libevent once no other
- // code here uses glib directly.
- chromeos::GlibMessageLoop loop;
- loop.SetAsCurrent();
+ chromeos_update_engine::UpdateEngineDaemon update_engine_daemon;
+ int exit_code = update_engine_daemon.Run();
- // Wait up to 2 minutes for DBus to be ready.
- LOG_IF(FATAL, !chromeos_update_engine::WaitForDBusSystem(
- base::TimeDelta::FromSeconds(kDBusSystemMaxWaitSeconds)))
- << "Failed to initialize DBus, aborting.";
-
- chromeos_update_engine::RealSystemState real_system_state;
- LOG_IF(ERROR, !real_system_state.Initialize())
- << "Failed to initialize system state.";
- chromeos_update_engine::UpdateAttempter *update_attempter =
- real_system_state.update_attempter();
- CHECK(update_attempter);
-
- // Sets static members for the certificate checker.
- chromeos_update_engine::CertificateChecker::set_system_state(
- &real_system_state);
- chromeos_update_engine::OpenSSLWrapper openssl_wrapper;
- chromeos_update_engine::CertificateChecker::set_openssl_wrapper(
- &openssl_wrapper);
-
- // Create the dbus service object:
- dbus_g_object_type_install_info(UPDATE_ENGINE_TYPE_SERVICE,
- &dbus_glib_update_engine_service_object_info);
- UpdateEngineService* service = update_engine_service_new();
- service->system_state_ = &real_system_state;
- update_attempter->set_dbus_service(service);
- chromeos_update_engine::SetupDBusService(service);
-
- // Initiate update checks.
- update_attempter->ScheduleUpdates();
-
- // Update boot flags after 45 seconds.
- loop.PostDelayedTask(
- FROM_HERE,
- base::Bind(&chromeos_update_engine::UpdateAttempter::UpdateBootFlags,
- base::Unretained(update_attempter)),
- base::TimeDelta::FromSeconds(45));
-
- // Broadcast the update engine status on startup to ensure consistent system
- // state on crashes.
- loop.PostTask(FROM_HERE, base::Bind(
- &chromeos_update_engine::UpdateAttempter::BroadcastStatus,
- base::Unretained(update_attempter)));
-
- // Run the UpdateEngineStarted() method on |update_attempter|.
- loop.PostTask(FROM_HERE, base::Bind(
- &chromeos_update_engine::UpdateAttempter::UpdateEngineStarted,
- base::Unretained(update_attempter)));
-
- // Run the main loop until exit time:
- loop.Run();
-
- // Cleanup:
- update_attempter->set_dbus_service(nullptr);
- g_object_unref(G_OBJECT(service));
-
- loop.ReleaseFromCurrent();
- LOG(INFO) << "Chrome OS Update Engine terminating";
- return 0;
+ LOG(INFO) << "Chrome OS Update Engine terminating with exit code "
+ << exit_code;
+ return exit_code;
}
diff --git a/mock_connection_manager.h b/mock_connection_manager.h
index 9530865..e885e2f 100644
--- a/mock_connection_manager.h
+++ b/mock_connection_manager.h
@@ -7,31 +7,23 @@
#include <gmock/gmock.h>
-#include "update_engine/connection_manager.h"
+#include "update_engine/connection_manager_interface.h"
namespace chromeos_update_engine {
// This class mocks the generic interface to the connection manager
// (e.g FlimFlam, Shill, etc.) to consolidate all connection-related
// logic in update_engine.
-class MockConnectionManager : public ConnectionManager {
+class MockConnectionManager : public ConnectionManagerInterface {
public:
- explicit MockConnectionManager(SystemState* system_state)
- : ConnectionManager(system_state) {}
+ MockConnectionManager() = default;
- MOCK_CONST_METHOD3(GetConnectionProperties,
- bool(DBusWrapperInterface* dbus_iface,
- NetworkConnectionType* out_type,
- NetworkTethering* out_tethering));
+ MOCK_METHOD2(GetConnectionProperties,
+ bool(NetworkConnectionType* out_type,
+ NetworkTethering* out_tethering));
MOCK_CONST_METHOD2(IsUpdateAllowedOver, bool(NetworkConnectionType type,
NetworkTethering tethering));
-
- MOCK_CONST_METHOD1(StringForConnectionType,
- const char*(NetworkConnectionType type));
-
- MOCK_CONST_METHOD1(StringForTethering,
- const char*(NetworkTethering tethering));
};
} // namespace chromeos_update_engine
diff --git a/mock_dbus_wrapper.h b/mock_dbus_wrapper.h
deleted file mode 100644
index 5d630a6..0000000
--- a/mock_dbus_wrapper.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UPDATE_ENGINE_MOCK_DBUS_WRAPPER_H_
-#define UPDATE_ENGINE_MOCK_DBUS_WRAPPER_H_
-
-#include <gmock/gmock.h>
-
-#include "update_engine/dbus_wrapper_interface.h"
-
-namespace chromeos_update_engine {
-
-class MockDBusWrapper : public DBusWrapperInterface {
- public:
- MOCK_METHOD4(ProxyNewForName, DBusGProxy*(DBusGConnection *connection,
- const char *name,
- const char *path,
- const char *interface));
-
- MOCK_METHOD1(ProxyUnref, void(DBusGProxy* proxy));
-
- MOCK_METHOD2(BusGet, DBusGConnection*(DBusBusType type, GError **error));
-
- MOCK_METHOD4(ProxyCall_0_1, gboolean(DBusGProxy *proxy,
- const char *method,
- GError **error,
- GHashTable** out1));
- MOCK_METHOD4(ProxyCall_0_1, gboolean(DBusGProxy *proxy,
- const char *method,
- GError **error,
- gint* out1));
- MOCK_METHOD4(ProxyCall_1_0, gboolean(DBusGProxy *proxy,
- const char *method,
- GError **error,
- gint in1));
- MOCK_METHOD6(ProxyCall_3_0, gboolean(DBusGProxy* proxy,
- const char* method,
- GError** error,
- const char* in1,
- const char* in2,
- const char* in3));
-
- MOCK_METHOD3(ProxyAddSignal_1, void(DBusGProxy* proxy,
- const char* signal_name,
- GType type1));
-
- MOCK_METHOD4(ProxyAddSignal_2, void(DBusGProxy* proxy,
- const char* signal_name,
- GType type1,
- GType type2));
-
- MOCK_METHOD5(ProxyConnectSignal, void(DBusGProxy* proxy,
- const char* signal_name,
- GCallback handler,
- void* data,
- GClosureNotify free_data_func));
-
- MOCK_METHOD4(ProxyDisconnectSignal, void(DBusGProxy* proxy,
- const char* signal_name,
- GCallback handler,
- void* data));
-
- MOCK_METHOD1(ConnectionGetConnection, DBusConnection*(DBusGConnection* gbus));
-
- MOCK_METHOD3(DBusBusAddMatch, void(DBusConnection* connection,
- const char* rule,
- DBusError* error));
-
- MOCK_METHOD4(DBusConnectionAddFilter, dbus_bool_t(
- DBusConnection* connection,
- DBusHandleMessageFunction function,
- void* user_data,
- DBusFreeFunction free_data_function));
-
- MOCK_METHOD3(DBusConnectionRemoveFilter, void(
- DBusConnection* connection,
- DBusHandleMessageFunction function,
- void* user_data));
-
- MOCK_METHOD3(DBusMessageIsSignal, dbus_bool_t(DBusMessage* message,
- const char* interface,
- const char* signal_name));
-
- MOCK_METHOD5(DBusMessageGetArgs_3, dbus_bool_t(DBusMessage* message,
- DBusError* error,
- char** out1,
- char** out2,
- char** out3));
-};
-
-} // namespace chromeos_update_engine
-
-#endif // UPDATE_ENGINE_MOCK_DBUS_WRAPPER_H_
diff --git a/mock_http_fetcher.h b/mock_http_fetcher.h
index 89f5d21..fa718bc 100644
--- a/mock_http_fetcher.h
+++ b/mock_http_fetcher.h
@@ -10,7 +10,6 @@
#include <base/logging.h>
#include <chromeos/message_loops/message_loop.h>
-#include <glib.h>
#include "update_engine/fake_system_state.h"
#include "update_engine/http_fetcher.h"
@@ -40,8 +39,7 @@
timeout_id_(chromeos::MessageLoop::kTaskIdNull),
paused_(false),
fail_transfer_(false),
- never_use_(false),
- mock_connection_manager_(&fake_system_state_) {
+ never_use_(false) {
fake_system_state_.set_connection_manager(&mock_connection_manager_);
data_.insert(data_.end(), data, data + size);
}
diff --git a/multi_range_http_fetcher.cc b/multi_range_http_fetcher.cc
index 07118a3..8d1fd06 100644
--- a/multi_range_http_fetcher.cc
+++ b/multi_range_http_fetcher.cc
@@ -169,9 +169,9 @@
std::string MultiRangeHttpFetcher::Range::ToString() const {
std::string range_str = base::StringPrintf("%jd+", offset());
if (HasLength())
- base::StringAppendF(&range_str, "%zu", length());
+ range_str += std::to_string(length());
else
- base::StringAppendF(&range_str, "?");
+ range_str += "?";
return range_str;
}
diff --git a/omaha_hash_calculator_unittest.cc b/omaha_hash_calculator_unittest.cc
index 29a0e45..d575fb0 100644
--- a/omaha_hash_calculator_unittest.cc
+++ b/omaha_hash_calculator_unittest.cc
@@ -11,7 +11,6 @@
#include <vector>
#include <chromeos/secure_blob.h>
-#include <glib.h>
#include <gtest/gtest.h>
#include "update_engine/libcurl_http_fetcher.h"
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index dff8ad3..3d7cccc 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -21,6 +21,7 @@
#include <expat.h>
#include "update_engine/action_pipe.h"
+#include "update_engine/connection_manager.h"
#include "update_engine/constants.h"
#include "update_engine/hardware_interface.h"
#include "update_engine/omaha_hash_calculator.h"
@@ -28,7 +29,6 @@
#include "update_engine/p2p_manager.h"
#include "update_engine/payload_state_interface.h"
#include "update_engine/prefs_interface.h"
-#include "update_engine/real_dbus_wrapper.h"
#include "update_engine/utils.h"
using base::Time;
@@ -1447,17 +1447,16 @@
bool OmahaRequestAction::IsUpdateAllowedOverCurrentConnection() const {
NetworkConnectionType type;
NetworkTethering tethering;
- RealDBusWrapper dbus_iface;
- ConnectionManager* connection_manager = system_state_->connection_manager();
- if (!connection_manager->GetConnectionProperties(&dbus_iface,
- &type, &tethering)) {
+ ConnectionManagerInterface* connection_manager =
+ system_state_->connection_manager();
+ if (!connection_manager->GetConnectionProperties(&type, &tethering)) {
LOG(INFO) << "We could not determine our connection type. "
<< "Defaulting to allow updates.";
return true;
}
bool is_allowed = connection_manager->IsUpdateAllowedOver(type, tethering);
LOG(INFO) << "We are connected via "
- << connection_manager->StringForConnectionType(type)
+ << ConnectionManager::StringForConnectionType(type)
<< ", Updates allowed: " << (is_allowed ? "Yes" : "No");
return is_allowed;
}
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index 1131c9c..2a11d7d 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -4,7 +4,6 @@
#include "update_engine/omaha_request_action.h"
-#include <glib.h>
#include <stdint.h>
#include <string>
@@ -445,17 +444,16 @@
OmahaResponse response;
// Set up a connection manager that doesn't allow a valid update over
// the current ethernet connection.
- MockConnectionManager mock_cm(nullptr);
+ MockConnectionManager mock_cm;
fake_system_state_.set_connection_manager(&mock_cm);
- EXPECT_CALL(mock_cm, GetConnectionProperties(_, _, _))
- .WillRepeatedly(DoAll(SetArgumentPointee<1>(kNetEthernet),
- SetArgumentPointee<2>(NetworkTethering::kUnknown),
- Return(true)));
- EXPECT_CALL(mock_cm, IsUpdateAllowedOver(kNetEthernet, _))
+ EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
+ .WillRepeatedly(
+ DoAll(SetArgumentPointee<0>(NetworkConnectionType::kEthernet),
+ SetArgumentPointee<1>(NetworkTethering::kUnknown),
+ Return(true)));
+ EXPECT_CALL(mock_cm, IsUpdateAllowedOver(NetworkConnectionType::kEthernet, _))
.WillRepeatedly(Return(false));
- EXPECT_CALL(mock_cm, StringForConnectionType(kNetEthernet))
- .WillRepeatedly(Return(shill::kTypeEthernet));
ASSERT_FALSE(
TestUpdateCheck(nullptr, // request_params
diff --git a/omaha_response_handler_action.cc b/omaha_response_handler_action.cc
index 67639cc..6c7c166 100644
--- a/omaha_response_handler_action.cc
+++ b/omaha_response_handler_action.cc
@@ -10,7 +10,7 @@
#include <base/strings/string_util.h>
#include <policy/device_policy.h>
-#include "update_engine/connection_manager.h"
+#include "update_engine/connection_manager_interface.h"
#include "update_engine/constants.h"
#include "update_engine/delta_performer.h"
#include "update_engine/hardware_interface.h"
diff --git a/p2p_manager.cc b/p2p_manager.cc
index c72a023..9b6a659 100644
--- a/p2p_manager.cc
+++ b/p2p_manager.cc
@@ -30,11 +30,11 @@
#include <base/bind.h>
#include <base/files/file_enumerator.h>
#include <base/files/file_path.h>
+#include <base/format_macros.h>
#include <base/logging.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include "update_engine/glib_utils.h"
#include "update_engine/subprocess.h"
#include "update_engine/update_manager/policy.h"
#include "update_engine/update_manager/update_manager.h"
@@ -91,7 +91,7 @@
vector<string> args;
args.push_back("p2p-client");
args.push_back(string("--get-url=") + file_id);
- args.push_back(StringPrintf("--minimum-size=%zu", minimum_size));
+ args.push_back(StringPrintf("--minimum-size=%" PRIuS, minimum_size));
return args;
}
@@ -369,8 +369,8 @@
~LookupData() {
if (timeout_task_ != MessageLoop::kTaskIdNull)
MessageLoop::current()->CancelTask(timeout_task_);
- if (child_tag_)
- Subprocess::Get().KillExec(child_tag_);
+ if (child_pid_)
+ Subprocess::Get().KillExec(child_pid_);
}
void InitiateLookup(const vector<string>& cmd, TimeDelta timeout) {
@@ -380,11 +380,11 @@
// guarantee is useful for testing).
// We expect to run just "p2p-client" and find it in the path.
- child_tag_ = Subprocess::Get().ExecFlags(
- cmd, G_SPAWN_SEARCH_PATH, false /* redirect stderr */, OnLookupDone,
- this);
+ child_pid_ = Subprocess::Get().ExecFlags(
+ cmd, Subprocess::kSearchPath,
+ Bind(&LookupData::OnLookupDone, base::Unretained(this)));
- if (!child_tag_) {
+ if (!child_pid_) {
LOG(ERROR) << "Error spawning " << utils::StringVectorToString(cmd);
ReportErrorAndDeleteInIdle();
return;
@@ -442,19 +442,16 @@
reported_ = true;
}
- static void OnLookupDone(int return_code,
- const string& output,
- void *user_data) {
- LookupData *lookup_data = reinterpret_cast<LookupData*>(user_data);
- lookup_data->child_tag_ = 0;
+ void OnLookupDone(int return_code, const string& output) {
+ child_pid_ = 0;
if (return_code != 0) {
LOG(INFO) << "Child exited with non-zero exit code "
<< return_code;
- lookup_data->ReportError();
+ ReportError();
} else {
- lookup_data->ReportSuccess(output);
+ ReportSuccess(output);
}
- delete lookup_data;
+ delete this;
}
void OnTimeout() {
@@ -467,7 +464,7 @@
// The Subprocess tag of the running process. A value of 0 means that the
// process is not running.
- uint32_t child_tag_{0};
+ pid_t child_pid_{0};
// The timeout task_id we are waiting on, if any.
MessageLoop::TaskId timeout_task_{MessageLoop::kTaskIdNull};
@@ -557,7 +554,7 @@
}
}
- string decimal_size = StringPrintf("%zu", expected_size);
+ string decimal_size = std::to_string(expected_size);
if (fsetxattr(fd, kCrosP2PFileSizeXAttrName,
decimal_size.c_str(), decimal_size.size(), 0) != 0) {
PLOG(ERROR) << "Error setting xattr " << path.value();
diff --git a/p2p_manager.h b/p2p_manager.h
index fb07c2e..559bba8 100644
--- a/p2p_manager.h
+++ b/p2p_manager.h
@@ -77,7 +77,7 @@
// Asynchronously finds a peer that serves the file identified by
// |file_id|. If |minimum_size| is non-zero, will find a peer that
// has at least that many bytes. When the result is ready |callback|
- // is called from the default GLib mainloop.
+ // is called from the current message loop.
//
// This operation may take a very long time to complete because part
// of the p2p protocol involves waiting for the LAN-wide sum of all
diff --git a/p2p_manager_unittest.cc b/p2p_manager_unittest.cc
index 8667578..7e3f57f 100644
--- a/p2p_manager_unittest.cc
+++ b/p2p_manager_unittest.cc
@@ -17,8 +17,10 @@
#include <base/bind.h>
#include <base/callback.h>
#include <base/files/file_util.h>
+#include <base/message_loop/message_loop.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/message_loops/glib_message_loop.h>
+#include <chromeos/asynchronous_signal_handler.h>
+#include <chromeos/message_loops/base_message_loop.h>
#include <chromeos/message_loops/message_loop.h>
#include <chromeos/message_loops/message_loop_utils.h>
#include <gmock/gmock.h>
@@ -57,6 +59,8 @@
// Derived from testing::Test.
void SetUp() override {
loop_.SetAsCurrent();
+ async_signal_handler_.Init();
+ subprocess_.Init(&async_signal_handler_);
test_conf_ = new FakeP2PManagerConfiguration();
// Allocate and install a mock policy implementation in the fake Update
@@ -71,14 +75,10 @@
TimeDelta::FromDays(5)));
}
- void TearDown() override {
- EXPECT_EQ(0, chromeos::MessageLoopRunMaxIterations(&loop_, 1));
- }
-
- // TODO(deymo): Replace this with a FakeMessageLoop. P2PManager uses glib to
- // interact with the p2p-client tool, so we need to run a GlibMessageLoop
- // here.
- chromeos::GlibMessageLoop loop_;
+ base::MessageLoopForIO base_loop_;
+ chromeos::BaseMessageLoop loop_{&base_loop_};
+ chromeos::AsynchronousSignalHandler async_signal_handler_;
+ Subprocess subprocess_;
// The P2PManager::Configuration instance used for testing.
FakeP2PManagerConfiguration *test_conf_;
@@ -312,7 +312,7 @@
}
if (size_xattr != 0) {
- string decimal_size = base::StringPrintf("%zu", size_xattr);
+ string decimal_size = std::to_string(size_xattr);
if (fsetxattr(fd, "user.cros-p2p-filesize",
decimal_size.c_str(), decimal_size.size(), 0) != 0) {
PLOG(ERROR) << "Error setting xattr on " << path;
diff --git a/payload_generator/ab_generator.cc b/payload_generator/ab_generator.cc
index 67163eb..573c5b8 100644
--- a/payload_generator/ab_generator.cc
+++ b/payload_generator/ab_generator.cc
@@ -16,47 +16,28 @@
#include "update_engine/utils.h"
using std::string;
-using std::unique_ptr;
using std::vector;
namespace chromeos_update_engine {
-namespace {
-
-// Compare two AnnotatedOperations by the start block of the first Extent in
-// their destination extents.
-bool CompareAopsByDestination(AnnotatedOperation first_aop,
- AnnotatedOperation second_aop) {
- // We want empty operations to be at the end of the payload.
- if (!first_aop.op.dst_extents().size() || !second_aop.op.dst_extents().size())
- return ((!first_aop.op.dst_extents().size()) <
- (!second_aop.op.dst_extents().size()));
- uint32_t first_dst_start = first_aop.op.dst_extents(0).start_block();
- uint32_t second_dst_start = second_aop.op.dst_extents(0).start_block();
- return first_dst_start < second_dst_start;
-}
-
-} // namespace
-
bool ABGenerator::GenerateOperations(
const PayloadGenerationConfig& config,
- int data_file_fd,
- off_t* data_file_size,
+ BlobFileWriter* blob_file,
vector<AnnotatedOperation>* rootfs_ops,
vector<AnnotatedOperation>* kernel_ops) {
- off_t chunk_blocks = (config.chunk_size == -1 ? -1 :
- config.chunk_size / config.block_size);
+ ssize_t hard_chunk_blocks = (config.hard_chunk_size == -1 ? -1 :
+ config.hard_chunk_size / config.block_size);
+ size_t soft_chunk_blocks = config.soft_chunk_size / config.block_size;
rootfs_ops->clear();
TEST_AND_RETURN_FALSE(diff_utils::DeltaReadPartition(
rootfs_ops,
config.source.rootfs,
config.target.rootfs,
- chunk_blocks,
- data_file_fd,
- data_file_size,
- false, // skip_block_0
+ hard_chunk_blocks,
+ soft_chunk_blocks,
+ blob_file,
true)); // src_ops_allowed
LOG(INFO) << "done reading normal files";
@@ -65,60 +46,58 @@
kernel_ops,
config.source.kernel,
config.target.kernel,
- chunk_blocks,
- data_file_fd,
- data_file_size,
- false, // skip_block_0
+ hard_chunk_blocks,
+ soft_chunk_blocks,
+ blob_file,
true)); // src_ops_allowed
LOG(INFO) << "done reading kernel";
TEST_AND_RETURN_FALSE(FragmentOperations(rootfs_ops,
config.target.rootfs.path,
- data_file_fd,
- data_file_size));
+ blob_file));
TEST_AND_RETURN_FALSE(FragmentOperations(kernel_ops,
config.target.kernel.path,
- data_file_fd,
- data_file_size));
+ blob_file));
SortOperationsByDestination(rootfs_ops);
SortOperationsByDestination(kernel_ops);
- // TODO(alliewood): Change merge operations to use config.chunk_size once
- // specifying chunk_size on the command line works. crbug/485397.
+
+ // Use the soft_chunk_size when merging operations to prevent merging all
+ // the operations into a huge one if there's no hard limit.
+ size_t merge_chunk_blocks = soft_chunk_blocks;
+ if (hard_chunk_blocks != -1 &&
+ static_cast<size_t>(hard_chunk_blocks) < soft_chunk_blocks) {
+ merge_chunk_blocks = hard_chunk_blocks;
+ }
+
TEST_AND_RETURN_FALSE(MergeOperations(rootfs_ops,
- kDefaultChunkSize,
+ merge_chunk_blocks,
config.target.rootfs.path,
- data_file_fd,
- data_file_size));
+ blob_file));
TEST_AND_RETURN_FALSE(MergeOperations(kernel_ops,
- kDefaultChunkSize,
+ merge_chunk_blocks,
config.target.kernel.path,
- data_file_fd,
- data_file_size));
+ blob_file));
return true;
}
void ABGenerator::SortOperationsByDestination(
vector<AnnotatedOperation>* aops) {
- sort(aops->begin(), aops->end(), CompareAopsByDestination);
+ sort(aops->begin(), aops->end(), diff_utils::CompareAopsByDestination);
}
bool ABGenerator::FragmentOperations(
vector<AnnotatedOperation>* aops,
const string& target_part_path,
- int data_fd,
- off_t* data_file_size) {
+ BlobFileWriter* blob_file) {
vector<AnnotatedOperation> fragmented_aops;
for (const AnnotatedOperation& aop : *aops) {
- if (aop.op.type() ==
- DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY) {
+ if (aop.op.type() == InstallOperation::SOURCE_COPY) {
TEST_AND_RETURN_FALSE(SplitSourceCopy(aop, &fragmented_aops));
- } else if ((aop.op.type() ==
- DeltaArchiveManifest_InstallOperation_Type_REPLACE) ||
- (aop.op.type() ==
- DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ)) {
+ } else if ((aop.op.type() == InstallOperation::REPLACE) ||
+ (aop.op.type() == InstallOperation::REPLACE_BZ)) {
TEST_AND_RETURN_FALSE(SplitReplaceOrReplaceBz(aop, &fragmented_aops,
- target_part_path, data_fd,
- data_file_size));
+ target_part_path,
+ blob_file));
} else {
fragmented_aops.push_back(aop);
}
@@ -130,16 +109,15 @@
bool ABGenerator::SplitSourceCopy(
const AnnotatedOperation& original_aop,
vector<AnnotatedOperation>* result_aops) {
- DeltaArchiveManifest_InstallOperation original_op = original_aop.op;
- TEST_AND_RETURN_FALSE(original_op.type() ==
- DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
+ InstallOperation original_op = original_aop.op;
+ TEST_AND_RETURN_FALSE(original_op.type() == InstallOperation::SOURCE_COPY);
// Keeps track of the index of curr_src_ext.
int curr_src_ext_index = 0;
Extent curr_src_ext = original_op.src_extents(curr_src_ext_index);
for (int i = 0; i < original_op.dst_extents_size(); i++) {
Extent dst_ext = original_op.dst_extents(i);
// The new operation which will have only one dst extent.
- DeltaArchiveManifest_InstallOperation new_op;
+ InstallOperation new_op;
uint64_t blocks_left = dst_ext.num_blocks();
while (blocks_left > 0) {
if (curr_src_ext.num_blocks() <= blocks_left) {
@@ -164,7 +142,7 @@
}
}
// Fix up our new operation and add it to the results.
- new_op.set_type(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
+ new_op.set_type(InstallOperation::SOURCE_COPY);
*(new_op.add_dst_extents()) = dst_ext;
new_op.set_src_length(dst_ext.num_blocks() * kBlockSize);
new_op.set_dst_length(dst_ext.num_blocks() * kBlockSize);
@@ -185,27 +163,23 @@
const AnnotatedOperation& original_aop,
vector<AnnotatedOperation>* result_aops,
const string& target_part_path,
- int data_fd,
- off_t* data_file_size) {
- DeltaArchiveManifest_InstallOperation original_op = original_aop.op;
- const bool is_replace =
- original_op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE;
- TEST_AND_RETURN_FALSE(
- is_replace ||
- (original_op.type() ==
- DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ));
+ BlobFileWriter* blob_file) {
+ InstallOperation original_op = original_aop.op;
+ const bool is_replace = original_op.type() == InstallOperation::REPLACE;
+ TEST_AND_RETURN_FALSE(is_replace ||
+ original_op.type() == InstallOperation::REPLACE_BZ);
uint32_t data_offset = original_op.data_offset();
for (int i = 0; i < original_op.dst_extents_size(); i++) {
Extent dst_ext = original_op.dst_extents(i);
// Make a new operation with only one dst extent.
- DeltaArchiveManifest_InstallOperation new_op;
+ InstallOperation new_op;
*(new_op.add_dst_extents()) = dst_ext;
uint32_t data_size = dst_ext.num_blocks() * kBlockSize;
new_op.set_dst_length(data_size);
// If this is a REPLACE, attempt to reuse portions of the existing blob.
if (is_replace) {
- new_op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
+ new_op.set_type(InstallOperation::REPLACE);
new_op.set_data_length(data_size);
new_op.set_data_offset(data_offset);
data_offset += data_size;
@@ -214,8 +188,8 @@
AnnotatedOperation new_aop;
new_aop.op = new_op;
new_aop.name = base::StringPrintf("%s:%d", original_aop.name.c_str(), i);
- TEST_AND_RETURN_FALSE(AddDataAndSetType(&new_aop, target_part_path, data_fd,
- data_file_size));
+ TEST_AND_RETURN_FALSE(AddDataAndSetType(&new_aop, target_part_path,
+ blob_file));
result_aops->push_back(new_aop);
}
@@ -223,10 +197,9 @@
}
bool ABGenerator::MergeOperations(vector<AnnotatedOperation>* aops,
- off_t chunk_size,
+ size_t chunk_blocks,
const string& target_part_path,
- int data_fd,
- off_t* data_file_size) {
+ BlobFileWriter* blob_file) {
vector<AnnotatedOperation> new_aops;
for (const AnnotatedOperation& curr_aop : *aops) {
if (new_aops.empty()) {
@@ -248,17 +221,13 @@
uint32_t combined_block_count =
last_aop.op.dst_extents(last_dst_idx).num_blocks() +
curr_aop.op.dst_extents(0).num_blocks();
- bool good_op_type =
- curr_aop.op.type() ==
- DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY ||
- curr_aop.op.type() ==
- DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
- curr_aop.op.type() ==
- DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ;
+ bool good_op_type = curr_aop.op.type() == InstallOperation::SOURCE_COPY ||
+ curr_aop.op.type() == InstallOperation::REPLACE ||
+ curr_aop.op.type() == InstallOperation::REPLACE_BZ;
if (good_op_type &&
last_aop.op.type() == curr_aop.op.type() &&
last_end_block == curr_start_block &&
- static_cast<off_t>(combined_block_count * kBlockSize) <= chunk_size) {
+ combined_block_count <= chunk_blocks) {
// If the operations have the same type (which is a type that we can
// merge), are contiguous, are fragmented to have one destination extent,
// and their combined block count would be less than chunk size, merge
@@ -278,10 +247,8 @@
last_aop.op.set_dst_length(last_aop.op.dst_length() +
curr_aop.op.dst_length());
// Set the data length to zero so we know to add the blob later.
- if (curr_aop.op.type() ==
- DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
- curr_aop.op.type() ==
- DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
+ if (curr_aop.op.type() == InstallOperation::REPLACE ||
+ curr_aop.op.type() == InstallOperation::REPLACE_BZ) {
last_aop.op.set_data_length(0);
}
} else {
@@ -293,12 +260,10 @@
// Set the blobs for REPLACE/REPLACE_BZ operations that have been merged.
for (AnnotatedOperation& curr_aop : new_aops) {
if (curr_aop.op.data_length() == 0 &&
- (curr_aop.op.type() ==
- DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
- curr_aop.op.type() ==
- DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ)) {
+ (curr_aop.op.type() == InstallOperation::REPLACE ||
+ curr_aop.op.type() == InstallOperation::REPLACE_BZ)) {
TEST_AND_RETURN_FALSE(AddDataAndSetType(&curr_aop, target_part_path,
- data_fd, data_file_size));
+ blob_file));
}
}
@@ -308,11 +273,9 @@
bool ABGenerator::AddDataAndSetType(AnnotatedOperation* aop,
const string& target_part_path,
- int data_fd,
- off_t* data_file_size) {
- TEST_AND_RETURN_FALSE(
- aop->op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
- aop->op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
+ BlobFileWriter* blob_file) {
+ TEST_AND_RETURN_FALSE(aop->op.type() == InstallOperation::REPLACE ||
+ aop->op.type() == InstallOperation::REPLACE_BZ);
chromeos::Blob data(aop->op.dst_length());
vector<Extent> dst_extents;
@@ -328,35 +291,20 @@
CHECK(!data_bz.empty());
chromeos::Blob* data_p = nullptr;
- DeltaArchiveManifest_InstallOperation_Type new_op_type;
+ InstallOperation_Type new_op_type;
if (data_bz.size() < data.size()) {
- new_op_type = DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ;
+ new_op_type = InstallOperation::REPLACE_BZ;
data_p = &data_bz;
} else {
- new_op_type = DeltaArchiveManifest_InstallOperation_Type_REPLACE;
+ new_op_type = InstallOperation::REPLACE;
data_p = &data;
}
- // If the operation already points to a data blob, check whether it's
- // identical to the new one, in which case don't add it.
- if (aop->op.type() == new_op_type &&
- aop->op.data_length() == data_p->size()) {
- chromeos::Blob current_data(data_p->size());
- ssize_t bytes_read;
- TEST_AND_RETURN_FALSE(utils::PReadAll(data_fd,
- current_data.data(),
- aop->op.data_length(),
- aop->op.data_offset(),
- &bytes_read));
- TEST_AND_RETURN_FALSE(bytes_read ==
- static_cast<ssize_t>(aop->op.data_length()));
- if (current_data == *data_p)
- data_p = nullptr;
- }
-
- if (data_p) {
+ // If the operation doesn't point to a data blob, then we add it.
+ if (aop->op.type() != new_op_type ||
+ aop->op.data_length() != data_p->size()) {
aop->op.set_type(new_op_type);
- aop->SetOperationBlob(data_p, data_fd, data_file_size);
+ aop->SetOperationBlob(data_p, blob_file);
}
return true;
diff --git a/payload_generator/ab_generator.h b/payload_generator/ab_generator.h
index d027f22..1aa74af 100644
--- a/payload_generator/ab_generator.h
+++ b/payload_generator/ab_generator.h
@@ -12,6 +12,7 @@
#include <chromeos/secure_blob.h>
#include "update_engine/payload_constants.h"
+#include "update_engine/payload_generator/blob_file_writer.h"
#include "update_engine/payload_generator/extent_utils.h"
#include "update_engine/payload_generator/filesystem_interface.h"
#include "update_engine/payload_generator/operations_generator.h"
@@ -34,13 +35,10 @@
// write the new image on the target partition, also possibly in random order.
// The rootfs operations are stored in |rootfs_ops| and should be executed in
// that order. The kernel operations are stored in |kernel_ops|. All
- // the offsets in the operations reference the data written to |data_file_fd|.
- // The total amount of data written to that file is stored in
- // |data_file_size|.
+ // the offsets in the operations reference the data written to |blob_file|.
bool GenerateOperations(
const PayloadGenerationConfig& config,
- int data_file_fd,
- off_t* data_file_size,
+ BlobFileWriter* blob_file,
std::vector<AnnotatedOperation>* rootfs_ops,
std::vector<AnnotatedOperation>* kernel_ops) override;
@@ -50,13 +48,10 @@
// fragmented except BSDIFF and SOURCE_BSDIFF operations.
// The |target_rootfs_part| is the filename of the new image, where the
// destination extents refer to. The blobs of the operations in |aops| should
- // reference the file |data_fd| whose initial size is |*data_file_size|. The
- // file contents and the value pointed by |data_file_size| are updated if
- // needed.
+ // reference |blob_file|. |blob_file| are updated if needed.
static bool FragmentOperations(std::vector<AnnotatedOperation>* aops,
const std::string& target_rootfs_part,
- int data_fd,
- off_t* data_file_size);
+ BlobFileWriter* blob_file);
// Takes a vector of AnnotatedOperations |aops| and sorts them by the first
// start block in their destination extents. Sets |aops| to a vector of the
@@ -84,33 +79,32 @@
const AnnotatedOperation& original_aop,
std::vector<AnnotatedOperation>* result_aops,
const std::string& target_part,
- int data_fd,
- off_t* data_file_size);
+ BlobFileWriter* blob_file);
// Takes a sorted (by first destination extent) vector of operations |aops|
// and merges SOURCE_COPY, REPLACE, and REPLACE_BZ operations in that vector.
// It will merge two operations if:
// - They are of the same type.
// - They are contiguous.
- // - Their combined blocks do not exceed |chunk_size|.
+ // - Their combined blocks do not exceed |chunk_blocks| blocks.
+ // Note that unlike other methods, you can't pass a negative number in
+ // |chunk_blocks|.
static bool MergeOperations(std::vector<AnnotatedOperation>* aops,
- off_t chunk_size,
+ size_t chunk_blocks,
const std::string& target_part,
- int data_fd,
- off_t* data_file_size);
+ BlobFileWriter* blob_file);
private:
// Adds the data payload for a REPLACE/REPLACE_BZ operation |aop| by reading
// its output extents from |target_part_path| and appending a corresponding
// data blob to |data_fd|. The blob will be compressed if this is smaller than
// the uncompressed form, and the operation type will be set accordingly.
- // |*data_file_size| will be updated as well. If the operation happens to have
- // the right type and already points to a data blob, we check whether its
- // content is identical to the new one, in which case nothing is written.
+ // |*blob_file| will be updated as well. If the operation happens to have
+ // the right type and already points to a data blob, nothing is written.
+ // Caller should only set type and data blob if it's valid.
static bool AddDataAndSetType(AnnotatedOperation* aop,
const std::string& target_part_path,
- int data_fd,
- off_t* data_file_size);
+ BlobFileWriter* blob_file);
DISALLOW_COPY_AND_ASSIGN(ABGenerator);
};
diff --git a/payload_generator/ab_generator_unittest.cc b/payload_generator/ab_generator_unittest.cc
index 9241701..b06ba63 100644
--- a/payload_generator/ab_generator_unittest.cc
+++ b/payload_generator/ab_generator_unittest.cc
@@ -33,9 +33,8 @@
}
// Tests splitting of a REPLACE/REPLACE_BZ operation.
-void TestSplitReplaceOrReplaceBzOperation(
- DeltaArchiveManifest_InstallOperation_Type orig_type,
- bool compressible) {
+void TestSplitReplaceOrReplaceBzOperation(InstallOperation_Type orig_type,
+ bool compressible) {
const size_t op_ex1_start_block = 2;
const size_t op_ex1_num_blocks = 2;
const size_t op_ex2_start_block = 6;
@@ -66,7 +65,7 @@
const size_t op_ex1_size = op_ex1_num_blocks * kBlockSize;
const size_t op_ex2_offset = op_ex2_start_block * kBlockSize;
const size_t op_ex2_size = op_ex2_num_blocks * kBlockSize;
- DeltaArchiveManifest_InstallOperation op;
+ InstallOperation op;
op.set_type(orig_type);
*(op.add_dst_extents()) = ExtentForRange(op_ex1_start_block,
op_ex1_num_blocks);
@@ -82,7 +81,7 @@
part_data.begin() + op_ex2_offset,
part_data.begin() + op_ex2_offset + op_ex2_size);
chromeos::Blob op_blob;
- if (orig_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
+ if (orig_type == InstallOperation::REPLACE) {
op_blob = op_data;
} else {
ASSERT_TRUE(BzipCompress(op_data, &op_blob));
@@ -105,22 +104,21 @@
EXPECT_TRUE(utils::WriteFile(data_path.c_str(), op_blob.data(),
op_blob.size()));
off_t data_file_size = op_blob.size();
+ BlobFileWriter blob_file(data_fd, &data_file_size);
// Split the operation.
vector<AnnotatedOperation> result_ops;
ASSERT_TRUE(ABGenerator::SplitReplaceOrReplaceBz(
- aop, &result_ops, part_path, data_fd, &data_file_size));
+ aop, &result_ops, part_path, &blob_file));
// Check the result.
- DeltaArchiveManifest_InstallOperation_Type expected_type =
- compressible ?
- DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ :
- DeltaArchiveManifest_InstallOperation_Type_REPLACE;
+ InstallOperation_Type expected_type =
+ compressible ? InstallOperation::REPLACE_BZ : InstallOperation::REPLACE;
ASSERT_EQ(2, result_ops.size());
EXPECT_EQ("SplitTestOp:0", result_ops[0].name);
- DeltaArchiveManifest_InstallOperation first_op = result_ops[0].op;
+ InstallOperation first_op = result_ops[0].op;
EXPECT_EQ(expected_type, first_op.type());
EXPECT_EQ(op_ex1_size, first_op.dst_length());
EXPECT_EQ(1, first_op.dst_extents().size());
@@ -149,7 +147,7 @@
EXPECT_EQ(first_expected_blob, first_data_blob);
EXPECT_EQ("SplitTestOp:1", result_ops[1].name);
- DeltaArchiveManifest_InstallOperation second_op = result_ops[1].op;
+ InstallOperation second_op = result_ops[1].op;
EXPECT_EQ(expected_type, second_op.type());
EXPECT_EQ(op_ex2_size, second_op.dst_length());
EXPECT_EQ(1, second_op.dst_extents().size());
@@ -181,16 +179,14 @@
second_op.data_offset());
EXPECT_EQ(second_op.data_offset() + second_op.data_length(), data_file_size);
// If we split a REPLACE into multiple ones, ensure reuse of preexisting blob.
- if (!compressible &&
- orig_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
+ if (!compressible && orig_type == InstallOperation::REPLACE) {
EXPECT_EQ(0, first_op.data_offset());
}
}
// Tests merging of REPLACE/REPLACE_BZ operations.
-void TestMergeReplaceOrReplaceBzOperations(
- DeltaArchiveManifest_InstallOperation_Type orig_type,
- bool compressible) {
+void TestMergeReplaceOrReplaceBzOperations(InstallOperation_Type orig_type,
+ bool compressible) {
const size_t first_op_num_blocks = 1;
const size_t second_op_num_blocks = 2;
const size_t total_op_num_blocks = first_op_num_blocks + second_op_num_blocks;
@@ -220,7 +216,7 @@
chromeos::Blob blob_data;
const size_t total_op_size = total_op_num_blocks * kBlockSize;
- DeltaArchiveManifest_InstallOperation first_op;
+ InstallOperation first_op;
first_op.set_type(orig_type);
const size_t first_op_size = first_op_num_blocks * kBlockSize;
first_op.set_dst_length(first_op_size);
@@ -228,7 +224,7 @@
chromeos::Blob first_op_data(part_data.begin(),
part_data.begin() + first_op_size);
chromeos::Blob first_op_blob;
- if (orig_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
+ if (orig_type == InstallOperation::REPLACE) {
first_op_blob = first_op_data;
} else {
ASSERT_TRUE(BzipCompress(first_op_data, &first_op_blob));
@@ -241,7 +237,7 @@
first_aop.name = "first";
aops.push_back(first_aop);
- DeltaArchiveManifest_InstallOperation second_op;
+ InstallOperation second_op;
second_op.set_type(orig_type);
const size_t second_op_size = second_op_num_blocks * kBlockSize;
second_op.set_dst_length(second_op_size);
@@ -250,7 +246,7 @@
chromeos::Blob second_op_data(part_data.begin() + first_op_size,
part_data.begin() + total_op_size);
chromeos::Blob second_op_blob;
- if (orig_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
+ if (orig_type == InstallOperation::REPLACE) {
second_op_blob = second_op_data;
} else {
ASSERT_TRUE(BzipCompress(second_op_data, &second_op_blob));
@@ -275,18 +271,17 @@
EXPECT_TRUE(utils::WriteFile(data_path.c_str(), blob_data.data(),
blob_data.size()));
off_t data_file_size = blob_data.size();
+ BlobFileWriter blob_file(data_fd, &data_file_size);
// Merge the operations.
EXPECT_TRUE(ABGenerator::MergeOperations(
- &aops, 5 * kBlockSize, part_path, data_fd, &data_file_size));
+ &aops, 5, part_path, &blob_file));
// Check the result.
- DeltaArchiveManifest_InstallOperation_Type expected_op_type =
- compressible ?
- DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ :
- DeltaArchiveManifest_InstallOperation_Type_REPLACE;
+ InstallOperation_Type expected_op_type =
+ compressible ? InstallOperation::REPLACE_BZ : InstallOperation::REPLACE;
EXPECT_EQ(1, aops.size());
- DeltaArchiveManifest_InstallOperation new_op = aops[0].op;
+ InstallOperation new_op = aops[0].op;
EXPECT_EQ(expected_op_type, new_op.type());
EXPECT_FALSE(new_op.has_src_length());
EXPECT_EQ(total_op_num_blocks * kBlockSize, new_op.dst_length());
@@ -321,8 +316,8 @@
class ABGeneratorTest : public ::testing::Test {};
TEST_F(ABGeneratorTest, SplitSourceCopyTest) {
- DeltaArchiveManifest_InstallOperation op;
- op.set_type(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
+ InstallOperation op;
+ op.set_type(InstallOperation::SOURCE_COPY);
*(op.add_src_extents()) = ExtentForRange(2, 3);
*(op.add_src_extents()) = ExtentForRange(6, 1);
*(op.add_src_extents()) = ExtentForRange(8, 4);
@@ -338,9 +333,8 @@
EXPECT_EQ(result_ops.size(), 3);
EXPECT_EQ("SplitSourceCopyTestOp:0", result_ops[0].name);
- DeltaArchiveManifest_InstallOperation first_op = result_ops[0].op;
- EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY,
- first_op.type());
+ InstallOperation first_op = result_ops[0].op;
+ EXPECT_EQ(InstallOperation::SOURCE_COPY, first_op.type());
EXPECT_EQ(kBlockSize * 2, first_op.src_length());
EXPECT_EQ(1, first_op.src_extents().size());
EXPECT_EQ(2, first_op.src_extents(0).start_block());
@@ -351,9 +345,8 @@
EXPECT_EQ(2, first_op.dst_extents(0).num_blocks());
EXPECT_EQ("SplitSourceCopyTestOp:1", result_ops[1].name);
- DeltaArchiveManifest_InstallOperation second_op = result_ops[1].op;
- EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY,
- second_op.type());
+ InstallOperation second_op = result_ops[1].op;
+ EXPECT_EQ(InstallOperation::SOURCE_COPY, second_op.type());
EXPECT_EQ(kBlockSize * 3, second_op.src_length());
EXPECT_EQ(3, second_op.src_extents().size());
EXPECT_EQ(4, second_op.src_extents(0).start_block());
@@ -368,9 +361,8 @@
EXPECT_EQ(3, second_op.dst_extents(0).num_blocks());
EXPECT_EQ("SplitSourceCopyTestOp:2", result_ops[2].name);
- DeltaArchiveManifest_InstallOperation third_op = result_ops[2].op;
- EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY,
- third_op.type());
+ InstallOperation third_op = result_ops[2].op;
+ EXPECT_EQ(InstallOperation::SOURCE_COPY, third_op.type());
EXPECT_EQ(kBlockSize * 3, third_op.src_length());
EXPECT_EQ(1, third_op.src_extents().size());
EXPECT_EQ(9, third_op.src_extents(0).start_block());
@@ -382,29 +374,25 @@
}
TEST_F(ABGeneratorTest, SplitReplaceTest) {
- TestSplitReplaceOrReplaceBzOperation(
- DeltaArchiveManifest_InstallOperation_Type_REPLACE, false);
+ TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE, false);
}
TEST_F(ABGeneratorTest, SplitReplaceIntoReplaceBzTest) {
- TestSplitReplaceOrReplaceBzOperation(
- DeltaArchiveManifest_InstallOperation_Type_REPLACE, true);
+ TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE, true);
}
TEST_F(ABGeneratorTest, SplitReplaceBzTest) {
- TestSplitReplaceOrReplaceBzOperation(
- DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ, true);
+ TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE_BZ, true);
}
TEST_F(ABGeneratorTest, SplitReplaceBzIntoReplaceTest) {
- TestSplitReplaceOrReplaceBzOperation(
- DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ, false);
+ TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE_BZ, false);
}
TEST_F(ABGeneratorTest, SortOperationsByDestinationTest) {
vector<AnnotatedOperation> aops;
// One operation with multiple destination extents.
- DeltaArchiveManifest_InstallOperation first_op;
+ InstallOperation first_op;
*(first_op.add_dst_extents()) = ExtentForRange(6, 1);
*(first_op.add_dst_extents()) = ExtentForRange(10, 2);
AnnotatedOperation first_aop;
@@ -413,14 +401,14 @@
aops.push_back(first_aop);
// One with no destination extent. Should end up at the end of the vector.
- DeltaArchiveManifest_InstallOperation second_op;
+ InstallOperation second_op;
AnnotatedOperation second_aop;
second_aop.op = second_op;
second_aop.name = "second";
aops.push_back(second_aop);
// One with one destination extent.
- DeltaArchiveManifest_InstallOperation third_op;
+ InstallOperation third_op;
*(third_op.add_dst_extents()) = ExtentForRange(3, 2);
AnnotatedOperation third_aop;
third_aop.op = third_op;
@@ -436,8 +424,8 @@
TEST_F(ABGeneratorTest, MergeSourceCopyOperationsTest) {
vector<AnnotatedOperation> aops;
- DeltaArchiveManifest_InstallOperation first_op;
- first_op.set_type(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
+ InstallOperation first_op;
+ first_op.set_type(InstallOperation::SOURCE_COPY);
first_op.set_src_length(kBlockSize);
first_op.set_dst_length(kBlockSize);
*(first_op.add_src_extents()) = ExtentForRange(1, 1);
@@ -447,8 +435,8 @@
first_aop.name = "1";
aops.push_back(first_aop);
- DeltaArchiveManifest_InstallOperation second_op;
- second_op.set_type(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
+ InstallOperation second_op;
+ second_op.set_type(InstallOperation::SOURCE_COPY);
second_op.set_src_length(3 * kBlockSize);
second_op.set_dst_length(3 * kBlockSize);
*(second_op.add_src_extents()) = ExtentForRange(2, 2);
@@ -460,8 +448,8 @@
second_aop.name = "2";
aops.push_back(second_aop);
- DeltaArchiveManifest_InstallOperation third_op;
- third_op.set_type(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
+ InstallOperation third_op;
+ third_op.set_type(InstallOperation::SOURCE_COPY);
third_op.set_src_length(kBlockSize);
third_op.set_dst_length(kBlockSize);
*(third_op.add_src_extents()) = ExtentForRange(11, 1);
@@ -471,13 +459,12 @@
third_aop.name = "3";
aops.push_back(third_aop);
- EXPECT_TRUE(ABGenerator::MergeOperations(&aops, 5 * kBlockSize,
- "", 0, nullptr));
+ BlobFileWriter blob_file(0, nullptr);
+ EXPECT_TRUE(ABGenerator::MergeOperations(&aops, 5, "", &blob_file));
EXPECT_EQ(aops.size(), 1);
- DeltaArchiveManifest_InstallOperation first_result_op = aops[0].op;
- EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY,
- first_result_op.type());
+ InstallOperation first_result_op = aops[0].op;
+ EXPECT_EQ(InstallOperation::SOURCE_COPY, first_result_op.type());
EXPECT_EQ(kBlockSize * 5, first_result_op.src_length());
EXPECT_EQ(3, first_result_op.src_extents().size());
EXPECT_TRUE(ExtentEquals(first_result_op.src_extents(0), 1, 3));
@@ -491,30 +478,26 @@
}
TEST_F(ABGeneratorTest, MergeReplaceOperationsTest) {
- TestMergeReplaceOrReplaceBzOperations(
- DeltaArchiveManifest_InstallOperation_Type_REPLACE, false);
+ TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE, false);
}
TEST_F(ABGeneratorTest, MergeReplaceOperationsToReplaceBzTest) {
- TestMergeReplaceOrReplaceBzOperations(
- DeltaArchiveManifest_InstallOperation_Type_REPLACE, true);
+ TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE, true);
}
TEST_F(ABGeneratorTest, MergeReplaceBzOperationsTest) {
- TestMergeReplaceOrReplaceBzOperations(
- DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ, true);
+ TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE_BZ, true);
}
TEST_F(ABGeneratorTest, MergeReplaceBzOperationsToReplaceTest) {
- TestMergeReplaceOrReplaceBzOperations(
- DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ, false);
+ TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE_BZ, false);
}
TEST_F(ABGeneratorTest, NoMergeOperationsTest) {
// Test to make sure we don't merge operations that shouldn't be merged.
vector<AnnotatedOperation> aops;
- DeltaArchiveManifest_InstallOperation first_op;
- first_op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
+ InstallOperation first_op;
+ first_op.set_type(InstallOperation::REPLACE_BZ);
*(first_op.add_dst_extents()) = ExtentForRange(0, 1);
first_op.set_data_length(kBlockSize);
AnnotatedOperation first_aop;
@@ -522,8 +505,8 @@
aops.push_back(first_aop);
// Should merge with first, except op types don't match...
- DeltaArchiveManifest_InstallOperation second_op;
- second_op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
+ InstallOperation second_op;
+ second_op.set_type(InstallOperation::REPLACE);
*(second_op.add_dst_extents()) = ExtentForRange(1, 2);
second_op.set_data_length(2 * kBlockSize);
AnnotatedOperation second_aop;
@@ -531,8 +514,8 @@
aops.push_back(second_aop);
// Should merge with second, except it would exceed chunk size...
- DeltaArchiveManifest_InstallOperation third_op;
- third_op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
+ InstallOperation third_op;
+ third_op.set_type(InstallOperation::REPLACE);
*(third_op.add_dst_extents()) = ExtentForRange(3, 3);
third_op.set_data_length(3 * kBlockSize);
AnnotatedOperation third_aop;
@@ -540,16 +523,16 @@
aops.push_back(third_aop);
// Should merge with third, except they aren't contiguous...
- DeltaArchiveManifest_InstallOperation fourth_op;
- fourth_op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
+ InstallOperation fourth_op;
+ fourth_op.set_type(InstallOperation::REPLACE);
*(fourth_op.add_dst_extents()) = ExtentForRange(7, 2);
fourth_op.set_data_length(2 * kBlockSize);
AnnotatedOperation fourth_aop;
fourth_aop.op = fourth_op;
aops.push_back(fourth_aop);
- EXPECT_TRUE(ABGenerator::MergeOperations(
- &aops, 4 * kBlockSize, "", 0, nullptr));
+ BlobFileWriter blob_file(0, nullptr);
+ EXPECT_TRUE(ABGenerator::MergeOperations(&aops, 4, "", &blob_file));
// No operations were merged, the number of ops is the same.
EXPECT_EQ(aops.size(), 4);
diff --git a/payload_generator/annotated_operation.cc b/payload_generator/annotated_operation.cc
index 2aa79fc..6c7bf10 100644
--- a/payload_generator/annotated_operation.cc
+++ b/payload_generator/annotated_operation.cc
@@ -25,33 +25,34 @@
}
} // namespace
-bool AnnotatedOperation::SetOperationBlob(chromeos::Blob* blob, int data_fd,
- off_t* data_file_size) {
- TEST_AND_RETURN_FALSE(utils::PWriteAll(data_fd,
- blob->data(),
- blob->size(),
- *data_file_size));
+bool AnnotatedOperation::SetOperationBlob(chromeos::Blob* blob,
+ BlobFileWriter* blob_file) {
op.set_data_length(blob->size());
- op.set_data_offset(*data_file_size);
- *data_file_size += blob->size();
+ off_t data_offset = blob_file->StoreBlob(*blob);
+ if (data_offset == -1)
+ return false;
+ op.set_data_offset(data_offset);
return true;
}
-string InstallOperationTypeName(
- DeltaArchiveManifest_InstallOperation_Type op_type) {
+string InstallOperationTypeName(InstallOperation_Type op_type) {
switch (op_type) {
- case DeltaArchiveManifest_InstallOperation_Type_BSDIFF:
+ case InstallOperation::BSDIFF:
return "BSDIFF";
- case DeltaArchiveManifest_InstallOperation_Type_MOVE:
+ case InstallOperation::MOVE:
return "MOVE";
- case DeltaArchiveManifest_InstallOperation_Type_REPLACE:
+ case InstallOperation::REPLACE:
return "REPLACE";
- case DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ:
+ case InstallOperation::REPLACE_BZ:
return "REPLACE_BZ";
- case DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY:
+ case InstallOperation::SOURCE_COPY:
return "SOURCE_COPY";
- case DeltaArchiveManifest_InstallOperation_Type_SOURCE_BSDIFF:
+ case InstallOperation::SOURCE_BSDIFF:
return "SOURCE_BSDIFF";
+ case InstallOperation::ZERO:
+ return "ZERO";
+ case InstallOperation::DISCARD:
+ return "DISCARD";
}
return "UNK";
}
diff --git a/payload_generator/annotated_operation.h b/payload_generator/annotated_operation.h
index bacc0ab..e28c0e8 100644
--- a/payload_generator/annotated_operation.h
+++ b/payload_generator/annotated_operation.h
@@ -9,6 +9,8 @@
#include <string>
#include <chromeos/secure_blob.h>
+
+#include "update_engine/payload_generator/blob_file_writer.h"
#include "update_engine/update_metadata.pb.h"
namespace chromeos_update_engine {
@@ -19,20 +21,18 @@
std::string name;
// The InstallOperation, as defined by the protobuf.
- DeltaArchiveManifest_InstallOperation op;
+ InstallOperation op;
// Writes |blob| to the end of |data_fd|, and updates |data_file_size| to
// match the new size of |data_fd|. It sets the data_offset and data_length
// in AnnotatedOperation to match the offset and size of |blob| in |data_fd|.
- bool SetOperationBlob(chromeos::Blob* blob, int data_fd,
- off_t* data_file_size);
+ bool SetOperationBlob(chromeos::Blob* blob, BlobFileWriter* blob_file);
};
// For logging purposes.
std::ostream& operator<<(std::ostream& os, const AnnotatedOperation& aop);
-std::string InstallOperationTypeName(
- DeltaArchiveManifest_InstallOperation_Type op_type);
+std::string InstallOperationTypeName(InstallOperation_Type op_type);
} // namespace chromeos_update_engine
diff --git a/payload_generator/blob_file_writer.cc b/payload_generator/blob_file_writer.cc
new file mode 100644
index 0000000..2032e14
--- /dev/null
+++ b/payload_generator/blob_file_writer.cc
@@ -0,0 +1,34 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/payload_generator/blob_file_writer.h"
+
+#include "update_engine/utils.h"
+
+namespace chromeos_update_engine {
+
+off_t BlobFileWriter::StoreBlob(const chromeos::Blob& blob) {
+ base::AutoLock auto_lock(blob_mutex_);
+ if (!utils::PWriteAll(blob_fd_, blob.data(), blob.size(), *blob_file_size_))
+ return -1;
+
+ off_t result = *blob_file_size_;
+ *blob_file_size_ += blob.size();
+
+ stored_blobs_++;
+ if (total_blobs_ > 0 &&
+ (10 * (stored_blobs_ - 1) / total_blobs_) !=
+ (10 * stored_blobs_ / total_blobs_)) {
+ LOG(INFO) << (100 * stored_blobs_ / total_blobs_)
+ << "% complete " << stored_blobs_ << "/" << total_blobs_
+ << " ops (output size: " << *blob_file_size_ << ")";
+ }
+ return result;
+}
+
+void BlobFileWriter::SetTotalBlobs(size_t total_blobs) {
+ total_blobs_ = total_blobs;
+}
+
+} // namespace chromeos_update_engine
diff --git a/payload_generator/blob_file_writer.h b/payload_generator/blob_file_writer.h
new file mode 100644
index 0000000..be8fd0d
--- /dev/null
+++ b/payload_generator/blob_file_writer.h
@@ -0,0 +1,46 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UPDATE_ENGINE_PAYLOAD_GENERATOR_BLOB_FILE_WRITER_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_BLOB_FILE_WRITER_H_
+
+#include <base/macros.h>
+
+#include <base/synchronization/lock.h>
+#include <chromeos/secure_blob.h>
+
+namespace chromeos_update_engine {
+
+class BlobFileWriter {
+ public:
+ // Create the BlobFileWriter object that will manage the blobs stored to
+ // |blob_fd| in a thread safe way.
+ BlobFileWriter(int blob_fd, off_t* blob_file_size)
+ : blob_fd_(blob_fd),
+ blob_file_size_(blob_file_size) {}
+
+ // Store the passed |blob| in the blob file. Returns the offset at which it
+ // was stored, or -1 in case of failure.
+ off_t StoreBlob(const chromeos::Blob& blob);
+
+ // The number of |total_blobs| is the number of blobs that will be stored but
+ // is only used for logging purposes. If not set, logging will be skipped.
+ void SetTotalBlobs(size_t total_blobs);
+
+ private:
+ size_t total_blobs_{0};
+ size_t stored_blobs_{0};
+
+ // The file and its size are protected with the |blob_mutex_|.
+ int blob_fd_;
+ off_t* blob_file_size_;
+
+ base::Lock blob_mutex_;
+
+ DISALLOW_COPY_AND_ASSIGN(BlobFileWriter);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_PAYLOAD_GENERATOR_BLOB_FILE_WRITER_H_
diff --git a/payload_generator/blob_file_writer_unittest.cc b/payload_generator/blob_file_writer_unittest.cc
new file mode 100644
index 0000000..c845f97
--- /dev/null
+++ b/payload_generator/blob_file_writer_unittest.cc
@@ -0,0 +1,47 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/payload_generator/blob_file_writer.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/test_utils.h"
+#include "update_engine/utils.h"
+
+using chromeos_update_engine::test_utils::FillWithData;
+using std::string;
+
+namespace chromeos_update_engine {
+
+class BlobFileWriterTest : public ::testing::Test {};
+
+TEST(BlobFileWriterTest, SimpleTest) {
+ string blob_path;
+ int blob_fd;
+ EXPECT_TRUE(utils::MakeTempFile("BlobFileWriterTest.XXXXXX",
+ &blob_path,
+ &blob_fd));
+ off_t blob_file_size = 0;
+ BlobFileWriter blob_file(blob_fd, &blob_file_size);
+
+ off_t blob_size = 1024;
+ chromeos::Blob blob(blob_size);
+ FillWithData(&blob);
+ EXPECT_EQ(0, blob_file.StoreBlob(blob));
+ EXPECT_EQ(blob_size, blob_file.StoreBlob(blob));
+
+ chromeos::Blob stored_blob(blob_size);
+ ssize_t bytes_read;
+ ASSERT_TRUE(utils::PReadAll(blob_fd,
+ stored_blob.data(),
+ blob_size,
+ 0,
+ &bytes_read));
+ EXPECT_EQ(bytes_read, blob_size);
+ EXPECT_EQ(blob, stored_blob);
+}
+
+} // namespace chromeos_update_engine
diff --git a/payload_generator/block_mapping.cc b/payload_generator/block_mapping.cc
new file mode 100644
index 0000000..734f44f
--- /dev/null
+++ b/payload_generator/block_mapping.cc
@@ -0,0 +1,149 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/payload_generator/block_mapping.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include "update_engine/utils.h"
+
+using std::string;
+using std::vector;
+
+namespace {
+
+size_t HashValue(const chromeos::Blob& blob) {
+ std::hash<string> hash_fn;
+ return hash_fn(string(blob.begin(), blob.end()));
+}
+
+} // namespace
+
+namespace chromeos_update_engine {
+
+BlockMapping::BlockId BlockMapping::AddBlock(const chromeos::Blob& block_data) {
+ return AddBlock(-1, 0, block_data);
+}
+
+BlockMapping::BlockId BlockMapping::AddDiskBlock(int fd, off_t byte_offset) {
+ chromeos::Blob blob(block_size_);
+ ssize_t bytes_read = 0;
+ if (!utils::PReadAll(fd, blob.data(), block_size_, byte_offset, &bytes_read))
+ return -1;
+ if (static_cast<size_t>(bytes_read) != block_size_)
+ return -1;
+ return AddBlock(fd, byte_offset, blob);
+}
+
+bool BlockMapping::AddManyDiskBlocks(int fd,
+ off_t initial_byte_offset,
+ size_t num_blocks,
+ std::vector<BlockId>* block_ids) {
+ bool ret = true;
+ block_ids->resize(num_blocks);
+ for (size_t block = 0; block < num_blocks; block++) {
+ (*block_ids)[block] = AddDiskBlock(
+ fd, initial_byte_offset + block * block_size_);
+ ret = ret && (*block_ids)[block] != -1;
+ }
+ return ret;
+}
+
+BlockMapping::BlockId BlockMapping::AddBlock(int fd,
+ off_t byte_offset,
+ const chromeos::Blob& block_data) {
+ if (block_data.size() != block_size_)
+ return -1;
+ size_t h = HashValue(block_data);
+
+ // We either reuse a UniqueBlock or create a new one. If we need a new
+ // UniqueBlock it could also be part of a new or existing bucket (if there is
+ // a hash collision).
+ vector<UniqueBlock> *bucket = nullptr;
+
+ auto mapping_it = mapping_.find(h);
+ if (mapping_it == mapping_.end()) {
+ bucket = &mapping_[h];
+ } else {
+ for (UniqueBlock& existing_block : mapping_it->second) {
+ bool equals = false;
+ if (!existing_block.CompareData(block_data, &equals))
+ return -1;
+ if (equals)
+ return existing_block.block_id;
+ }
+ bucket = &mapping_it->second;
+ }
+
+ // No existing block was found at this point, so we create and fill in a new
+ // one.
+ bucket->emplace_back();
+ UniqueBlock *new_ublock = &bucket->back();
+
+ new_ublock->times_read = 1;
+ new_ublock->fd = fd;
+ new_ublock->byte_offset = byte_offset;
+ new_ublock->block_id = used_block_ids++;
+ // We need to cache blocks that are not referencing any disk location.
+ if (fd == -1)
+ new_ublock->block_data = block_data;
+
+ return new_ublock->block_id;
+}
+
+bool BlockMapping::UniqueBlock::CompareData(const chromeos::Blob& other_block,
+ bool* equals) {
+ if (!block_data.empty()) {
+ *equals = block_data == other_block;
+ return true;
+ }
+ const size_t block_size = other_block.size();
+ chromeos::Blob blob(block_size);
+ ssize_t bytes_read = 0;
+ if (!utils::PReadAll(fd, blob.data(), block_size, byte_offset, &bytes_read))
+ return false;
+ if (static_cast<size_t>(bytes_read) != block_size)
+ return false;
+ *equals = blob == other_block;
+
+ // We increase the number of times we had to read this block from disk and
+ // we cache this block based on that. This caching method is optimized for
+ // the common use case of having two partitions that share blocks between them
+ // but have few repeated blocks inside each partition, such as the block
+ // with all zeros or duplicated files.
+ times_read++;
+ if (times_read > 3)
+ block_data = std::move(blob);
+ return true;
+}
+
+bool MapPartitionBlocks(const string& old_part,
+ const string& new_part,
+ size_t old_size,
+ size_t new_size,
+ size_t block_size,
+ vector<BlockMapping::BlockId>* old_block_ids,
+ vector<BlockMapping::BlockId>* new_block_ids) {
+ BlockMapping mapping(block_size);
+ if (mapping.AddBlock(chromeos::Blob(block_size, '\0')) != 0)
+ return false;
+ int old_fd = HANDLE_EINTR(open(old_part.c_str(), O_RDONLY));
+ int new_fd = HANDLE_EINTR(open(new_part.c_str(), O_RDONLY));
+ ScopedFdCloser old_fd_closer(&old_fd);
+ ScopedFdCloser new_fd_closer(&new_fd);
+
+ TEST_AND_RETURN_FALSE(mapping.AddManyDiskBlocks(
+ old_fd, 0, old_size / block_size, old_block_ids));
+ TEST_AND_RETURN_FALSE(mapping.AddManyDiskBlocks(
+ new_fd, 0, new_size / block_size, new_block_ids));
+ return true;
+}
+
+} // namespace chromeos_update_engine
diff --git a/payload_generator/block_mapping.h b/payload_generator/block_mapping.h
new file mode 100644
index 0000000..2cfca56
--- /dev/null
+++ b/payload_generator/block_mapping.h
@@ -0,0 +1,100 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UPDATE_ENGINE_PAYLOAD_GENERATOR_BLOCK_MAPPING_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_BLOCK_MAPPING_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <chromeos/secure_blob.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "update_engine/payload_generator/payload_generation_config.h"
+
+namespace chromeos_update_engine {
+
+// BlockMapping allows to map data blocks (chromeos::Blobs of block_size size)
+// into unique integer values called "block ids". This mapping differs from a
+// hash function in that two blocks with the same data will have the same id but
+// also two blocks with the same id will have the same data. This is only valid
+// in the context of the same BlockMapping instance.
+class BlockMapping {
+ public:
+ using BlockId = int64_t;
+
+ explicit BlockMapping(size_t block_size) : block_size_(block_size) {}
+
+ // Add a single data block to the mapping. Returns its unique block id.
+ // In case of error returns -1.
+ BlockId AddBlock(const chromeos::Blob& block_data);
+
+ // Add a block from disk reading it from the file descriptor |fd| from the
+ // offset in bytes |byte_offset|. The data block may or may not be cached, so
+ // the file descriptor must be available until the BlockMapping is destroyed.
+ // Returns the unique block id of the added block or -1 in case of error.
+ BlockId AddDiskBlock(int fd, off_t byte_offset);
+
+ // This is a helper method to add |num_blocks| contiguous blocks reading them
+ // from the file descriptor |fd| starting at offset |initial_byte_offset|.
+ // Returns whether it succeeded to add all the disk blocks and stores in
+ // |block_ids| the block id for each one of the added blocks.
+ bool AddManyDiskBlocks(int fd, off_t initial_byte_offset, size_t num_blocks,
+ std::vector<BlockId>* block_ids);
+
+ private:
+ FRIEND_TEST(BlockMappingTest, BlocksAreNotKeptInMemory);
+
+ // Add a single block passed in |block_data|. If |fd| is not -1, the block
+ // can be discarded to save RAM and retrieved later from |fd| at the position
+ // |byte_offset|.
+ BlockId AddBlock(int fd, off_t byte_offset, const chromeos::Blob& block_data);
+
+ size_t block_size_;
+
+ BlockId used_block_ids{0};
+
+ // The UniqueBlock represents the data of a block associated to a unique
+ // block id.
+ struct UniqueBlock {
+ chromeos::Blob block_data;
+
+ // The block id assigned to this unique block.
+ BlockId block_id;
+
+ // The location on this unique block on disk (if not cached in block_data).
+ int fd{-1};
+ off_t byte_offset{0};
+
+ // Number of times we have seen this data block. Used for caching.
+ uint32_t times_read{0};
+
+ // Compares the UniqueBlock data with the other_block data and stores if
+ // they are equal in |equals|. Returns whether there was an error reading
+ // the block from disk while comparing it.
+ bool CompareData(const chromeos::Blob& other_block, bool* equals);
+ };
+
+ // A mapping from hash values to possible block ids.
+ std::map<size_t, std::vector<UniqueBlock>> mapping_;
+};
+
+// Maps the blocks of the old and new partitions |old_part| and |new_part| whose
+// size in bytes are |old_size| and |new_size| into block ids where two blocks
+// with the same data will have the same block id and vice versa, regardless of
+// the partition they are on.
+// The block ids number 0 corresponds to the block with all zeros, but any
+// other block id number is assigned randomly.
+bool MapPartitionBlocks(const std::string& old_part,
+ const std::string& new_part,
+ size_t old_size,
+ size_t new_size,
+ size_t block_size,
+ std::vector<BlockMapping::BlockId>* old_block_ids,
+ std::vector<BlockMapping::BlockId>* new_block_ids);
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_PAYLOAD_GENERATOR_BLOCK_MAPPING_H_
diff --git a/payload_generator/block_mapping_unittest.cc b/payload_generator/block_mapping_unittest.cc
new file mode 100644
index 0000000..699ac60
--- /dev/null
+++ b/payload_generator/block_mapping_unittest.cc
@@ -0,0 +1,122 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/payload_generator/block_mapping.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/test_utils.h"
+#include "update_engine/utils.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+} // namespace
+
+class BlockMappingTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ EXPECT_TRUE(utils::MakeTempFile("BlockMappingTest_old.XXXXXX",
+ &old_part_path_,
+ nullptr));
+ EXPECT_TRUE(utils::MakeTempFile("BlockMappingTest_new.XXXXXX",
+ &new_part_path_,
+ nullptr));
+
+ old_part_unlinker_.reset(new ScopedPathUnlinker(old_part_path_));
+ new_part_unlinker_.reset(new ScopedPathUnlinker(new_part_path_));
+ }
+
+ // Old new partition files used in testing.
+ string old_part_path_;
+ string new_part_path_;
+ std::unique_ptr<ScopedPathUnlinker> old_part_unlinker_;
+ std::unique_ptr<ScopedPathUnlinker> new_part_unlinker_;
+
+ size_t block_size_{1024};
+ BlockMapping bm_{block_size_}; // BlockMapping under test.
+};
+
+TEST_F(BlockMappingTest, FirstAddedBlockIsZero) {
+ chromeos::Blob blob(block_size_);
+ // The BlockMapping just assigns the block ids in order, so it doesn't matter
+ // what are the contents of the first block.
+ blob[0] = 42;
+ EXPECT_EQ(0, bm_.AddBlock(blob));
+ blob[0] = 5;
+ EXPECT_EQ(1, bm_.AddBlock(blob));
+}
+
+TEST_F(BlockMappingTest, BlocksAreNotKeptInMemory) {
+ test_utils::WriteFileString(old_part_path_, string(block_size_, 'a'));
+ int old_fd = HANDLE_EINTR(open(old_part_path_.c_str(), O_RDONLY));
+ ScopedFdCloser old_fd_closer(&old_fd);
+
+ EXPECT_EQ(0, bm_.AddDiskBlock(old_fd, 0));
+
+ // Check that the block_data is not stored on memory if we just used the block
+ // once.
+ for (const auto& it : bm_.mapping_) {
+ for (const BlockMapping::UniqueBlock& ublock : it.second) {
+ EXPECT_TRUE(ublock.block_data.empty());
+ }
+ }
+
+ chromeos::Blob block(block_size_, 'a');
+ for (int i = 0; i < 5; ++i) {
+ // Re-add the same block 5 times.
+ EXPECT_EQ(0, bm_.AddBlock(block));
+ }
+
+ for (const auto& it : bm_.mapping_) {
+ for (const BlockMapping::UniqueBlock& ublock : it.second) {
+ EXPECT_FALSE(ublock.block_data.empty());
+ // The block was loaded from disk only 4 times, and after that the counter
+ // is not updated anymore.
+ EXPECT_EQ(4, ublock.times_read);
+ }
+ }
+}
+
+TEST_F(BlockMappingTest, MapPartitionBlocks) {
+ // A string with 10 blocks where all the blocks are different.
+ string old_contents(10 * block_size_, '\0');
+ for (size_t i = 0; i < old_contents.size(); ++i)
+ old_contents[i] = 4 + i / block_size_;
+ test_utils::WriteFileString(old_part_path_, old_contents);
+
+ // A string including the block with all zeros and overlapping some of the
+ // other blocks in old_contents.
+ string new_contents(6 * block_size_, '\0');
+ for (size_t i = 0; i < new_contents.size(); ++i)
+ new_contents[i] = i / block_size_;
+ test_utils::WriteFileString(new_part_path_, new_contents);
+
+ vector<BlockMapping::BlockId> old_ids, new_ids;
+ EXPECT_TRUE(MapPartitionBlocks(old_part_path_,
+ new_part_path_,
+ old_contents.size(),
+ new_contents.size(),
+ block_size_,
+ &old_ids,
+ &new_ids));
+
+ EXPECT_EQ((vector<BlockMapping::BlockId>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}),
+ old_ids);
+ EXPECT_EQ((vector<BlockMapping::BlockId>{0, 11, 12, 13, 1, 2}),
+ new_ids);
+}
+
+} // namespace chromeos_update_engine
diff --git a/payload_generator/cycle_breaker.cc b/payload_generator/cycle_breaker.cc
index 906502d..9c92161 100644
--- a/payload_generator/cycle_breaker.cc
+++ b/payload_generator/cycle_breaker.cc
@@ -43,9 +43,9 @@
skipped_ops_ = 0;
for (Graph::size_type i = 0; i < subgraph_.size(); i++) {
- DeltaArchiveManifest_InstallOperation_Type op_type = graph[i].op.type();
- if (op_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
- op_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
+ InstallOperation_Type op_type = graph[i].aop.op.type();
+ if (op_type == InstallOperation::REPLACE ||
+ op_type == InstallOperation::REPLACE_BZ) {
skipped_ops_++;
continue;
}
diff --git a/payload_generator/cycle_breaker_unittest.cc b/payload_generator/cycle_breaker_unittest.cc
index b58bb08..69443ed 100644
--- a/payload_generator/cycle_breaker_unittest.cc
+++ b/payload_generator/cycle_breaker_unittest.cc
@@ -25,8 +25,8 @@
namespace {
void SetOpForNodes(Graph* graph) {
- for (Graph::iterator it = graph->begin(), e = graph->end(); it != e; ++it) {
- it->op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
+ for (Vertex& vertex : *graph) {
+ vertex.aop.op.set_type(InstallOperation::MOVE);
}
}
} // namespace
@@ -249,8 +249,8 @@
Graph graph(kNodeCount);
SetOpForNodes(&graph);
- graph[n_a].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
- graph[n_c].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
+ graph[n_a].aop.op.set_type(InstallOperation::REPLACE_BZ);
+ graph[n_c].aop.op.set_type(InstallOperation::REPLACE);
graph[n_a].out_edges.insert(EdgeWithWeight(n_b, 1));
graph[n_c].out_edges.insert(EdgeWithWeight(n_b, 1));
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index dbb63a8..ca8fb38 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -21,6 +21,7 @@
#include "update_engine/delta_performer.h"
#include "update_engine/payload_constants.h"
#include "update_engine/payload_generator/ab_generator.h"
+#include "update_engine/payload_generator/blob_file_writer.h"
#include "update_engine/payload_generator/delta_diff_utils.h"
#include "update_engine/payload_generator/full_update_generator.h"
#include "update_engine/payload_generator/inplace_generator.h"
@@ -115,10 +116,10 @@
{
ScopedFdCloser data_file_fd_closer(&data_file_fd);
+ BlobFileWriter blob_file(data_file_fd, &data_file_size);
// Generate the operations using the strategy we selected above.
TEST_AND_RETURN_FALSE(strategy->GenerateOperations(config,
- data_file_fd,
- &data_file_size,
+ &blob_file,
&rootfs_ops,
&kernel_ops));
}
diff --git a/payload_generator/delta_diff_utils.cc b/payload_generator/delta_diff_utils.cc
index 6c15919..4b39806 100644
--- a/payload_generator/delta_diff_utils.cc
+++ b/payload_generator/delta_diff_utils.cc
@@ -13,6 +13,7 @@
#include "update_engine/bzip.h"
#include "update_engine/omaha_hash_calculator.h"
+#include "update_engine/payload_generator/block_mapping.h"
#include "update_engine/payload_generator/delta_diff_generator.h"
#include "update_engine/payload_generator/extent_ranges.h"
#include "update_engine/payload_generator/extent_utils.h"
@@ -21,7 +22,6 @@
using std::map;
using std::string;
-using std::unique_ptr;
using std::vector;
namespace chromeos_update_engine {
@@ -142,23 +142,24 @@
vector<AnnotatedOperation>* aops,
const PartitionConfig& old_part,
const PartitionConfig& new_part,
- off_t chunk_blocks,
- int data_fd,
- off_t* data_file_size,
- bool skip_block_0,
+ ssize_t hard_chunk_blocks,
+ size_t soft_chunk_blocks,
+ BlobFileWriter* blob_file,
bool src_ops_allowed) {
ExtentRanges old_visited_blocks;
ExtentRanges new_visited_blocks;
- // We can't produce a MOVE operation with a 0 block as neither source nor
- // destination, so we avoid generating an operation for the block 0 here, and
- // we will add an operation for it in the InplaceGenerator. Excluding both
- // old and new blocks ensures that identical images would still produce empty
- // deltas.
- if (skip_block_0) {
- old_visited_blocks.AddBlock(0);
- new_visited_blocks.AddBlock(0);
- }
+ TEST_AND_RETURN_FALSE(DeltaMovedAndZeroBlocks(
+ aops,
+ old_part.path,
+ new_part.path,
+ old_part.fs_interface ? old_part.fs_interface->GetBlockCount() : 0,
+ new_part.fs_interface->GetBlockCount(),
+ soft_chunk_blocks,
+ src_ops_allowed,
+ blob_file,
+ &old_visited_blocks,
+ &new_visited_blocks));
map<string, vector<Extent>> old_files_map;
if (old_part.fs_interface) {
@@ -213,9 +214,8 @@
old_file_extents,
new_file_extents,
new_file.name, // operation name
- chunk_blocks,
- data_fd,
- data_file_size,
+ hard_chunk_blocks,
+ blob_file,
src_ops_allowed));
}
// Process all the blocks not included in any file. We provided all the unused
@@ -233,7 +233,11 @@
}
LOG(INFO) << "Scanning " << BlocksInExtents(new_unvisited)
- << " unwritten blocks";
+ << " unwritten blocks using chunk size of "
+ << soft_chunk_blocks << " blocks.";
+ // We use the soft_chunk_blocks limit for the <non-file-data> as we don't
+ // really know the structure of this data and we should not expect it to have
+ // redundancy between partitions.
TEST_AND_RETURN_FALSE(DeltaReadFile(
aops,
old_part.path,
@@ -241,14 +245,156 @@
old_unvisited,
new_unvisited,
"<non-file-data>", // operation name
- chunk_blocks,
- data_fd,
- data_file_size,
+ soft_chunk_blocks,
+ blob_file,
src_ops_allowed));
return true;
}
+bool DeltaMovedAndZeroBlocks(
+ vector<AnnotatedOperation>* aops,
+ const string& old_part,
+ const string& new_part,
+ size_t old_num_blocks,
+ size_t new_num_blocks,
+ ssize_t chunk_blocks,
+ bool src_ops_allowed,
+ BlobFileWriter* blob_file,
+ ExtentRanges* old_visited_blocks,
+ ExtentRanges* new_visited_blocks) {
+ vector<BlockMapping::BlockId> old_block_ids;
+ vector<BlockMapping::BlockId> new_block_ids;
+ TEST_AND_RETURN_FALSE(MapPartitionBlocks(old_part,
+ new_part,
+ old_num_blocks * kBlockSize,
+ new_num_blocks * kBlockSize,
+ kBlockSize,
+ &old_block_ids,
+ &new_block_ids));
+
+ // For minor-version=1, we map all the blocks that didn't move, regardless of
+ // the contents since they are already copied and no operation is required.
+ if (!src_ops_allowed) {
+ uint64_t num_blocks = std::min(old_num_blocks, new_num_blocks);
+ for (uint64_t block = 0; block < num_blocks; block++) {
+ if (old_block_ids[block] == new_block_ids[block] &&
+ !old_visited_blocks->ContainsBlock(block) &&
+ !new_visited_blocks->ContainsBlock(block)) {
+ old_visited_blocks->AddBlock(block);
+ new_visited_blocks->AddBlock(block);
+ }
+ }
+ }
+
+ // A mapping from the block_id to the list of block numbers with that block id
+ // in the old partition. This is used to lookup where in the old partition
+ // is a block from the new partition.
+ map<BlockMapping::BlockId, vector<uint64_t>> old_blocks_map;
+
+ for (uint64_t block = old_num_blocks; block-- > 0; ) {
+ if (old_block_ids[block] != 0 && !old_visited_blocks->ContainsBlock(block))
+ old_blocks_map[old_block_ids[block]].push_back(block);
+ }
+
+ // The collection of blocks in the new partition with just zeros. This is a
+ // common case for free-space that's also problematic for bsdiff, so we want
+ // to optimize it using REPLACE_BZ operations. The blob for a REPLACE_BZ of
+ // just zeros is so small that it doesn't make sense to spend the I/O reading
+ // zeros from the old partition.
+ vector<Extent> new_zeros;
+
+ vector<Extent> old_identical_blocks;
+ vector<Extent> new_identical_blocks;
+
+ for (uint64_t block = 0; block < new_num_blocks; block++) {
+ // Only produce operations for blocks that were not yet visited.
+ if (new_visited_blocks->ContainsBlock(block))
+ continue;
+ if (new_block_ids[block] == 0) {
+ AppendBlockToExtents(&new_zeros, block);
+ continue;
+ }
+
+ auto old_blocks_map_it = old_blocks_map.find(new_block_ids[block]);
+ // Check if the block exists in the old partition at all.
+ if (old_blocks_map_it == old_blocks_map.end() ||
+ old_blocks_map_it->second.empty())
+ continue;
+
+ AppendBlockToExtents(&old_identical_blocks,
+ old_blocks_map_it->second.back());
+ AppendBlockToExtents(&new_identical_blocks, block);
+ // We can't reuse source blocks in minor version 1 because the cycle
+ // breaking algorithm doesn't support that.
+ if (!src_ops_allowed)
+ old_blocks_map_it->second.pop_back();
+ }
+
+ // Produce operations for the zero blocks split per output extent.
+ size_t num_ops = aops->size();
+ new_visited_blocks->AddExtents(new_zeros);
+ for (Extent extent : new_zeros) {
+ TEST_AND_RETURN_FALSE(DeltaReadFile(
+ aops,
+ "",
+ new_part,
+ vector<Extent>(), // old_extents
+ vector<Extent>{extent}, // new_extents
+ "<zeros>",
+ chunk_blocks,
+ blob_file,
+ src_ops_allowed));
+ }
+ LOG(INFO) << "Produced " << (aops->size() - num_ops) << " operations for "
+ << BlocksInExtents(new_zeros) << " zeroed blocks";
+
+ // Produce MOVE/SOURCE_COPY operations for the moved blocks.
+ num_ops = aops->size();
+ if (chunk_blocks == -1)
+ chunk_blocks = new_num_blocks;
+ uint64_t used_blocks = 0;
+ old_visited_blocks->AddExtents(old_identical_blocks);
+ new_visited_blocks->AddExtents(new_identical_blocks);
+ for (Extent extent : new_identical_blocks) {
+ // We split the operation at the extent boundary or when bigger than
+ // chunk_blocks.
+ for (uint64_t op_block_offset = 0; op_block_offset < extent.num_blocks();
+ op_block_offset += chunk_blocks) {
+ aops->emplace_back();
+ AnnotatedOperation* aop = &aops->back();
+ aop->name = "<identical-blocks>";
+ aop->op.set_type(src_ops_allowed ? InstallOperation::SOURCE_COPY
+ : InstallOperation::MOVE);
+
+ uint64_t chunk_num_blocks =
+ std::min(extent.num_blocks() - op_block_offset,
+ static_cast<uint64_t>(chunk_blocks));
+
+ // The current operation represents the move/copy operation for the
+ // sublist starting at |used_blocks| of length |chunk_num_blocks| where
+ // the src and dst are from |old_identical_blocks| and
+ // |new_identical_blocks| respectively.
+ StoreExtents(
+ ExtentsSublist(old_identical_blocks, used_blocks, chunk_num_blocks),
+ aop->op.mutable_src_extents());
+
+ Extent* op_dst_extent = aop->op.add_dst_extents();
+ op_dst_extent->set_start_block(extent.start_block() + op_block_offset);
+ op_dst_extent->set_num_blocks(chunk_num_blocks);
+ CHECK(
+ vector<Extent>{*op_dst_extent} == // NOLINT(whitespace/braces)
+ ExtentsSublist(new_identical_blocks, used_blocks, chunk_num_blocks));
+
+ used_blocks += chunk_num_blocks;
+ }
+ }
+ LOG(INFO) << "Produced " << (aops->size() - num_ops) << " operations for "
+ << used_blocks << " identical blocks moved";
+
+ return true;
+}
+
bool DeltaReadFile(
vector<AnnotatedOperation>* aops,
const string& old_part,
@@ -256,12 +402,11 @@
const vector<Extent>& old_extents,
const vector<Extent>& new_extents,
const string& name,
- off_t chunk_blocks,
- int data_fd,
- off_t* data_file_size,
+ ssize_t chunk_blocks,
+ BlobFileWriter* blob_file,
bool src_ops_allowed) {
chromeos::Blob data;
- DeltaArchiveManifest_InstallOperation operation;
+ InstallOperation operation;
uint64_t total_blocks = BlocksInExtents(new_extents);
if (chunk_blocks == -1)
@@ -305,7 +450,7 @@
// Check if the operation writes nothing.
if (operation.dst_extents_size() == 0) {
- if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) {
+ if (operation.type() == InstallOperation::MOVE) {
LOG(INFO) << "Empty MOVE operation ("
<< name << "), skipping";
continue;
@@ -315,17 +460,6 @@
}
}
- // Write the data
- if (operation.type() != DeltaArchiveManifest_InstallOperation_Type_MOVE &&
- operation.type() !=
- DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY) {
- operation.set_data_offset(*data_file_size);
- operation.set_data_length(data.size());
- }
-
- TEST_AND_RETURN_FALSE(utils::WriteAll(data_fd, data.data(), data.size()));
- *data_file_size += data.size();
-
// Now, insert into the list of operations.
AnnotatedOperation aop;
aop.name = name;
@@ -334,6 +468,14 @@
name.c_str(), block_offset / chunk_blocks);
}
aop.op = operation;
+
+ // Write the data
+ if (operation.type() != InstallOperation::MOVE &&
+ operation.type() != InstallOperation::SOURCE_COPY) {
+ TEST_AND_RETURN_FALSE(aop.SetOperationBlob(&data, blob_file));
+ } else {
+ TEST_AND_RETURN_FALSE(blob_file->StoreBlob(data) != -1);
+ }
aops->emplace_back(aop);
}
return true;
@@ -345,9 +487,9 @@
const vector<Extent>& new_extents,
bool bsdiff_allowed,
chromeos::Blob* out_data,
- DeltaArchiveManifest_InstallOperation* out_op,
+ InstallOperation* out_op,
bool src_ops_allowed) {
- DeltaArchiveManifest_InstallOperation operation;
+ InstallOperation operation;
// Data blob that will be written to delta file.
const chromeos::Blob* data_blob = nullptr;
@@ -370,7 +512,7 @@
// Using a REPLACE is always an option.
- operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
+ operation.set_type(InstallOperation::REPLACE);
data_blob = &new_data;
// Try compressing it with bzip2.
@@ -379,7 +521,7 @@
CHECK(!new_data_bz.empty());
if (new_data_bz.size() < data_blob->size()) {
// A REPLACE_BZ is better.
- operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
+ operation.set_type(InstallOperation::REPLACE_BZ);
data_blob = &new_data_bz;
}
@@ -394,10 +536,9 @@
if (old_data == new_data) {
// No change in data.
if (src_ops_allowed) {
- operation.set_type(
- DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
+ operation.set_type(InstallOperation::SOURCE_COPY);
} else {
- operation.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
+ operation.set_type(InstallOperation::MOVE);
}
data_blob = &empty_blob;
} else if (bsdiff_allowed) {
@@ -421,10 +562,9 @@
CHECK_GT(bsdiff_delta.size(), static_cast<chromeos::Blob::size_type>(0));
if (bsdiff_delta.size() < data_blob->size()) {
if (src_ops_allowed) {
- operation.set_type(
- DeltaArchiveManifest_InstallOperation_Type_SOURCE_BSDIFF);
+ operation.set_type(InstallOperation::SOURCE_BSDIFF);
} else {
- operation.set_type(DeltaArchiveManifest_InstallOperation_Type_BSDIFF);
+ operation.set_type(InstallOperation::BSDIFF);
}
data_blob = &bsdiff_delta;
}
@@ -433,7 +573,7 @@
size_t removed_bytes = 0;
// Remove identical src/dst block ranges in MOVE operations.
- if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) {
+ if (operation.type() == InstallOperation::MOVE) {
removed_bytes = RemoveIdenticalBlockRanges(
&src_extents, &dst_extents, new_data.size());
}
@@ -446,9 +586,8 @@
StoreExtents(dst_extents, operation.mutable_dst_extents());
// Replace operations should not have source extents.
- if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
- operation.type() ==
- DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
+ if (operation.type() == InstallOperation::REPLACE ||
+ operation.type() == InstallOperation::REPLACE_BZ) {
operation.clear_src_extents();
operation.clear_src_length();
}
@@ -487,8 +626,8 @@
// Returns true if |op| is a no-op operation that doesn't do any useful work
// (e.g., a move operation that copies blocks onto themselves).
-bool IsNoopOperation(const DeltaArchiveManifest_InstallOperation& op) {
- return (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE &&
+bool IsNoopOperation(const InstallOperation& op) {
+ return (op.type() == InstallOperation::MOVE &&
ExpandExtents(op.src_extents()) == ExpandExtents(op.dst_extents()));
}
@@ -512,6 +651,17 @@
return true;
}
+bool CompareAopsByDestination(AnnotatedOperation first_aop,
+ AnnotatedOperation second_aop) {
+ // We want empty operations to be at the end of the payload.
+ if (!first_aop.op.dst_extents().size() || !second_aop.op.dst_extents().size())
+ return ((!first_aop.op.dst_extents().size()) <
+ (!second_aop.op.dst_extents().size()));
+ uint32_t first_dst_start = first_aop.op.dst_extents(0).start_block();
+ uint32_t second_dst_start = second_aop.op.dst_extents(0).start_block();
+ return first_dst_start < second_dst_start;
+}
+
} // namespace diff_utils
} // namespace chromeos_update_engine
diff --git a/payload_generator/delta_diff_utils.h b/payload_generator/delta_diff_utils.h
index 0be8666..ece8115 100644
--- a/payload_generator/delta_diff_utils.h
+++ b/payload_generator/delta_diff_utils.h
@@ -11,6 +11,7 @@
#include <chromeos/secure_blob.h>
#include "update_engine/payload_generator/annotated_operation.h"
+#include "update_engine/payload_generator/extent_ranges.h"
#include "update_engine/payload_generator/payload_generation_config.h"
#include "update_engine/update_metadata.pb.h"
@@ -24,15 +25,41 @@
// blocks in that partition (if available) to determine the best way to compress
// the new files (REPLACE, REPLACE_BZ, COPY, BSDIFF) and writes any necessary
// data to the end of |data_fd| updating |data_file_size| accordingly.
+// |hard_chunk_blocks| and |soft_chunk_blocks| are the hard and soft chunk
+// limits in number of blocks respectively. The soft chunk limit is used to
+// split MOVE and SOURCE_COPY operations and REPLACE_BZ of zeroed blocks, while
+// the hard limit is used to split a file when generating other operations. A
+// value of -1 in |hard_chunk_blocks| means whole files.
bool DeltaReadPartition(std::vector<AnnotatedOperation>* aops,
const PartitionConfig& old_part,
const PartitionConfig& new_part,
- off_t chunk_blocks,
- int data_fd,
- off_t* data_file_size,
- bool skip_block_0,
+ ssize_t hard_chunk_blocks,
+ size_t soft_chunk_blocks,
+ BlobFileWriter* blob_file,
bool src_ops_allowed);
+// Create operations in |aops| for identical blocks that moved around in the old
+// and new partition and also handle zeroed blocks. The old and new partition
+// are stored in the |old_part| and |new_part| files and have |old_num_blocks|
+// and |new_num_blocks| respectively. The maximum operation size is
+// |chunk_blocks| blocks, or unlimited if |chunk_blocks| is -1. The blobs of the
+// produced operations are stored in the |data_fd| file whose size is updated
+// in the value pointed by |data_file_size|.
+// The collections |old_visited_blocks| and |new_visited_blocks| state what
+// blocks already have operations reading or writing them and only operations
+// for unvisited blocks are produced by this function updating both collections
+// with the used blocks.
+bool DeltaMovedAndZeroBlocks(std::vector<AnnotatedOperation>* aops,
+ const std::string& old_part,
+ const std::string& new_part,
+ size_t old_num_blocks,
+ size_t new_num_blocks,
+ ssize_t chunk_blocks,
+ bool src_ops_allowed,
+ BlobFileWriter* blob_file,
+ ExtentRanges* old_visited_blocks,
+ ExtentRanges* new_visited_blocks);
+
// For a given file |name| append operations to |aops| to produce it in the
// |new_part|. The file will be split in chunks of |chunk_blocks| blocks each
// or treated as a single chunk if |chunk_blocks| is -1. The file data is
@@ -47,9 +74,8 @@
const std::vector<Extent>& old_extents,
const std::vector<Extent>& new_extents,
const std::string& name,
- off_t chunk_blocks,
- int data_fd,
- off_t* data_file_size,
+ ssize_t chunk_blocks,
+ BlobFileWriter* blob_file,
bool src_ops_allowed);
// Reads the blocks |old_extents| from |old_part| (if it exists) and the
@@ -67,7 +93,7 @@
const std::vector<Extent>& new_extents,
bool bsdiff_allowed,
chromeos::Blob* out_data,
- DeltaArchiveManifest_InstallOperation* out_op,
+ InstallOperation* out_op,
bool src_ops_allowed);
// Runs the bsdiff tool on two files and returns the resulting delta in
@@ -78,7 +104,7 @@
// Returns true if |op| is a no-op operation that doesn't do any useful work
// (e.g., a move operation that copies blocks onto themselves).
-bool IsNoopOperation(const DeltaArchiveManifest_InstallOperation& op);
+bool IsNoopOperation(const InstallOperation& op);
// Filters all the operations that are no-op, maintaining the relative order
// of the rest of the operations.
@@ -87,6 +113,11 @@
bool InitializePartitionInfo(const PartitionConfig& partition,
PartitionInfo* info);
+// Compare two AnnotatedOperations by the start block of the first Extent in
+// their destination extents.
+bool CompareAopsByDestination(AnnotatedOperation first_aop,
+ AnnotatedOperation second_aop);
+
} // namespace diff_utils
} // namespace chromeos_update_engine
diff --git a/payload_generator/delta_diff_utils_unittest.cc b/payload_generator/delta_diff_utils_unittest.cc
index 2157aa1..0425785 100644
--- a/payload_generator/delta_diff_utils_unittest.cc
+++ b/payload_generator/delta_diff_utils_unittest.cc
@@ -9,6 +9,8 @@
#include <vector>
#include <base/files/scoped_file.h>
+#include <base/format_macros.h>
+#include <base/strings/stringprintf.h>
#include <gtest/gtest.h>
#include "update_engine/payload_generator/delta_diff_generator.h"
@@ -19,7 +21,6 @@
#include "update_engine/utils.h"
using std::string;
-using std::unique_ptr;
using std::vector;
namespace chromeos_update_engine {
@@ -51,61 +52,117 @@
return true;
}
+// Create a fake filesystem of the given |size| and initialize the partition
+// holding it in the PartitionConfig |part|.
+void CreatePartition(PartitionConfig* part, const string& pattern,
+ uint64_t block_size, off_t size) {
+ int fd = -1;
+ ASSERT_TRUE(utils::MakeTempFile(pattern.c_str(), &part->path, &fd));
+ ASSERT_EQ(0, ftruncate(fd, size));
+ ASSERT_EQ(0, close(fd));
+ part->fs_interface.reset(new FakeFilesystem(block_size, size / block_size));
+ part->size = size;
+}
+
+// Writes to the |partition| path blocks such they are all different and they
+// include the tag passed in |tag|, making them also different to any other
+// block on a partition initialized with this function with a different tag.
+// The |block_size| should be a divisor of the partition size.
+// Returns whether the function succeeded writing the partition.
+bool InitializePartitionWithUniqueBlocks(const PartitionConfig& part,
+ uint64_t block_size,
+ uint64_t tag) {
+ TEST_AND_RETURN_FALSE(part.size % block_size == 0);
+ size_t num_blocks = part.size / block_size;
+ chromeos::Blob file_data(part.size);
+ for (size_t i = 0; i < num_blocks; ++i) {
+ string prefix = base::StringPrintf(
+ "block tag 0x%.16" PRIx64 ", block number %16" PRIuS " ", tag, i);
+ chromeos::Blob block_data(prefix.begin(), prefix.end());
+ TEST_AND_RETURN_FALSE(prefix.size() <= block_size);
+ block_data.resize(block_size, 'X');
+ std::copy(block_data.begin(), block_data.end(),
+ file_data.begin() + i * block_size);
+ }
+ return test_utils::WriteFileVector(part.path, file_data);
+}
+
} // namespace
class DeltaDiffUtilsTest : public ::testing::Test {
protected:
- const uint64_t kFilesystemSize = kBlockSize * 1024;
+ const uint64_t kDefaultBlockCount = 128;
void SetUp() override {
- old_part_path_ = "DeltaDiffUtilsTest-old_part_path-XXXXXX";
- CreateFilesystem(&old_fs_, &old_part_path_, kFilesystemSize);
-
- new_part_path_ = "DeltaDiffUtilsTest-new_part_path-XXXXXX";
- CreateFilesystem(&old_fs_, &new_part_path_, kFilesystemSize);
+ CreatePartition(&old_part_, "DeltaDiffUtilsTest-old_part-XXXXXX",
+ block_size_, block_size_ * kDefaultBlockCount);
+ CreatePartition(&new_part_, "DeltaDiffUtilsTest-old_part-XXXXXX",
+ block_size_, block_size_ * kDefaultBlockCount);
+ ASSERT_TRUE(utils::MakeTempFile("DeltaDiffUtilsTest-blob-XXXXXX",
+ &blob_path_,
+ &blob_fd_));
}
void TearDown() override {
- unlink(old_part_path_.c_str());
- unlink(new_part_path_.c_str());
+ unlink(old_part_.path.c_str());
+ unlink(new_part_.path.c_str());
+ if (blob_fd_ != -1)
+ close(blob_fd_);
+ unlink(blob_path_.c_str());
}
- // Create a fake filesystem of the given size and initialize the partition
- // holding it.
- void CreateFilesystem(unique_ptr<FakeFilesystem>* fs, string* filename,
- uint64_t size) {
- string pattern = *filename;
- ASSERT_TRUE(utils::MakeTempFile(pattern.c_str(), filename, nullptr));
- ASSERT_EQ(0, truncate(filename->c_str(), size));
- fs->reset(new FakeFilesystem(kBlockSize, size / kBlockSize));
+ // Helper function to call DeltaMovedAndZeroBlocks() using this class' data
+ // members. This simply avoid repeating all the arguments that never change.
+ bool RunDeltaMovedAndZeroBlocks(ssize_t chunk_blocks,
+ bool src_ops_allowed) {
+ BlobFileWriter blob_file(blob_fd_, &blob_size_);
+ return diff_utils::DeltaMovedAndZeroBlocks(
+ &aops_,
+ old_part_.path,
+ new_part_.path,
+ old_part_.size / block_size_,
+ new_part_.size / block_size_,
+ chunk_blocks,
+ src_ops_allowed,
+ &blob_file,
+ &old_visited_blocks_,
+ &new_visited_blocks_);
}
- // Paths to old and new temporary filesystems used in the tests.
- string old_part_path_;
- string new_part_path_;
+ // Old and new temporary partitions used in the tests. These are initialized
+ // with
+ PartitionConfig old_part_{PartitionName::kRootfs};
+ PartitionConfig new_part_{PartitionName::kRootfs};
- // FilesystemInterface fake implementations used to mock out the file/block
- // distribution.
- unique_ptr<FakeFilesystem> old_fs_;
- unique_ptr<FakeFilesystem> new_fs_;
+ // The file holding the output blob from the various diff utils functions.
+ string blob_path_;
+ int blob_fd_{-1};
+ off_t blob_size_{0};
+
+ size_t block_size_{kBlockSize};
+
+ // Default input/output arguments used when calling DeltaMovedAndZeroBlocks().
+ vector<AnnotatedOperation> aops_;
+ ExtentRanges old_visited_blocks_;
+ ExtentRanges new_visited_blocks_;
};
TEST_F(DeltaDiffUtilsTest, MoveSmallTest) {
- chromeos::Blob data_blob(kBlockSize);
+ chromeos::Blob data_blob(block_size_);
test_utils::FillWithData(&data_blob);
// The old file is on a different block than the new one.
vector<Extent> old_extents = { ExtentForRange(11, 1) };
vector<Extent> new_extents = { ExtentForRange(1, 1) };
- EXPECT_TRUE(WriteExtents(old_part_path_, old_extents, kBlockSize, data_blob));
- EXPECT_TRUE(WriteExtents(new_part_path_, new_extents, kBlockSize, data_blob));
+ EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
+ EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
chromeos::Blob data;
- DeltaArchiveManifest_InstallOperation op;
+ InstallOperation op;
EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
- old_part_path_,
- new_part_path_,
+ old_part_.path,
+ new_part_.path,
old_extents,
new_extents,
true, // bsdiff_allowed
@@ -115,7 +172,7 @@
EXPECT_TRUE(data.empty());
EXPECT_TRUE(op.has_type());
- EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE, op.type());
+ EXPECT_EQ(InstallOperation::MOVE, op.type());
EXPECT_FALSE(op.has_data_offset());
EXPECT_FALSE(op.has_data_length());
EXPECT_EQ(1, op.src_extents_size());
@@ -158,14 +215,14 @@
file_data.resize(file_data.size() + kBlockSize, 'a' + i);
}
- EXPECT_TRUE(WriteExtents(old_part_path_, old_extents, kBlockSize, file_data));
- EXPECT_TRUE(WriteExtents(new_part_path_, new_extents, kBlockSize, file_data));
+ EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, file_data));
+ EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, file_data));
chromeos::Blob data;
- DeltaArchiveManifest_InstallOperation op;
+ InstallOperation op;
EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
- old_part_path_,
- new_part_path_,
+ old_part_.path,
+ new_part_.path,
old_extents,
new_extents,
true, // bsdiff_allowed
@@ -176,7 +233,7 @@
EXPECT_TRUE(data.empty());
EXPECT_TRUE(op.has_type());
- EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE, op.type());
+ EXPECT_EQ(InstallOperation::MOVE, op.type());
EXPECT_FALSE(op.has_data_offset());
EXPECT_FALSE(op.has_data_length());
@@ -220,16 +277,16 @@
vector<Extent> old_extents = { ExtentForRange(1, 1) };
vector<Extent> new_extents = { ExtentForRange(2, 1) };
- EXPECT_TRUE(WriteExtents(old_part_path_, old_extents, kBlockSize, data_blob));
+ EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
// Modify one byte in the new file.
data_blob[0]++;
- EXPECT_TRUE(WriteExtents(new_part_path_, new_extents, kBlockSize, data_blob));
+ EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
chromeos::Blob data;
- DeltaArchiveManifest_InstallOperation op;
+ InstallOperation op;
EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
- old_part_path_,
- new_part_path_,
+ old_part_.path,
+ new_part_.path,
old_extents,
new_extents,
true, // bsdiff_allowed
@@ -240,7 +297,7 @@
EXPECT_FALSE(data.empty());
EXPECT_TRUE(op.has_type());
- EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_BSDIFF, op.type());
+ EXPECT_EQ(InstallOperation::BSDIFF, op.type());
EXPECT_FALSE(op.has_data_offset());
EXPECT_FALSE(op.has_data_length());
EXPECT_EQ(1, op.src_extents_size());
@@ -262,16 +319,16 @@
vector<Extent> old_extents = { ExtentForRange(1, 1) };
vector<Extent> new_extents = { ExtentForRange(2, 1) };
- EXPECT_TRUE(WriteExtents(old_part_path_, old_extents, kBlockSize, data_blob));
+ EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
// Modify one byte in the new file.
data_blob[0]++;
- EXPECT_TRUE(WriteExtents(new_part_path_, new_extents, kBlockSize, data_blob));
+ EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
chromeos::Blob data;
- DeltaArchiveManifest_InstallOperation op;
+ InstallOperation op;
EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
- old_part_path_,
- new_part_path_,
+ old_part_.path,
+ new_part_.path,
old_extents,
new_extents,
false, // bsdiff_allowed
@@ -284,7 +341,7 @@
// The point of this test is that we don't use BSDIFF the way the above
// did. The rest of the details are to be caught in other tests.
EXPECT_TRUE(op.has_type());
- EXPECT_NE(DeltaArchiveManifest_InstallOperation_Type_BSDIFF, op.type());
+ EXPECT_NE(InstallOperation::BSDIFF, op.type());
}
TEST_F(DeltaDiffUtilsTest, BsdiffNotAllowedMoveTest) {
@@ -295,14 +352,14 @@
vector<Extent> old_extents = { ExtentForRange(1, 1) };
vector<Extent> new_extents = { ExtentForRange(2, 1) };
- EXPECT_TRUE(WriteExtents(old_part_path_, old_extents, kBlockSize, data_blob));
- EXPECT_TRUE(WriteExtents(new_part_path_, new_extents, kBlockSize, data_blob));
+ EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
+ EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
chromeos::Blob data;
- DeltaArchiveManifest_InstallOperation op;
+ InstallOperation op;
EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
- old_part_path_,
- new_part_path_,
+ old_part_.path,
+ new_part_.path,
old_extents,
new_extents,
false, // bsdiff_allowed
@@ -315,7 +372,7 @@
// The point of this test is that we can still use a MOVE for a file
// that is blacklisted.
EXPECT_TRUE(op.has_type());
- EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE, op.type());
+ EXPECT_EQ(InstallOperation::MOVE, op.type());
}
TEST_F(DeltaDiffUtilsTest, ReplaceSmallTest) {
@@ -337,14 +394,14 @@
for (int i = 0; i < 2; i++) {
chromeos::Blob data_to_test = i == 0 ? random_data : ones;
// The old_extents will be initialized with 0.
- EXPECT_TRUE(WriteExtents(new_part_path_, new_extents, kBlockSize,
+ EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize,
data_to_test));
chromeos::Blob data;
- DeltaArchiveManifest_InstallOperation op;
+ InstallOperation op;
EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
- old_part_path_,
- new_part_path_,
+ old_part_.path,
+ new_part_.path,
old_extents,
new_extents,
true, // bsdiff_allowed
@@ -354,9 +411,8 @@
EXPECT_FALSE(data.empty());
EXPECT_TRUE(op.has_type());
- const DeltaArchiveManifest_InstallOperation_Type expected_type =
- (i == 0 ? DeltaArchiveManifest_InstallOperation_Type_REPLACE :
- DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
+ const InstallOperation_Type expected_type =
+ (i == 0 ? InstallOperation::REPLACE : InstallOperation::REPLACE_BZ);
EXPECT_EQ(expected_type, op.type());
EXPECT_FALSE(op.has_data_offset());
EXPECT_FALSE(op.has_data_length());
@@ -379,14 +435,14 @@
vector<Extent> old_extents = { ExtentForRange(11, 1) };
vector<Extent> new_extents = { ExtentForRange(1, 1) };
- EXPECT_TRUE(WriteExtents(old_part_path_, old_extents, kBlockSize, data_blob));
- EXPECT_TRUE(WriteExtents(new_part_path_, new_extents, kBlockSize, data_blob));
+ EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
+ EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
chromeos::Blob data;
- DeltaArchiveManifest_InstallOperation op;
+ InstallOperation op;
EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
- old_part_path_,
- new_part_path_,
+ old_part_.path,
+ new_part_.path,
old_extents,
new_extents,
true, // bsdiff_allowed
@@ -396,7 +452,7 @@
EXPECT_TRUE(data.empty());
EXPECT_TRUE(op.has_type());
- EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY, op.type());
+ EXPECT_EQ(InstallOperation::SOURCE_COPY, op.type());
}
TEST_F(DeltaDiffUtilsTest, SourceBsdiffTest) {
@@ -410,16 +466,16 @@
vector<Extent> old_extents = { ExtentForRange(1, 1) };
vector<Extent> new_extents = { ExtentForRange(2, 1) };
- EXPECT_TRUE(WriteExtents(old_part_path_, old_extents, kBlockSize, data_blob));
+ EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
// Modify one byte in the new file.
data_blob[0]++;
- EXPECT_TRUE(WriteExtents(new_part_path_, new_extents, kBlockSize, data_blob));
+ EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
chromeos::Blob data;
- DeltaArchiveManifest_InstallOperation op;
+ InstallOperation op;
EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
- old_part_path_,
- new_part_path_,
+ old_part_.path,
+ new_part_.path,
old_extents,
new_extents,
true, // bsdiff_allowed
@@ -429,15 +485,14 @@
EXPECT_FALSE(data.empty());
EXPECT_TRUE(op.has_type());
- EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_SOURCE_BSDIFF,
- op.type());
+ EXPECT_EQ(InstallOperation::SOURCE_BSDIFF, op.type());
}
TEST_F(DeltaDiffUtilsTest, IsNoopOperationTest) {
- DeltaArchiveManifest_InstallOperation op;
- op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
+ InstallOperation op;
+ op.set_type(InstallOperation::REPLACE_BZ);
EXPECT_FALSE(diff_utils::IsNoopOperation(op));
- op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
+ op.set_type(InstallOperation::MOVE);
EXPECT_TRUE(diff_utils::IsNoopOperation(op));
*(op.add_src_extents()) = ExtentForRange(3, 2);
*(op.add_dst_extents()) = ExtentForRange(3, 2);
@@ -456,7 +511,7 @@
TEST_F(DeltaDiffUtilsTest, FilterNoopOperations) {
AnnotatedOperation aop1;
- aop1.op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
+ aop1.op.set_type(InstallOperation::REPLACE_BZ);
*(aop1.op.add_dst_extents()) = ExtentForRange(3, 2);
aop1.name = "aop1";
@@ -464,7 +519,7 @@
aop2.name = "aop2";
AnnotatedOperation noop;
- noop.op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
+ noop.op.set_type(InstallOperation::MOVE);
*(noop.op.add_src_extents()) = ExtentForRange(3, 2);
*(noop.op.add_dst_extents()) = ExtentForRange(3, 2);
noop.name = "noop";
@@ -476,4 +531,233 @@
EXPECT_EQ("aop2", ops[1].name);
}
+// Test the simple case where all the blocks are different and no new blocks are
+// zeroed.
+TEST_F(DeltaDiffUtilsTest, NoZeroedOrUniqueBlocksDetected) {
+ InitializePartitionWithUniqueBlocks(old_part_, block_size_, 5);
+ InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
+
+ EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1, // chunk_blocks
+ false)); // src_ops_allowed
+
+ EXPECT_EQ(0, old_visited_blocks_.blocks());
+ EXPECT_EQ(0, new_visited_blocks_.blocks());
+ EXPECT_EQ(0, blob_size_);
+ EXPECT_TRUE(aops_.empty());
+}
+
+// Test that when the partitions have identical blocks in the same positions no
+// MOVE operation is performed and all the blocks are handled.
+TEST_F(DeltaDiffUtilsTest, IdenticalPartitionsDontMove) {
+ InitializePartitionWithUniqueBlocks(old_part_, block_size_, 42);
+ InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
+
+ // Mark some of the blocks as already visited.
+ vector<Extent> already_visited = {ExtentForRange(5, 10),
+ ExtentForRange(25, 10)};
+ old_visited_blocks_.AddExtents(already_visited);
+ new_visited_blocks_.AddExtents(already_visited);
+
+ // Most of the blocks rest in the same place, but there's no need for MOVE
+ // operations on those blocks.
+ EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1, // chunk_blocks
+ false)); // src_ops_allowed
+
+ EXPECT_EQ(kDefaultBlockCount, old_visited_blocks_.blocks());
+ EXPECT_EQ(kDefaultBlockCount, new_visited_blocks_.blocks());
+ EXPECT_EQ(0, blob_size_);
+ EXPECT_TRUE(aops_.empty());
+}
+
+// Test that when the partitions have identical blocks in the same positions
+// MOVE operation is performed and all the blocks are handled.
+TEST_F(DeltaDiffUtilsTest, IdenticalBlocksAreCopiedFromSource) {
+ // We use a smaller partition for this test.
+ old_part_.size = kBlockSize * 50;
+ new_part_.size = kBlockSize * 50;
+
+ InitializePartitionWithUniqueBlocks(old_part_, block_size_, 42);
+ InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
+
+ // Mark some of the blocks as already visited.
+ vector<Extent> already_visited = {ExtentForRange(5, 5),
+ ExtentForRange(25, 7)};
+ old_visited_blocks_.AddExtents(already_visited);
+ new_visited_blocks_.AddExtents(already_visited);
+
+ // Override some of the old blocks with different data.
+ vector<Extent> different_blocks = {ExtentForRange(40, 5)};
+ EXPECT_TRUE(WriteExtents(old_part_.path, different_blocks, kBlockSize,
+ chromeos::Blob(5 * kBlockSize, 'a')));
+
+ EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(10, // chunk_blocks
+ true)); // src_ops_allowed
+
+ ExtentRanges expected_ranges;
+ expected_ranges.AddExtent(ExtentForRange(0, 50));
+ expected_ranges.SubtractExtents(different_blocks);
+
+ EXPECT_EQ(expected_ranges.extent_set(), old_visited_blocks_.extent_set());
+ EXPECT_EQ(expected_ranges.extent_set(), new_visited_blocks_.extent_set());
+ EXPECT_EQ(0, blob_size_);
+
+ // We expect all the blocks that we didn't override with |different_blocks|
+ // and that we didn't mark as visited in |already_visited| to match and have a
+ // SOURCE_COPY operation chunked at 10 blocks.
+ vector<Extent> expected_op_extents = {
+ ExtentForRange(0, 5),
+ ExtentForRange(10, 10),
+ ExtentForRange(20, 5),
+ ExtentForRange(32, 8),
+ ExtentForRange(45, 5),
+ };
+
+ EXPECT_EQ(expected_op_extents.size(), aops_.size());
+ for (size_t i = 0; i < aops_.size() && i < expected_op_extents.size(); ++i) {
+ SCOPED_TRACE(base::StringPrintf("Failed on operation number %" PRIuS, i));
+ const AnnotatedOperation& aop = aops_[i];
+ EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
+ EXPECT_EQ(1, aop.op.src_extents_size());
+ EXPECT_EQ(expected_op_extents[i], aop.op.src_extents(0));
+ EXPECT_EQ(1, aop.op.dst_extents_size());
+ EXPECT_EQ(expected_op_extents[i], aop.op.dst_extents(0));
+ }
+}
+
+TEST_F(DeltaDiffUtilsTest, IdenticalBlocksAreCopiedInOder) {
+ // We use a smaller partition for this test.
+ old_part_.size = block_size_ * 50;
+ new_part_.size = block_size_ * 50;
+
+ // Create two identical partitions with 5 copies of the same unique "file".
+ chromeos::Blob file_data(block_size_ * 10, 'a');
+ for (size_t offset = 0; offset < file_data.size(); offset += block_size_)
+ file_data[offset] = 'a' + offset / block_size_;
+
+ chromeos::Blob partition_data(old_part_.size);
+ for (size_t offset = 0; offset < partition_data.size();
+ offset += file_data.size()) {
+ std::copy(file_data.begin(), file_data.end(),
+ partition_data.begin() + offset);
+ }
+ EXPECT_TRUE(test_utils::WriteFileVector(old_part_.path, partition_data));
+ EXPECT_TRUE(test_utils::WriteFileVector(new_part_.path, partition_data));
+
+ EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1, // chunk_blocks
+ true)); // src_ops_allowed
+
+ // There should be only one SOURCE_COPY, for the whole partition and the
+ // source extents should cover only the first copy of the source file since
+ // we prefer to re-read files (maybe cached) instead of continue reading the
+ // rest of the partition.
+ EXPECT_EQ(1, aops_.size());
+ const AnnotatedOperation& aop = aops_[0];
+ EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
+ EXPECT_EQ(5, aop.op.src_extents_size());
+ for (int i = 0; i < aop.op.src_extents_size(); ++i) {
+ EXPECT_EQ(ExtentForRange(0, 10), aop.op.src_extents(i));
+ }
+
+ EXPECT_EQ(1, aop.op.dst_extents_size());
+ EXPECT_EQ(ExtentForRange(0, 50), aop.op.dst_extents(0));
+
+ EXPECT_EQ(0, blob_size_);
+}
+
+// Test that all blocks with zeros are handled separately using REPLACE_BZ
+// operations unless they are not moved.
+TEST_F(DeltaDiffUtilsTest, ZeroBlocksUseReplaceBz) {
+ InitializePartitionWithUniqueBlocks(old_part_, block_size_, 42);
+ InitializePartitionWithUniqueBlocks(new_part_, block_size_, 5);
+
+ // We set four ranges of blocks with zeros: a single block, a range that fits
+ // in the chunk size, a range that doesn't and finally a range of zeros that
+ // was also zeros in the old image.
+ vector<Extent> new_zeros = {
+ ExtentForRange(10, 1),
+ ExtentForRange(20, 4),
+ // The last range is split since the old image has zeros in part of it.
+ ExtentForRange(30, 20),
+ };
+ chromeos::Blob zeros_data(BlocksInExtents(new_zeros) * block_size_, '\0');
+ EXPECT_TRUE(WriteExtents(new_part_.path, new_zeros, block_size_, zeros_data));
+
+ vector<Extent> old_zeros = vector<Extent>{ExtentForRange(43, 7)};
+ EXPECT_TRUE(WriteExtents(old_part_.path, old_zeros, block_size_, zeros_data));
+
+ EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(5, // chunk_blocks
+ false)); // src_ops_allowed
+
+ // Zeroed blocks from old_visited_blocks_ were copied over, so me actually
+ // use them regardless of the trivial MOVE operation not being emitted.
+ EXPECT_EQ(old_zeros,
+ old_visited_blocks_.GetExtentsForBlockCount(
+ old_visited_blocks_.blocks()));
+
+ // All the new zeroed blocks should be used, part with REPLACE_BZ and part
+ // trivial MOVE operations (not included).
+ EXPECT_EQ(new_zeros,
+ new_visited_blocks_.GetExtentsForBlockCount(
+ new_visited_blocks_.blocks()));
+
+ vector<Extent> expected_op_extents = {
+ ExtentForRange(10, 1),
+ ExtentForRange(20, 4),
+ // This range should be split.
+ ExtentForRange(30, 5),
+ ExtentForRange(35, 5),
+ ExtentForRange(40, 3),
+ };
+
+ EXPECT_EQ(expected_op_extents.size(), aops_.size());
+ for (size_t i = 0; i < aops_.size() && i < expected_op_extents.size(); ++i) {
+ SCOPED_TRACE(base::StringPrintf("Failed on operation number %" PRIuS, i));
+ const AnnotatedOperation& aop = aops_[i];
+ EXPECT_EQ(InstallOperation::REPLACE_BZ, aop.op.type());
+ EXPECT_EQ(0, aop.op.src_extents_size());
+ EXPECT_EQ(1, aop.op.dst_extents_size());
+ EXPECT_EQ(expected_op_extents[i], aop.op.dst_extents(0));
+ }
+ EXPECT_NE(0, blob_size_);
+}
+
+TEST_F(DeltaDiffUtilsTest, ShuffledBlocksAreTracked) {
+ vector<uint64_t> permutation = {0, 1, 5, 6, 7, 2, 3, 4, 9, 10, 11, 12, 8};
+ vector<Extent> perm_extents;
+ for (uint64_t x : permutation)
+ AppendBlockToExtents(&perm_extents, x);
+
+ // We use a smaller partition for this test.
+ old_part_.size = block_size_ * permutation.size();
+ new_part_.size = block_size_ * permutation.size();
+ InitializePartitionWithUniqueBlocks(new_part_, block_size_, 123);
+
+ // We initialize the old_part_ with the blocks from new_part but in the
+ // |permutation| order. Block i in the old_part_ will contain the same data
+ // as block permutation[i] in the new_part_.
+ chromeos::Blob new_contents;
+ EXPECT_TRUE(utils::ReadFile(new_part_.path, &new_contents));
+ EXPECT_TRUE(WriteExtents(old_part_.path, perm_extents, block_size_,
+ new_contents));
+
+ EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1, // chunk_blocks
+ true)); // src_ops_allowed
+
+ EXPECT_EQ(permutation.size(), old_visited_blocks_.blocks());
+ EXPECT_EQ(permutation.size(), new_visited_blocks_.blocks());
+
+ // There should be only one SOURCE_COPY, with a complicate list of extents.
+ EXPECT_EQ(1, aops_.size());
+ const AnnotatedOperation& aop = aops_[0];
+ EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
+ vector<Extent> aop_src_extents;
+ ExtentsToVector(aop.op.src_extents(), &aop_src_extents);
+ EXPECT_EQ(perm_extents, aop_src_extents);
+
+ EXPECT_EQ(1, aop.op.dst_extents_size());
+ EXPECT_EQ(ExtentForRange(0, permutation.size()), aop.op.dst_extents(0));
+
+ EXPECT_EQ(0, blob_size_);
+}
+
} // namespace chromeos_update_engine
diff --git a/payload_generator/extent_ranges.cc b/payload_generator/extent_ranges.cc
index d86bcfc..9151efe 100644
--- a/payload_generator/extent_ranges.cc
+++ b/payload_generator/extent_ranges.cc
@@ -180,6 +180,23 @@
}
}
+bool ExtentRanges::ContainsBlock(uint64_t block) const {
+ auto lower = extent_set_.lower_bound(ExtentForRange(block, 1));
+ // The block could be on the extent before the one in |lower|.
+ if (lower != extent_set_.begin())
+ lower--;
+ // Any extent starting at block+1 or later is not interesting, so this is the
+ // upper limit.
+ auto upper = extent_set_.lower_bound(ExtentForRange(block + 1, 0));
+ for (auto iter = lower; iter != upper; ++iter) {
+ if (iter->start_block() <= block &&
+ block < iter->start_block() + iter->num_blocks()) {
+ return true;
+ }
+ }
+ return false;
+}
+
void ExtentRanges::Dump() const {
LOG(INFO) << "ExtentRanges Dump. blocks: " << blocks_;
for (ExtentSet::const_iterator it = extent_set_.begin(),
@@ -223,7 +240,7 @@
return out;
}
-vector<Extent> FilterExtentRanges(const std::vector<Extent>& extents,
+vector<Extent> FilterExtentRanges(const vector<Extent>& extents,
const ExtentRanges& ranges) {
vector<Extent> result;
const ExtentRanges::ExtentSet& extent_set = ranges.extent_set();
diff --git a/payload_generator/extent_ranges.h b/payload_generator/extent_ranges.h
index fc15149..f14bb61 100644
--- a/payload_generator/extent_ranges.h
+++ b/payload_generator/extent_ranges.h
@@ -48,6 +48,9 @@
void AddRanges(const ExtentRanges& ranges);
void SubtractRanges(const ExtentRanges& ranges);
+ // Returns whether the block |block| is in this ExtentRange.
+ bool ContainsBlock(uint64_t block) const;
+
static bool ExtentsOverlapOrTouch(const Extent& a, const Extent& b);
static bool ExtentsOverlap(const Extent& a, const Extent& b);
diff --git a/payload_generator/extent_ranges_unittest.cc b/payload_generator/extent_ranges_unittest.cc
index b7036a5..2cda195 100644
--- a/payload_generator/extent_ranges_unittest.cc
+++ b/payload_generator/extent_ranges_unittest.cc
@@ -256,6 +256,25 @@
}
}
+TEST(ExtentRangesTest, ContainsBlockTest) {
+ ExtentRanges ranges;
+ EXPECT_FALSE(ranges.ContainsBlock(123));
+
+ ranges.AddExtent(ExtentForRange(10, 10));
+ ranges.AddExtent(ExtentForRange(100, 1));
+
+ EXPECT_FALSE(ranges.ContainsBlock(9));
+ EXPECT_TRUE(ranges.ContainsBlock(10));
+ EXPECT_TRUE(ranges.ContainsBlock(15));
+ EXPECT_TRUE(ranges.ContainsBlock(19));
+ EXPECT_FALSE(ranges.ContainsBlock(20));
+
+ // Test for an extent with just the block we are requesting.
+ EXPECT_FALSE(ranges.ContainsBlock(99));
+ EXPECT_TRUE(ranges.ContainsBlock(100));
+ EXPECT_FALSE(ranges.ContainsBlock(101));
+}
+
TEST(ExtentRangesTest, FilterExtentRangesEmptyRanges) {
ExtentRanges ranges;
EXPECT_EQ(vector<Extent>(),
diff --git a/payload_generator/extent_utils_unittest.cc b/payload_generator/extent_utils_unittest.cc
index 188164d..3f66d3e 100644
--- a/payload_generator/extent_utils_unittest.cc
+++ b/payload_generator/extent_utils_unittest.cc
@@ -63,11 +63,11 @@
}
TEST(ExtentUtilsTest, ExtendExtentsTest) {
- DeltaArchiveManifest_InstallOperation first_op;
+ InstallOperation first_op;
*(first_op.add_src_extents()) = ExtentForRange(1, 1);
*(first_op.add_src_extents()) = ExtentForRange(3, 1);
- DeltaArchiveManifest_InstallOperation second_op;
+ InstallOperation second_op;
*(second_op.add_src_extents()) = ExtentForRange(4, 2);
*(second_op.add_src_extents()) = ExtentForRange(8, 2);
diff --git a/payload_generator/full_update_generator.cc b/payload_generator/full_update_generator.cc
index c28f7e8..00d7300 100644
--- a/payload_generator/full_update_generator.cc
+++ b/payload_generator/full_update_generator.cc
@@ -12,14 +12,15 @@
#include <memory>
#include <base/format_macros.h>
-#include <base/strings/stringprintf.h>
#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <base/synchronization/lock.h>
+#include <base/threading/simple_thread.h>
+#include <chromeos/secure_blob.h>
#include "update_engine/bzip.h"
#include "update_engine/utils.h"
-using std::deque;
-using std::shared_ptr;
using std::string;
using std::vector;
@@ -29,82 +30,81 @@
const size_t kDefaultFullChunkSize = 1024 * 1024; // 1 MiB
-// This class encapsulates a full update chunk processing thread. The processor
-// reads a chunk of data from the input file descriptor and compresses it. The
-// processor needs to be started through Start() then waited on through Wait().
-class ChunkProcessor {
+// This class encapsulates a full update chunk processing thread work. The
+// processor reads a chunk of data from the input file descriptor and compresses
+// it. The processor will destroy itself when the work is done.
+class ChunkProcessor : public base::DelegateSimpleThread::Delegate {
public:
// Read a chunk of |size| bytes from |fd| starting at offset |offset|.
- ChunkProcessor(int fd, off_t offset, size_t size)
- : thread_(nullptr),
- fd_(fd),
- offset_(offset),
- buffer_in_(size) {}
- ~ChunkProcessor() { Wait(); }
+ ChunkProcessor(int fd, off_t offset, size_t size,
+ BlobFileWriter* blob_file, AnnotatedOperation* aop)
+ : fd_(fd),
+ offset_(offset),
+ size_(size),
+ blob_file_(blob_file),
+ aop_(aop) {}
+ // We use a default move constructor since all the data members are POD types.
+ ChunkProcessor(ChunkProcessor&&) = default;
+ ~ChunkProcessor() override = default;
- off_t offset() const { return offset_; }
- const chromeos::Blob& buffer_in() const { return buffer_in_; }
- const chromeos::Blob& buffer_compressed() const { return buffer_compressed_; }
-
- // Starts the processor. Returns true on success, false on failure.
- bool Start();
-
- // Waits for the processor to complete. Returns true on success, false on
- // failure.
- bool Wait();
-
- bool ShouldCompress() const {
- return buffer_compressed_.size() < buffer_in_.size();
- }
+ // Overrides DelegateSimpleThread::Delegate.
+ // Run() handles the read from |fd| in a thread-safe way, and stores the
+ // new operation to generate the region starting at |offset| of size |size|
+ // in the output operation |aop|. The associated blob data is stored in
+ // |blob_fd| and |blob_file_size| is updated.
+ void Run() override;
private:
- // Reads the input data into |buffer_in_| and compresses it into
- // |buffer_compressed_|. Returns true on success, false otherwise.
- bool ReadAndCompress();
- static gpointer ReadAndCompressThread(gpointer data);
+ bool ProcessChunk();
- GThread* thread_;
+ // Stores the operation blob in the |blob_fd_| and updates the
+ // |blob_file_size| accordingly.
+ // This method is thread-safe since it uses a mutex to access the file.
+ // Returns the data offset where the data was written to.
+ off_t StoreBlob(const chromeos::Blob& blob);
+
+ // Work parameters.
int fd_;
off_t offset_;
- chromeos::Blob buffer_in_;
- chromeos::Blob buffer_compressed_;
+ size_t size_;
+ BlobFileWriter* blob_file_;
+ AnnotatedOperation* aop_;
DISALLOW_COPY_AND_ASSIGN(ChunkProcessor);
};
-bool ChunkProcessor::Start() {
- // g_thread_create is deprecated since glib 2.32. Use
- // g_thread_new instead.
- thread_ = g_thread_try_new("chunk_proc", ReadAndCompressThread, this,
- nullptr);
- TEST_AND_RETURN_FALSE(thread_ != nullptr);
- return true;
-}
-
-bool ChunkProcessor::Wait() {
- if (!thread_) {
- return false;
+void ChunkProcessor::Run() {
+ if (!ProcessChunk()) {
+ LOG(ERROR) << "Error processing region at " << offset_ << " of size "
+ << size_;
}
- gpointer result = g_thread_join(thread_);
- thread_ = nullptr;
- TEST_AND_RETURN_FALSE(result == this);
- return true;
}
-gpointer ChunkProcessor::ReadAndCompressThread(gpointer data) {
- return reinterpret_cast<ChunkProcessor*>(data)->ReadAndCompress() ?
- data : nullptr;
-}
-
-bool ChunkProcessor::ReadAndCompress() {
+bool ChunkProcessor::ProcessChunk() {
+ chromeos::Blob buffer_in_(size_);
+ chromeos::Blob op_blob;
ssize_t bytes_read = -1;
TEST_AND_RETURN_FALSE(utils::PReadAll(fd_,
buffer_in_.data(),
buffer_in_.size(),
offset_,
&bytes_read));
- TEST_AND_RETURN_FALSE(bytes_read == static_cast<ssize_t>(buffer_in_.size()));
- TEST_AND_RETURN_FALSE(BzipCompress(buffer_in_, &buffer_compressed_));
+ TEST_AND_RETURN_FALSE(bytes_read == static_cast<ssize_t>(size_));
+ TEST_AND_RETURN_FALSE(BzipCompress(buffer_in_, &op_blob));
+
+ InstallOperation_Type op_type = InstallOperation::REPLACE_BZ;
+
+ if (op_blob.size() >= buffer_in_.size()) {
+ // A REPLACE is cheaper than a REPLACE_BZ in this case.
+ op_blob = std::move(buffer_in_);
+ op_type = InstallOperation::REPLACE;
+ }
+
+ off_t op_offset = blob_file_->StoreBlob(op_blob);
+ TEST_AND_RETURN_FALSE(op_offset >= 0);
+ aop_->op.set_data_offset(op_offset);
+ aop_->op.set_data_length(op_blob.size());
+ aop_->op.set_type(op_type);
return true;
}
@@ -112,101 +112,106 @@
bool FullUpdateGenerator::GenerateOperations(
const PayloadGenerationConfig& config,
- int fd,
- off_t* data_file_size,
+ BlobFileWriter* blob_file,
vector<AnnotatedOperation>* rootfs_ops,
vector<AnnotatedOperation>* kernel_ops) {
TEST_AND_RETURN_FALSE(config.Validate());
- rootfs_ops->clear();
- kernel_ops->clear();
// FullUpdateGenerator requires a positive chunk_size, otherwise there will
// be only one operation with the whole partition which should not be allowed.
- size_t full_chunk_size = kDefaultFullChunkSize;
- if (config.chunk_size >= 0) {
- full_chunk_size = config.chunk_size;
+ // For performance reasons, we force a small default hard limit of 1 MiB. This
+ // limit can be changed in the config, and we will use the smaller of the two
+ // soft/hard limits.
+ size_t full_chunk_size;
+ if (config.hard_chunk_size >= 0) {
+ full_chunk_size = std::min(static_cast<size_t>(config.hard_chunk_size),
+ config.soft_chunk_size);
} else {
+ full_chunk_size = std::min(kDefaultFullChunkSize, config.soft_chunk_size);
LOG(INFO) << "No chunk_size provided, using the default chunk_size for the "
<< "full operations: " << full_chunk_size << " bytes.";
}
TEST_AND_RETURN_FALSE(full_chunk_size > 0);
TEST_AND_RETURN_FALSE(full_chunk_size % config.block_size == 0);
+ TEST_AND_RETURN_FALSE(GenerateOperationsForPartition(
+ config.target.rootfs,
+ config.block_size,
+ full_chunk_size / config.block_size,
+ blob_file,
+ rootfs_ops));
+ TEST_AND_RETURN_FALSE(GenerateOperationsForPartition(
+ config.target.kernel,
+ config.block_size,
+ full_chunk_size / config.block_size,
+ blob_file,
+ kernel_ops));
+ return true;
+}
+
+bool FullUpdateGenerator::GenerateOperationsForPartition(
+ const PartitionConfig& new_part,
+ size_t block_size,
+ size_t chunk_blocks,
+ BlobFileWriter* blob_file,
+ vector<AnnotatedOperation>* aops) {
size_t max_threads = std::max(sysconf(_SC_NPROCESSORS_ONLN), 4L);
- LOG(INFO) << "Max threads: " << max_threads;
+ LOG(INFO) << "Compressing partition " << PartitionNameString(new_part.name)
+ << " from " << new_part.path << " splitting in chunks of "
+ << chunk_blocks << " blocks (" << block_size
+ << " bytes each) using " << max_threads << " threads";
- const PartitionConfig* partitions[] = {
- &config.target.rootfs,
- &config.target.kernel};
+ int in_fd = open(new_part.path.c_str(), O_RDONLY, 0);
+ TEST_AND_RETURN_FALSE(in_fd >= 0);
+ ScopedFdCloser in_fd_closer(&in_fd);
- for (int part_id = 0; part_id < 2; ++part_id) {
- const PartitionConfig* partition = partitions[part_id];
- LOG(INFO) << "compressing " << partition->path;
- int in_fd = open(partition->path.c_str(), O_RDONLY, 0);
- TEST_AND_RETURN_FALSE(in_fd >= 0);
- ScopedFdCloser in_fd_closer(&in_fd);
- deque<shared_ptr<ChunkProcessor>> threads;
- int last_progress_update = INT_MIN;
- size_t bytes_left = partition->size, counter = 0, offset = 0;
- while (bytes_left > 0 || !threads.empty()) {
- // Check and start new chunk processors if possible.
- while (threads.size() < max_threads && bytes_left > 0) {
- size_t this_chunk_bytes = std::min(bytes_left, full_chunk_size);
- shared_ptr<ChunkProcessor> processor(
- new ChunkProcessor(in_fd, offset, this_chunk_bytes));
- threads.push_back(processor);
- TEST_AND_RETURN_FALSE(processor->Start());
- bytes_left -= this_chunk_bytes;
- offset += this_chunk_bytes;
- }
+ // We potentially have all the ChunkProcessors in memory but only
+ // |max_threads| will actually hold a block in memory while we process.
+ size_t partition_blocks = new_part.size / block_size;
+ size_t num_chunks = (partition_blocks + chunk_blocks - 1) / chunk_blocks;
+ aops->resize(num_chunks);
+ vector<ChunkProcessor> chunk_processors;
+ chunk_processors.reserve(num_chunks);
+ blob_file->SetTotalBlobs(num_chunks);
- // Need to wait for a chunk processor to complete and process its output
- // before spawning new processors.
- shared_ptr<ChunkProcessor> processor = threads.front();
- threads.pop_front();
- TEST_AND_RETURN_FALSE(processor->Wait());
+ const string part_name_str = PartitionNameString(new_part.name);
- DeltaArchiveManifest_InstallOperation* op = nullptr;
- if (part_id == 0) {
- rootfs_ops->emplace_back();
- rootfs_ops->back().name =
- base::StringPrintf("<rootfs-operation-%" PRIuS ">", counter++);
- op = &rootfs_ops->back().op;
- } else {
- kernel_ops->emplace_back();
- kernel_ops->back().name =
- base::StringPrintf("<kernel-operation-%" PRIuS ">", counter++);
- op = &kernel_ops->back().op;
- }
+ for (size_t i = 0; i < num_chunks; ++i) {
+ size_t start_block = i * chunk_blocks;
+ // The last chunk could be smaller.
+ size_t num_blocks = std::min(chunk_blocks,
+ partition_blocks - i * chunk_blocks);
- const bool compress = processor->ShouldCompress();
- const chromeos::Blob& use_buf =
- compress ? processor->buffer_compressed() : processor->buffer_in();
- op->set_type(compress ?
- DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ :
- DeltaArchiveManifest_InstallOperation_Type_REPLACE);
- op->set_data_offset(*data_file_size);
- TEST_AND_RETURN_FALSE(utils::WriteAll(fd, use_buf.data(),
- use_buf.size()));
- *data_file_size += use_buf.size();
- op->set_data_length(use_buf.size());
- Extent* dst_extent = op->add_dst_extents();
- dst_extent->set_start_block(processor->offset() / config.block_size);
- dst_extent->set_num_blocks(
- processor->buffer_in().size() / config.block_size);
+ // Preset all the static information about the operations. The
+ // ChunkProcessor will set the rest.
+ AnnotatedOperation* aop = aops->data() + i;
+ aop->name = base::StringPrintf("<%s-operation-%" PRIuS ">",
+ part_name_str.c_str(), i);
+ Extent* dst_extent = aop->op.add_dst_extents();
+ dst_extent->set_start_block(start_block);
+ dst_extent->set_num_blocks(num_blocks);
- int progress = static_cast<int>(
- (processor->offset() + processor->buffer_in().size()) * 100.0 /
- partition->size);
- if (last_progress_update < progress &&
- (last_progress_update + 10 <= progress || progress == 100)) {
- LOG(INFO) << progress << "% complete (output size: "
- << *data_file_size << ")";
- last_progress_update = progress;
- }
- }
+ chunk_processors.emplace_back(
+ in_fd,
+ static_cast<off_t>(start_block) * block_size,
+ num_blocks * block_size,
+ blob_file,
+ aop);
}
+ // Thread pool used for worker threads.
+ base::DelegateSimpleThreadPool thread_pool("full-update-generator",
+ max_threads);
+ thread_pool.Start();
+ for (ChunkProcessor& processor : chunk_processors)
+ thread_pool.AddWork(&processor);
+ thread_pool.JoinAll();
+ // All the operations must have a type set at this point. Otherwise, a
+ // ChunkProcessor failed to complete.
+ for (const AnnotatedOperation& aop : *aops) {
+ if (!aop.op.has_type())
+ return false;
+ }
return true;
}
diff --git a/payload_generator/full_update_generator.h b/payload_generator/full_update_generator.h
index b4d1135..4d75b22 100644
--- a/payload_generator/full_update_generator.h
+++ b/payload_generator/full_update_generator.h
@@ -10,6 +10,7 @@
#include <base/macros.h>
+#include "update_engine/payload_generator/blob_file_writer.h"
#include "update_engine/payload_generator/operations_generator.h"
#include "update_engine/payload_generator/payload_generation_config.h"
@@ -19,6 +20,7 @@
public:
FullUpdateGenerator() = default;
+ // OperationsGenerator override.
// Creates a full update for the target image defined in |config|. |config|
// must be a valid payload generation configuration for a full payload.
// Populates |rootfs_ops| and |kernel_ops|, with data about the update
@@ -26,11 +28,24 @@
// |data_file_size| as it does.
bool GenerateOperations(
const PayloadGenerationConfig& config,
- int data_file_fd,
- off_t* data_file_size,
+ BlobFileWriter* blob_file,
std::vector<AnnotatedOperation>* rootfs_ops,
std::vector<AnnotatedOperation>* kernel_ops) override;
+ // Generates the list of operations to update inplace from the partition
+ // |old_part| to |new_part|. The |partition_size| should be at least
+ // |new_part.size| and any extra space there could be used as scratch space.
+ // The operations generated will not write more than |chunk_blocks| blocks.
+ // The new operations will create blobs in |data_file_fd| and update
+ // the file size pointed by |data_file_size| if needed.
+ // On success, stores the new operations in |aops| and returns true.
+ static bool GenerateOperationsForPartition(
+ const PartitionConfig& new_part,
+ size_t block_size,
+ size_t chunk_blocks,
+ BlobFileWriter* blob_file,
+ std::vector<AnnotatedOperation>* aops);
+
private:
DISALLOW_COPY_AND_ASSIGN(FullUpdateGenerator);
};
diff --git a/payload_generator/full_update_generator_unittest.cc b/payload_generator/full_update_generator_unittest.cc
index 0a04e4e..8c47ef6 100644
--- a/payload_generator/full_update_generator_unittest.cc
+++ b/payload_generator/full_update_generator_unittest.cc
@@ -24,7 +24,7 @@
void SetUp() override {
config_.is_delta = false;
config_.minor_version = DeltaPerformer::kFullPayloadMinorVersion;
- config_.chunk_size = 128 * 1024;
+ config_.hard_chunk_size = 128 * 1024;
config_.block_size = 4096;
EXPECT_TRUE(utils::MakeTempFile("FullUpdateTest_rootfs.XXXXXX",
@@ -37,6 +37,7 @@
&out_blobs_path_,
&out_blobs_fd_));
+ blob_file_.reset(new BlobFileWriter(out_blobs_fd_, &out_blobs_length_));
rootfs_part_unlinker_.reset(
new ScopedPathUnlinker(config_.target.rootfs.path));
kernel_part_unlinker_.reset(
@@ -49,8 +50,10 @@
// Output file holding the payload blobs.
string out_blobs_path_;
int out_blobs_fd_{-1};
+ off_t out_blobs_length_{0};
ScopedFdCloser out_blobs_fd_closer_{&out_blobs_fd_};
+ std::unique_ptr<BlobFileWriter> blob_file_;
std::unique_ptr<ScopedPathUnlinker> rootfs_part_unlinker_;
std::unique_ptr<ScopedPathUnlinker> kernel_part_unlinker_;
std::unique_ptr<ScopedPathUnlinker> out_blobs_unlinker_;
@@ -75,29 +78,25 @@
EXPECT_TRUE(test_utils::WriteFileVector(config_.target.kernel.path,
new_kern));
- off_t out_blobs_length = 0;
vector<AnnotatedOperation> rootfs_ops;
vector<AnnotatedOperation> kernel_ops;
EXPECT_TRUE(generator_.GenerateOperations(config_,
- out_blobs_fd_,
- &out_blobs_length,
+ blob_file_.get(),
&rootfs_ops,
&kernel_ops));
int64_t target_rootfs_chunks =
- config_.target.rootfs.size / config_.chunk_size;
+ config_.target.rootfs.size / config_.hard_chunk_size;
EXPECT_EQ(target_rootfs_chunks, rootfs_ops.size());
- EXPECT_EQ(new_kern.size() / config_.chunk_size, kernel_ops.size());
+ EXPECT_EQ(new_kern.size() / config_.hard_chunk_size, kernel_ops.size());
for (off_t i = 0; i < target_rootfs_chunks; ++i) {
EXPECT_EQ(1, rootfs_ops[i].op.dst_extents_size());
- EXPECT_EQ(i * config_.chunk_size / config_.block_size,
+ EXPECT_EQ(i * config_.hard_chunk_size / config_.block_size,
rootfs_ops[i].op.dst_extents(0).start_block()) << "i = " << i;
- EXPECT_EQ(config_.chunk_size / config_.block_size,
+ EXPECT_EQ(config_.hard_chunk_size / config_.block_size,
rootfs_ops[i].op.dst_extents(0).num_blocks());
- if (rootfs_ops[i].op.type() !=
- DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
- EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ,
- rootfs_ops[i].op.type());
+ if (rootfs_ops[i].op.type() != InstallOperation::REPLACE) {
+ EXPECT_EQ(InstallOperation::REPLACE_BZ, rootfs_ops[i].op.type());
}
}
}
@@ -105,7 +104,8 @@
// Test that if the chunk size is not a divisor of the image size, it handles
// correctly the last chunk of each partition.
TEST_F(FullUpdateGeneratorTest, ChunkSizeTooBig) {
- config_.chunk_size = 1024 * 1024;
+ config_.hard_chunk_size = 1024 * 1024;
+ config_.soft_chunk_size = config_.hard_chunk_size;
chromeos::Blob new_root(1536 * 1024); // 1.5 MiB
chromeos::Blob new_kern(128 * 1024);
config_.rootfs_partition_size = new_root.size();
@@ -117,20 +117,18 @@
EXPECT_TRUE(test_utils::WriteFileVector(config_.target.kernel.path,
new_kern));
- off_t out_blobs_length = 0;
vector<AnnotatedOperation> rootfs_ops;
vector<AnnotatedOperation> kernel_ops;
EXPECT_TRUE(generator_.GenerateOperations(config_,
- out_blobs_fd_,
- &out_blobs_length,
+ blob_file_.get(),
&rootfs_ops,
&kernel_ops));
// rootfs has one chunk and a half.
EXPECT_EQ(2, rootfs_ops.size());
- EXPECT_EQ(config_.chunk_size / config_.block_size,
+ EXPECT_EQ(config_.hard_chunk_size / config_.block_size,
BlocksInExtents(rootfs_ops[0].op.dst_extents()));
- EXPECT_EQ((new_root.size() - config_.chunk_size) / config_.block_size,
+ EXPECT_EQ((new_root.size() - config_.hard_chunk_size) / config_.block_size,
BlocksInExtents(rootfs_ops[1].op.dst_extents()));
// kernel has less than one chunk.
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index 170a612..ecdadc7 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -16,7 +16,6 @@
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_split.h>
#include <chromeos/flag_helper.h>
-#include <glib.h>
#include "update_engine/delta_performer.h"
#include "update_engine/payload_generator/delta_diff_generator.h"
@@ -25,7 +24,6 @@
#include "update_engine/payload_generator/payload_signer.h"
#include "update_engine/payload_verifier.h"
#include "update_engine/prefs.h"
-#include "update_engine/subprocess.h"
#include "update_engine/terminator.h"
#include "update_engine/update_metadata.pb.h"
#include "update_engine/utils.h"
@@ -217,12 +215,6 @@
}
int Main(int argc, char** argv) {
- DEFINE_string(old_dir, "",
- "[DEPRECATED] Directory where the old rootfs is loop mounted "
- "read-only. Not required anymore.");
- DEFINE_string(new_dir, "",
- "[DEPRECATED] Directory where the new rootfs is loop mounted "
- "read-only. Not required anymore.");
DEFINE_string(old_image, "", "Path to the old rootfs");
DEFINE_string(new_image, "", "Path to the new rootfs");
DEFINE_string(old_kernel, "", "Path to the old kernel partition image");
@@ -252,7 +244,8 @@
"e.g. /path/to/sig:/path/to/next:/path/to/last_sig . Each "
"signature will be assigned a client version, starting from "
"kSignatureOriginalVersion.");
- DEFINE_int32(chunk_size, -1, "Payload chunk size (-1 -- no limit/default)");
+ DEFINE_int32(chunk_size, 200 * 1024 * 1024,
+ "Payload chunk size (-1 for whole files)");
DEFINE_uint64(rootfs_partition_size,
chromeos_update_engine::kRootFSPartitionSize,
"RootFS partition size for the image once installed");
@@ -302,24 +295,15 @@
"image is provided. It also provides debugging options to apply, sign\n"
"and verify payloads.");
Terminator::Init();
- Subprocess::Init();
logging::LoggingSettings log_settings;
log_settings.log_file = "delta_generator.log";
log_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
- log_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
+ log_settings.lock_log = logging::LOCK_LOG_FILE;
log_settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
logging::InitLogging(log_settings);
- // Check flags.
- if (!FLAGS_old_dir.empty()) {
- LOG(INFO) << "--old_dir flag is deprecated and ignored.";
- }
- if (!FLAGS_new_dir.empty()) {
- LOG(INFO) << "--new_dir flag is deprecated and ignored.";
- }
-
vector<int> signature_sizes;
ParseSignatureSizes(FLAGS_signature_size, &signature_sizes);
@@ -359,7 +343,8 @@
payload_config.target.rootfs.path = FLAGS_new_image;
payload_config.target.kernel.path = FLAGS_new_kernel;
- payload_config.chunk_size = FLAGS_chunk_size;
+ // Use the default soft_chunk_size defined in the config.
+ payload_config.hard_chunk_size = FLAGS_chunk_size;
payload_config.block_size = kBlockSize;
// The kernel and rootfs size is never passed to the delta_generator, so we
diff --git a/payload_generator/graph_types.h b/payload_generator/graph_types.h
index 7b20bb5..16572b4 100644
--- a/payload_generator/graph_types.h
+++ b/payload_generator/graph_types.h
@@ -13,6 +13,7 @@
#include <base/macros.h>
+#include "update_engine/payload_generator/annotated_operation.h"
#include "update_engine/payload_generator/extent_utils.h"
#include "update_engine/update_metadata.pb.h"
@@ -59,8 +60,7 @@
std::vector<Vertex>::size_type lowlink;
// Other Vertex properties:
- DeltaArchiveManifest_InstallOperation op;
- std::string file_name;
+ AnnotatedOperation aop;
typedef std::vector<Vertex>::size_type Index;
static const Vertex::Index kInvalidIndex;
diff --git a/payload_generator/graph_utils.cc b/payload_generator/graph_utils.cc
index 9166560..265177f 100644
--- a/payload_generator/graph_utils.cc
+++ b/payload_generator/graph_utils.cc
@@ -114,12 +114,12 @@
for (Graph::size_type i = 0, e = graph.size(); i != e; ++i) {
LOG(INFO) << i
<< (graph[i].valid ? "" : "-INV")
- << ": " << graph[i].file_name
- << ": " << InstallOperationTypeName(graph[i].op.type());
+ << ": " << graph[i].aop.name
+ << ": " << InstallOperationTypeName(graph[i].aop.op.type());
LOG(INFO) << " src_extents:";
- DumpExtents(graph[i].op.src_extents(), 4);
+ DumpExtents(graph[i].aop.op.src_extents(), 4);
LOG(INFO) << " dst_extents:";
- DumpExtents(graph[i].op.dst_extents(), 4);
+ DumpExtents(graph[i].aop.op.dst_extents(), 4);
LOG(INFO) << " out edges:";
DumpOutEdges(graph[i].out_edges);
}
diff --git a/payload_generator/inplace_generator.cc b/payload_generator/inplace_generator.cc
index 2e0dbaf..f2d8a11 100644
--- a/payload_generator/inplace_generator.cc
+++ b/payload_generator/inplace_generator.cc
@@ -27,13 +27,14 @@
using std::pair;
using std::set;
using std::string;
-using std::unique_ptr;
using std::vector;
namespace chromeos_update_engine {
using Block = InplaceGenerator::Block;
+namespace {
+
// This class allocates non-existent temp blocks, starting from
// kTempBlockStart. Other code is responsible for converting these
// temp blocks into real blocks, as the client can't read or write to
@@ -62,9 +63,28 @@
return new_extents;
}
+// Helper class to compare two operations by start block of the first Extent in
+// their destination extents given the index of the operations in the graph.
+class IndexedInstallOperationsDstComparator {
+ public:
+ explicit IndexedInstallOperationsDstComparator(Graph* graph)
+ : graph_(graph) {}
+
+ // Compares the operations in the vertex a and b of graph_.
+ bool operator()(size_t a, size_t b) const {
+ return diff_utils::CompareAopsByDestination((*graph_)[a].aop,
+ (*graph_)[b].aop);
+ }
+
+ private:
+ const Graph* const graph_;
+};
+
+} // namespace
+
void InplaceGenerator::CheckGraph(const Graph& graph) {
for (const Vertex& v : graph) {
- CHECK(v.op.has_type());
+ CHECK(v.aop.op.has_type());
}
}
@@ -74,7 +94,7 @@
const vector<Extent>& replace_extents) {
// First, expand out the blocks that op reads from
vector<uint64_t> read_blocks =
- ExpandExtents(vertex->op.src_extents());
+ ExpandExtents(vertex->aop.op.src_extents());
{
// Expand remove_extents and replace_extents
vector<uint64_t> remove_extents_expanded = ExpandExtents(remove_extents);
@@ -95,9 +115,9 @@
}
}
// Convert read_blocks back to extents
- vertex->op.clear_src_extents();
+ vertex->aop.op.clear_src_extents();
vector<Extent> new_extents = CompressExtents(read_blocks);
- StoreExtents(new_extents, vertex->op.mutable_src_extents());
+ StoreExtents(new_extents, vertex->aop.op.mutable_src_extents());
}
bool InplaceGenerator::CutEdges(Graph* graph,
@@ -136,14 +156,14 @@
cut_edge_properties));
// Set src/dst extents and other proto variables for copy operation
- graph->back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
+ graph->back().aop.op.set_type(InstallOperation::MOVE);
StoreExtents(cut_edge_properties.extents,
- graph->back().op.mutable_src_extents());
+ graph->back().aop.op.mutable_src_extents());
StoreExtents(cuts.back().tmp_extents,
- graph->back().op.mutable_dst_extents());
- graph->back().op.set_src_length(
+ graph->back().aop.op.mutable_dst_extents());
+ graph->back().aop.op.set_src_length(
graph_utils::EdgeWeight(*graph, edge) * kBlockSize);
- graph->back().op.set_dst_length(graph->back().op.src_length());
+ graph->back().aop.op.set_dst_length(graph->back().aop.op.src_length());
// make the dest node read from the scratch space
SubstituteBlocks(
@@ -235,24 +255,26 @@
sort(cuts->begin(), cuts->end(), less);
}
-void InplaceGenerator::MoveFullOpsToBack(Graph* graph,
- vector<Vertex::Index>* op_indexes) {
+void InplaceGenerator::MoveAndSortFullOpsToBack(
+ Graph* graph,
+ vector<Vertex::Index>* op_indexes) {
vector<Vertex::Index> ret;
vector<Vertex::Index> full_ops;
ret.reserve(op_indexes->size());
- for (vector<Vertex::Index>::size_type i = 0, e = op_indexes->size(); i != e;
- ++i) {
- DeltaArchiveManifest_InstallOperation_Type type =
- (*graph)[(*op_indexes)[i]].op.type();
- if (type == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
- type == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
- full_ops.push_back((*op_indexes)[i]);
+ for (auto op_index : *op_indexes) {
+ InstallOperation_Type type = (*graph)[op_index].aop.op.type();
+ if (type == InstallOperation::REPLACE ||
+ type == InstallOperation::REPLACE_BZ) {
+ full_ops.push_back(op_index);
} else {
- ret.push_back((*op_indexes)[i]);
+ ret.push_back(op_index);
}
}
LOG(INFO) << "Stats: " << full_ops.size() << " full ops out of "
<< (full_ops.size() + ret.size()) << " total ops.";
+ // Sort full ops according to their dst_extents.
+ sort(full_ops.begin(), full_ops.end(),
+ IndexedInstallOperationsDstComparator(graph));
ret.insert(ret.end(), full_ops.begin(), full_ops.end());
op_indexes->swap(ret);
}
@@ -285,8 +307,7 @@
bool ConvertCutsToFull(
Graph* graph,
const string& new_part,
- int data_fd,
- off_t* data_file_size,
+ BlobFileWriter* blob_file,
vector<Vertex::Index>* op_indexes,
vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
const vector<CutEdgeVertexes>& cuts) {
@@ -297,8 +318,7 @@
graph,
cut,
new_part,
- data_fd,
- data_file_size));
+ blob_file));
deleted_nodes.insert(cut.new_vertex);
}
deleted_nodes.insert(cuts[0].old_dst);
@@ -325,8 +345,7 @@
bool AssignBlockForAdjoiningCuts(
Graph* graph,
const string& new_part,
- int data_fd,
- off_t* data_file_size,
+ BlobFileWriter* blob_file,
vector<Vertex::Index>* op_indexes,
vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
const vector<CutEdgeVertexes>& cuts) {
@@ -357,10 +376,10 @@
continue;
// See if this node has sufficient blocks
ExtentRanges ranges;
- ranges.AddRepeatedExtents((*graph)[test_node].op.dst_extents());
+ ranges.AddRepeatedExtents((*graph)[test_node].aop.op.dst_extents());
ranges.SubtractExtent(ExtentForRange(
kTempBlockStart, kSparseHole - kTempBlockStart));
- ranges.SubtractRepeatedExtents((*graph)[test_node].op.src_extents());
+ ranges.SubtractRepeatedExtents((*graph)[test_node].aop.op.src_extents());
// For now, for simplicity, subtract out all blocks in read-before
// dependencies.
for (Vertex::EdgeMap::const_iterator edge_i =
@@ -369,6 +388,14 @@
edge_i != edge_e; ++edge_i) {
ranges.SubtractExtents(edge_i->second.extents);
}
+
+ // Prevent using the block 0 as scratch space due to crbug.com/480751.
+ if (ranges.ContainsBlock(0)) {
+ LOG(INFO) << "Removing block 0 from the selected scratch range in vertex "
+ << i;
+ ranges.SubtractBlock(0);
+ }
+
if (ranges.blocks() == 0)
continue;
@@ -389,8 +416,7 @@
LOG(INFO) << "Unable to find sufficient scratch";
TEST_AND_RETURN_FALSE(ConvertCutsToFull(graph,
new_part,
- data_fd,
- data_file_size,
+ blob_file,
op_indexes,
reverse_op_indexes,
cuts));
@@ -423,7 +449,7 @@
// Fix the new node w/ the real blocks. Since the new node is just a
// copy operation, we can replace all the dest extents w/ the real
// blocks.
- DeltaArchiveManifest_InstallOperation *op = &(*graph)[cut.new_vertex].op;
+ InstallOperation* op = &(*graph)[cut.new_vertex].aop.op;
op->clear_dst_extents();
StoreExtents(real_extents, op->mutable_dst_extents());
}
@@ -435,8 +461,7 @@
bool InplaceGenerator::AssignTempBlocks(
Graph* graph,
const string& new_part,
- int data_fd,
- off_t* data_file_size,
+ BlobFileWriter* blob_file,
vector<Vertex::Index>* op_indexes,
vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
const vector<CutEdgeVertexes>& cuts) {
@@ -450,7 +475,7 @@
LOG(INFO) << "Fixing temp blocks in cut " << i
<< ": old dst: " << cuts[i].old_dst << " new vertex: "
<< cuts[i].new_vertex << " path: "
- << (*graph)[cuts[i].old_dst].file_name;
+ << (*graph)[cuts[i].old_dst].aop.name;
if (cuts_group.empty() || (cuts_group[0].old_dst == cuts[i].old_dst)) {
cuts_group.push_back(cuts[i]);
@@ -458,8 +483,7 @@
CHECK(!cuts_group.empty());
TEST_AND_RETURN_FALSE(AssignBlockForAdjoiningCuts(graph,
new_part,
- data_fd,
- data_file_size,
+ blob_file,
op_indexes,
reverse_op_indexes,
cuts_group));
@@ -475,8 +499,7 @@
CHECK(!cuts_group.empty());
TEST_AND_RETURN_FALSE(AssignBlockForAdjoiningCuts(graph,
new_part,
- data_fd,
- data_file_size,
+ blob_file,
op_indexes,
reverse_op_indexes,
cuts_group));
@@ -489,7 +512,7 @@
++it, ++idx) {
if (!it->valid)
continue;
- const DeltaArchiveManifest_InstallOperation& op = it->op;
+ const InstallOperation& op = it->aop.op;
if (TempBlocksExistInExtents(op.dst_extents()) ||
TempBlocksExistInExtents(op.src_extents())) {
LOG(INFO) << "bad extents in node " << idx;
@@ -513,15 +536,12 @@
bool InplaceGenerator::ConvertCutToFullOp(Graph* graph,
const CutEdgeVertexes& cut,
const string& new_part,
- int data_fd,
- off_t* data_file_size) {
+ BlobFileWriter* blob_file) {
// Drop all incoming edges, keep all outgoing edges
// Keep all outgoing edges
- if ((*graph)[cut.old_dst].op.type() !=
- DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ &&
- (*graph)[cut.old_dst].op.type() !=
- DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
+ if ((*graph)[cut.old_dst].aop.op.type() != InstallOperation::REPLACE_BZ &&
+ (*graph)[cut.old_dst].aop.op.type() != InstallOperation::REPLACE) {
Vertex::EdgeMap out_edges = (*graph)[cut.old_dst].out_edges;
graph_utils::DropWriteBeforeDeps(&out_edges);
@@ -529,7 +549,7 @@
// |new_extents| list of blocks and update the graph.
vector<AnnotatedOperation> new_aop;
vector<Extent> new_extents;
- ExtentsToVector((*graph)[cut.old_dst].op.dst_extents(),
+ ExtentsToVector((*graph)[cut.old_dst].aop.op.dst_extents(),
&new_extents);
TEST_AND_RETURN_FALSE(diff_utils::DeltaReadFile(
&new_aop,
@@ -537,10 +557,9 @@
new_part,
vector<Extent>(), // old_extents
new_extents,
- (*graph)[cut.old_dst].file_name,
+ (*graph)[cut.old_dst].aop.name,
-1, // chunk_blocks, forces to have a single operation.
- data_fd,
- data_file_size,
+ blob_file,
false)); // src_ops_allowed
TEST_AND_RETURN_FALSE(new_aop.size() == 1);
TEST_AND_RETURN_FALSE(AddInstallOpToGraph(
@@ -564,8 +583,7 @@
bool InplaceGenerator::ConvertGraphToDag(Graph* graph,
const string& new_part,
- int fd,
- off_t* data_file_size,
+ BlobFileWriter* blob_file,
vector<Vertex::Index>* final_order,
Vertex::Index scratch_vertex) {
CycleBreaker cycle_breaker;
@@ -590,7 +608,7 @@
CheckGraph(*graph);
LOG(INFO) << "Moving full ops to the back";
- MoveFullOpsToBack(graph, final_order);
+ MoveAndSortFullOpsToBack(graph, final_order);
LOG(INFO) << "done moving full ops to back";
vector<vector<Vertex::Index>::size_type> inverse_final_order;
@@ -601,8 +619,7 @@
if (!cuts.empty())
TEST_AND_RETURN_FALSE(AssignTempBlocks(graph,
new_part,
- fd,
- data_file_size,
+ blob_file,
final_order,
&inverse_final_order,
cuts));
@@ -625,17 +642,17 @@
void InplaceGenerator::CreateScratchNode(uint64_t start_block,
uint64_t num_blocks,
Vertex* vertex) {
- vertex->file_name = "<scratch>";
- vertex->op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
- vertex->op.set_data_offset(0);
- vertex->op.set_data_length(0);
- Extent* extent = vertex->op.add_dst_extents();
+ vertex->aop.name = "<scratch>";
+ vertex->aop.op.set_type(InstallOperation::REPLACE_BZ);
+ vertex->aop.op.set_data_offset(0);
+ vertex->aop.op.set_data_length(0);
+ Extent* extent = vertex->aop.op.add_dst_extents();
extent->set_start_block(start_block);
extent->set_num_blocks(num_blocks);
}
bool InplaceGenerator::AddInstallOpToBlocksVector(
- const DeltaArchiveManifest_InstallOperation& operation,
+ const InstallOperation& operation,
const Graph& graph,
Vertex::Index vertex,
vector<Block>* blocks) {
@@ -661,9 +678,9 @@
LOG(FATAL) << "Block " << block << " is already "
<< past_participle << " by "
<< (*blocks)[block].*access_type << "("
- << graph[(*blocks)[block].*access_type].file_name
+ << graph[(*blocks)[block].*access_type].aop.name
<< ") and also " << vertex << "("
- << graph[vertex].file_name << ")";
+ << graph[vertex].aop.name << ")";
}
(*blocks)[block].*access_type = vertex;
}
@@ -672,24 +689,23 @@
return true;
}
-bool InplaceGenerator::AddInstallOpToGraph(
- Graph* graph,
- Vertex::Index existing_vertex,
- vector<Block>* blocks,
- const DeltaArchiveManifest_InstallOperation operation,
- const string& op_name) {
+bool InplaceGenerator::AddInstallOpToGraph(Graph* graph,
+ Vertex::Index existing_vertex,
+ vector<Block>* blocks,
+ const InstallOperation operation,
+ const string& op_name) {
Vertex::Index vertex = existing_vertex;
if (vertex == Vertex::kInvalidIndex) {
graph->emplace_back();
vertex = graph->size() - 1;
}
- (*graph)[vertex].op = operation;
- CHECK((*graph)[vertex].op.has_type());
- (*graph)[vertex].file_name = op_name;
+ (*graph)[vertex].aop.op = operation;
+ CHECK((*graph)[vertex].aop.op.has_type());
+ (*graph)[vertex].aop.name = op_name;
if (blocks)
TEST_AND_RETURN_FALSE(InplaceGenerator::AddInstallOpToBlocksVector(
- (*graph)[vertex].op,
+ (*graph)[vertex].aop.op,
*graph,
vertex,
blocks));
@@ -705,62 +721,31 @@
}
}
-bool InplaceGenerator::GenerateOperations(
- const PayloadGenerationConfig& config,
- int data_file_fd,
- off_t* data_file_size,
- vector<AnnotatedOperation>* rootfs_ops,
- vector<AnnotatedOperation>* kernel_ops) {
- off_t chunk_blocks = (config.chunk_size == -1 ? -1 :
- config.chunk_size / config.block_size);
-
- // Temporary list of operations used to construct the dependency graph.
- vector<AnnotatedOperation> aops;
- TEST_AND_RETURN_FALSE(
- diff_utils::DeltaReadPartition(&aops,
- config.source.rootfs,
- config.target.rootfs,
- chunk_blocks,
- data_file_fd,
- data_file_size,
- true, // skip_block_0
- false)); // src_ops_allowed
- // Convert the rootfs operations to the graph.
+bool InplaceGenerator::ResolveReadAfterWriteDependencies(
+ const PartitionConfig& new_part,
+ uint64_t partition_size,
+ size_t block_size,
+ BlobFileWriter* blob_file,
+ vector<AnnotatedOperation>* aops) {
+ // Convert the operations to the graph.
Graph graph;
CheckGraph(graph);
- vector<Block> blocks(config.target.rootfs.size / config.block_size);
- for (const auto& aop : aops) {
+ vector<Block> blocks(new_part.size / block_size);
+ for (const auto& aop : *aops) {
AddInstallOpToGraph(
&graph, Vertex::kInvalidIndex, &blocks, aop.op, aop.name);
}
- LOG(INFO) << "done reading normal files";
CheckGraph(graph);
// Final scratch block (if there's space)
Vertex::Index scratch_vertex = Vertex::kInvalidIndex;
- if (blocks.size() < (config.rootfs_partition_size / kBlockSize)) {
+ if (blocks.size() < (partition_size / block_size)) {
scratch_vertex = graph.size();
graph.emplace_back();
- CreateScratchNode(
- blocks.size(),
- (config.rootfs_partition_size / kBlockSize) - blocks.size(),
- &graph.back());
+ size_t scratch_blocks = (partition_size / block_size) - blocks.size();
+ LOG(INFO) << "Added " << scratch_blocks << " scratch space blocks.";
+ CreateScratchNode(blocks.size(), scratch_blocks, &graph.back());
}
-
- // Read kernel partition
- LOG(INFO) << "Delta compressing kernel partition...";
- // It is safe to not skip the block 0 since we will not be using the cycle
- // breaking algorithm on this list of operations as we expect no cycles here.
- TEST_AND_RETURN_FALSE(
- diff_utils::DeltaReadPartition(kernel_ops,
- config.source.kernel,
- config.target.kernel,
- chunk_blocks,
- data_file_fd,
- data_file_size,
- false, // skip_block_0
- false)); // src_ops_allowed
- LOG(INFO) << "done reading kernel";
CheckGraph(graph);
LOG(INFO) << "Creating edges...";
@@ -771,34 +756,81 @@
vector<Vertex::Index> final_order;
TEST_AND_RETURN_FALSE(ConvertGraphToDag(
&graph,
- config.target.rootfs.path,
- data_file_fd,
- data_file_size,
+ new_part.path,
+ blob_file,
&final_order,
scratch_vertex));
- // Copy operations over to the rootfs_ops in the final_order generated by the
- // topological sort.
- rootfs_ops->clear();
+ // Copy operations over to the |aops| vector in the final_order generated by
+ // the topological sort.
+ aops->clear();
+ aops->reserve(final_order.size());
for (const Vertex::Index vertex_index : final_order) {
const Vertex& vertex = graph[vertex_index];
- rootfs_ops->emplace_back();
- rootfs_ops->back().op = vertex.op;
- rootfs_ops->back().name = vertex.file_name;
+ aops->push_back(vertex.aop);
}
+ return true;
+}
- // Re-add the operation for the block 0.
- TEST_AND_RETURN_FALSE(diff_utils::DeltaReadFile(
- rootfs_ops,
- config.source.rootfs.path,
- config.target.rootfs.path,
- vector<Extent>{ExtentForRange(0, 1)},
- vector<Extent>{ExtentForRange(0, 1)},
- "<block-0>", // operation name
- -1, // chunk_blocks
- data_file_fd,
- data_file_size,
- false)); // src_ops_allowed
+bool InplaceGenerator::GenerateOperationsForPartition(
+ const PartitionConfig& old_part,
+ const PartitionConfig& new_part,
+ uint64_t partition_size,
+ size_t block_size,
+ ssize_t hard_chunk_blocks,
+ size_t soft_chunk_blocks,
+ BlobFileWriter* blob_file,
+ vector<AnnotatedOperation>* aops) {
+ const string part_name = PartitionNameString(new_part.name);
+ LOG(INFO) << "Delta compressing " << part_name << " partition...";
+ TEST_AND_RETURN_FALSE(
+ diff_utils::DeltaReadPartition(aops,
+ old_part,
+ new_part,
+ hard_chunk_blocks,
+ soft_chunk_blocks,
+ blob_file,
+ false)); // src_ops_allowed
+ LOG(INFO) << "Done reading " << part_name;
+
+ TEST_AND_RETURN_FALSE(
+ ResolveReadAfterWriteDependencies(new_part,
+ partition_size,
+ block_size,
+ blob_file,
+ aops));
+ LOG(INFO) << "Done reordering " << part_name;
+ return true;
+}
+
+bool InplaceGenerator::GenerateOperations(
+ const PayloadGenerationConfig& config,
+ BlobFileWriter* blob_file,
+ vector<AnnotatedOperation>* rootfs_ops,
+ vector<AnnotatedOperation>* kernel_ops) {
+ ssize_t hard_chunk_blocks = (config.hard_chunk_size == -1 ? -1 :
+ config.hard_chunk_size / config.block_size);
+ size_t soft_chunk_blocks = config.soft_chunk_size / config.block_size;
+
+ TEST_AND_RETURN_FALSE(GenerateOperationsForPartition(
+ config.source.rootfs,
+ config.target.rootfs,
+ config.rootfs_partition_size,
+ config.block_size,
+ hard_chunk_blocks,
+ soft_chunk_blocks,
+ blob_file,
+ rootfs_ops));
+
+ TEST_AND_RETURN_FALSE(GenerateOperationsForPartition(
+ config.source.kernel,
+ config.target.kernel,
+ config.target.kernel.size, // kernel "filesystem" is the whole partition.
+ config.block_size,
+ hard_chunk_blocks,
+ soft_chunk_blocks,
+ blob_file,
+ kernel_ops));
return true;
}
diff --git a/payload_generator/inplace_generator.h b/payload_generator/inplace_generator.h
index 158e364..70e868c 100644
--- a/payload_generator/inplace_generator.h
+++ b/payload_generator/inplace_generator.h
@@ -10,6 +10,7 @@
#include <string>
#include <vector>
+#include "update_engine/payload_generator/blob_file_writer.h"
#include "update_engine/payload_generator/delta_diff_generator.h"
#include "update_engine/payload_generator/graph_types.h"
#include "update_engine/payload_generator/operations_generator.h"
@@ -103,7 +104,7 @@
// Given a topologically sorted graph |op_indexes| and |graph|, alters
// |op_indexes| to move all the full operations to the end of the vector.
// Full operations should not be depended on, so this is safe.
- static void MoveFullOpsToBack(Graph* graph,
+ static void MoveAndSortFullOpsToBack(Graph* graph,
std::vector<Vertex::Index>* op_indexes);
// Returns true iff there are no extents in the graph that refer to temp
@@ -120,8 +121,7 @@
static bool AssignTempBlocks(
Graph* graph,
const std::string& new_part,
- int data_fd,
- off_t* data_file_size,
+ BlobFileWriter* blob_file,
std::vector<Vertex::Index>* op_indexes,
std::vector<std::vector<Vertex::Index>::size_type>* reverse_op_indexes,
const std::vector<CutEdgeVertexes>& cuts);
@@ -136,8 +136,7 @@
static bool ConvertCutToFullOp(Graph* graph,
const CutEdgeVertexes& cut,
const std::string& new_part,
- int data_fd,
- off_t* data_file_size);
+ BlobFileWriter* blob_file);
// Takes a graph, which is not a DAG, which represents the files just
// read from disk, and converts it into a DAG by breaking all cycles
@@ -150,8 +149,7 @@
// Returns true on success.
static bool ConvertGraphToDag(Graph* graph,
const std::string& new_part,
- int fd,
- off_t* data_file_size,
+ BlobFileWriter* blob_file,
std::vector<Vertex::Index>* final_order,
Vertex::Index scratch_vertex);
@@ -170,23 +168,21 @@
// in |blocks| and set the reader/writer field to the vertex passed.
// |graph| is not strictly necessary, but useful for printing out
// error messages.
- static bool AddInstallOpToBlocksVector(
- const DeltaArchiveManifest_InstallOperation& operation,
- const Graph& graph,
- Vertex::Index vertex,
- std::vector<Block>* blocks);
+ static bool AddInstallOpToBlocksVector(const InstallOperation& operation,
+ const Graph& graph,
+ Vertex::Index vertex,
+ std::vector<Block>* blocks);
// Add a vertex (if |existing_vertex| is kInvalidVertex) or update an
// |existing_vertex| with the passed |operation|.
// This method will also register the vertex as the reader or writer of the
// blocks involved in the operation updating the |blocks| vector. The
// |op_name| associated with the Vertex is used for logging purposes.
- static bool AddInstallOpToGraph(
- Graph* graph,
- Vertex::Index existing_vertex,
- std::vector<Block>* blocks,
- const DeltaArchiveManifest_InstallOperation operation,
- const std::string& op_name);
+ static bool AddInstallOpToGraph(Graph* graph,
+ Vertex::Index existing_vertex,
+ std::vector<Block>* blocks,
+ const InstallOperation operation,
+ const std::string& op_name);
// Apply the transformation stored in |the_map| to the |collection| vector
// replacing the map keys found in |collection| with its associated value in
@@ -194,6 +190,39 @@
static void ApplyMap(std::vector<uint64_t>* collection,
const std::map<uint64_t, uint64_t>& the_map);
+ // Resolve all read-after-write dependencies in the operation list |aops|. The
+ // operations in |aops| are such that they generate the desired |new_part| if
+ // applied reading always from the original image. This function reorders the
+ // operations and generates new operations when needed to make these
+ // operations produce the same |new_part| result when applied in-place.
+ // The new operations will create blobs in |data_file_fd| and update
+ // the file size pointed by |data_file_size| if needed.
+ // On success, stores the new operations in |aops| in the right order and
+ // returns true.
+ static bool ResolveReadAfterWriteDependencies(
+ const PartitionConfig& new_part,
+ uint64_t partition_size,
+ size_t block_size,
+ BlobFileWriter* blob_file,
+ std::vector<AnnotatedOperation>* aops);
+
+ // Generates the list of operations to update inplace from the partition
+ // |old_part| to |new_part|. The |partition_size| should be at least
+ // |new_part.size| and any extra space there could be used as scratch space.
+ // The operations generated will not write more than |chunk_blocks| blocks.
+ // The new operations will create blobs in |data_file_fd| and update
+ // the file size pointed by |data_file_size| if needed.
+ // On success, stores the new operations in |aops| and returns true.
+ static bool GenerateOperationsForPartition(
+ const PartitionConfig& old_part,
+ const PartitionConfig& new_part,
+ uint64_t partition_size,
+ size_t block_size,
+ ssize_t hard_chunk_blocks,
+ size_t soft_chunk_blocks,
+ BlobFileWriter* blob_file,
+ std::vector<AnnotatedOperation>* aops);
+
// Generate the update payload operations for the kernel and rootfs using
// only operations that read from the target and/or write to the target,
// hence, applying the payload "in-place" in the target partition. This method
@@ -207,8 +236,7 @@
// |data_file_size|.
bool GenerateOperations(
const PayloadGenerationConfig& config,
- int data_file_fd,
- off_t* data_file_size,
+ BlobFileWriter* blob_file,
std::vector<AnnotatedOperation>* rootfs_ops,
std::vector<AnnotatedOperation>* kernel_ops) override;
diff --git a/payload_generator/inplace_generator_unittest.cc b/payload_generator/inplace_generator_unittest.cc
index 5de0df9..d938d0d 100644
--- a/payload_generator/inplace_generator_unittest.cc
+++ b/payload_generator/inplace_generator_unittest.cc
@@ -11,8 +11,10 @@
#include <utility>
#include <vector>
+#include <base/format_macros.h>
#include <base/logging.h>
#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
#include <gtest/gtest.h>
#include "update_engine/payload_generator/cycle_breaker.h"
@@ -35,20 +37,15 @@
namespace {
-#define OP_BSDIFF DeltaArchiveManifest_InstallOperation_Type_BSDIFF
-#define OP_MOVE DeltaArchiveManifest_InstallOperation_Type_MOVE
-#define OP_REPLACE DeltaArchiveManifest_InstallOperation_Type_REPLACE
-#define OP_REPLACE_BZ DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ
-
void GenVertex(Vertex* out,
const vector<Extent>& src_extents,
const vector<Extent>& dst_extents,
const string& path,
- DeltaArchiveManifest_InstallOperation_Type type) {
- out->op.set_type(type);
- out->file_name = path;
- StoreExtents(src_extents, out->op.mutable_src_extents());
- StoreExtents(dst_extents, out->op.mutable_dst_extents());
+ InstallOperation_Type type) {
+ out->aop.op.set_type(type);
+ out->aop.name = path;
+ StoreExtents(src_extents, out->aop.op.mutable_src_extents());
+ StoreExtents(dst_extents, out->aop.op.mutable_dst_extents());
}
vector<Extent> VectOfExt(uint64_t start_block, uint64_t num_blocks) {
@@ -83,9 +80,7 @@
vect->back().set_num_blocks(length);
}
-void OpAppendExtent(DeltaArchiveManifest_InstallOperation* op,
- uint64_t start,
- uint64_t length) {
+void OpAppendExtent(InstallOperation* op, uint64_t start, uint64_t length) {
Extent* extent = op->add_src_extents();
extent->set_start_block(start);
extent->set_num_blocks(length);
@@ -94,6 +89,31 @@
} // namespace
class InplaceGeneratorTest : public ::testing::Test {
+ protected:
+ // Initialize |blob_path_|, |blob_file_size_| and |blob_file_fd_| variables
+ // with a new blob file. The file is closed and removed automatically when
+ // the test finishes.
+ void CreateBlobFile() {
+ // blob_fd_closer_ takes a pointer to blob_fd_. Make sure we destroy a
+ // previous instance before overriding blob_fd_.
+ blob_fd_closer_.reset();
+ EXPECT_TRUE(utils::MakeTempFile(
+ "InplaceGenerator_blob_file.XXXXXX", &blob_path_, &blob_fd_));
+ blob_path_unlinker_.reset(new ScopedPathUnlinker(blob_path_));
+ blob_fd_closer_.reset(new ScopedFdCloser(&blob_fd_));
+ blob_file_size_ = 0;
+ EXPECT_GE(blob_fd_, 0);
+ blob_file_.reset(new BlobFileWriter(blob_fd_, &blob_file_size_));
+ }
+
+ // Blob file name, file descriptor and file size used to store operation
+ // blobs.
+ string blob_path_;
+ int blob_fd_{-1};
+ off_t blob_file_size_{0};
+ std::unique_ptr<BlobFileWriter> blob_file_;
+ std::unique_ptr<ScopedPathUnlinker> blob_path_unlinker_;
+ std::unique_ptr<ScopedFdCloser> blob_fd_closer_;
};
TEST_F(InplaceGeneratorTest, BlockDefaultValues) {
@@ -112,7 +132,7 @@
AppendExtent(&replace_blocks, 10, 2);
AppendExtent(&replace_blocks, 13, 2);
Vertex vertex;
- DeltaArchiveManifest_InstallOperation& op = vertex.op;
+ InstallOperation& op = vertex.aop.op;
OpAppendExtent(&op, 4, 3);
OpAppendExtent(&op, kSparseHole, 4); // Sparse hole in file
OpAppendExtent(&op, 3, 1);
@@ -144,14 +164,13 @@
// Create nodes in graph
{
graph.resize(graph.size() + 1);
- graph.back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
+ graph.back().aop.op.set_type(InstallOperation::MOVE);
// Reads from blocks 3, 5, 7
vector<Extent> extents;
AppendBlockToExtents(&extents, 3);
AppendBlockToExtents(&extents, 5);
AppendBlockToExtents(&extents, 7);
- StoreExtents(extents,
- graph.back().op.mutable_src_extents());
+ StoreExtents(extents, graph.back().aop.op.mutable_src_extents());
blocks[3].reader = graph.size() - 1;
blocks[5].reader = graph.size() - 1;
blocks[7].reader = graph.size() - 1;
@@ -161,22 +180,20 @@
AppendBlockToExtents(&extents, 1);
AppendBlockToExtents(&extents, 2);
AppendBlockToExtents(&extents, 4);
- StoreExtents(extents,
- graph.back().op.mutable_dst_extents());
+ StoreExtents(extents, graph.back().aop.op.mutable_dst_extents());
blocks[1].writer = graph.size() - 1;
blocks[2].writer = graph.size() - 1;
blocks[4].writer = graph.size() - 1;
}
{
graph.resize(graph.size() + 1);
- graph.back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
+ graph.back().aop.op.set_type(InstallOperation::MOVE);
// Reads from blocks 1, 2, 4
vector<Extent> extents;
AppendBlockToExtents(&extents, 1);
AppendBlockToExtents(&extents, 2);
AppendBlockToExtents(&extents, 4);
- StoreExtents(extents,
- graph.back().op.mutable_src_extents());
+ StoreExtents(extents, graph.back().aop.op.mutable_src_extents());
blocks[1].reader = graph.size() - 1;
blocks[2].reader = graph.size() - 1;
blocks[4].reader = graph.size() - 1;
@@ -186,8 +203,7 @@
AppendBlockToExtents(&extents, 3);
AppendBlockToExtents(&extents, 5);
AppendBlockToExtents(&extents, 6);
- StoreExtents(extents,
- graph.back().op.mutable_dst_extents());
+ StoreExtents(extents, graph.back().aop.op.mutable_dst_extents());
blocks[3].writer = graph.size() - 1;
blocks[5].writer = graph.size() - 1;
blocks[6].writer = graph.size() - 1;
@@ -211,27 +227,26 @@
EXPECT_EQ(3, graph.size());
// Check new node in graph:
- EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE,
- graph.back().op.type());
- EXPECT_EQ(2, graph.back().op.src_extents_size());
- EXPECT_EQ(1, graph.back().op.dst_extents_size());
- EXPECT_EQ(kTempBlockStart, graph.back().op.dst_extents(0).start_block());
- EXPECT_EQ(2, graph.back().op.dst_extents(0).num_blocks());
+ EXPECT_EQ(InstallOperation::MOVE, graph.back().aop.op.type());
+ EXPECT_EQ(2, graph.back().aop.op.src_extents_size());
+ EXPECT_EQ(1, graph.back().aop.op.dst_extents_size());
+ EXPECT_EQ(kTempBlockStart, graph.back().aop.op.dst_extents(0).start_block());
+ EXPECT_EQ(2, graph.back().aop.op.dst_extents(0).num_blocks());
EXPECT_TRUE(graph.back().out_edges.empty());
// Check that old node reads from new blocks
- EXPECT_EQ(2, graph[0].op.src_extents_size());
- EXPECT_EQ(kTempBlockStart, graph[0].op.src_extents(0).start_block());
- EXPECT_EQ(2, graph[0].op.src_extents(0).num_blocks());
- EXPECT_EQ(7, graph[0].op.src_extents(1).start_block());
- EXPECT_EQ(1, graph[0].op.src_extents(1).num_blocks());
+ EXPECT_EQ(2, graph[0].aop.op.src_extents_size());
+ EXPECT_EQ(kTempBlockStart, graph[0].aop.op.src_extents(0).start_block());
+ EXPECT_EQ(2, graph[0].aop.op.src_extents(0).num_blocks());
+ EXPECT_EQ(7, graph[0].aop.op.src_extents(1).start_block());
+ EXPECT_EQ(1, graph[0].aop.op.src_extents(1).num_blocks());
// And that the old dst extents haven't changed
- EXPECT_EQ(2, graph[0].op.dst_extents_size());
- EXPECT_EQ(1, graph[0].op.dst_extents(0).start_block());
- EXPECT_EQ(2, graph[0].op.dst_extents(0).num_blocks());
- EXPECT_EQ(4, graph[0].op.dst_extents(1).start_block());
- EXPECT_EQ(1, graph[0].op.dst_extents(1).num_blocks());
+ EXPECT_EQ(2, graph[0].aop.op.dst_extents_size());
+ EXPECT_EQ(1, graph[0].aop.op.dst_extents(0).start_block());
+ EXPECT_EQ(2, graph[0].aop.op.dst_extents(0).num_blocks());
+ EXPECT_EQ(4, graph[0].aop.op.dst_extents(1).start_block());
+ EXPECT_EQ(1, graph[0].aop.op.dst_extents(1).num_blocks());
// Ensure it only depends on the next node and the new temp node
EXPECT_EQ(2, graph[0].out_edges.size());
@@ -240,17 +255,17 @@
1));
// Check second node has unchanged extents
- EXPECT_EQ(2, graph[1].op.src_extents_size());
- EXPECT_EQ(1, graph[1].op.src_extents(0).start_block());
- EXPECT_EQ(2, graph[1].op.src_extents(0).num_blocks());
- EXPECT_EQ(4, graph[1].op.src_extents(1).start_block());
- EXPECT_EQ(1, graph[1].op.src_extents(1).num_blocks());
+ EXPECT_EQ(2, graph[1].aop.op.src_extents_size());
+ EXPECT_EQ(1, graph[1].aop.op.src_extents(0).start_block());
+ EXPECT_EQ(2, graph[1].aop.op.src_extents(0).num_blocks());
+ EXPECT_EQ(4, graph[1].aop.op.src_extents(1).start_block());
+ EXPECT_EQ(1, graph[1].aop.op.src_extents(1).num_blocks());
- EXPECT_EQ(2, graph[1].op.dst_extents_size());
- EXPECT_EQ(3, graph[1].op.dst_extents(0).start_block());
- EXPECT_EQ(1, graph[1].op.dst_extents(0).num_blocks());
- EXPECT_EQ(5, graph[1].op.dst_extents(1).start_block());
- EXPECT_EQ(2, graph[1].op.dst_extents(1).num_blocks());
+ EXPECT_EQ(2, graph[1].aop.op.dst_extents_size());
+ EXPECT_EQ(3, graph[1].aop.op.dst_extents(0).start_block());
+ EXPECT_EQ(1, graph[1].aop.op.dst_extents(0).num_blocks());
+ EXPECT_EQ(5, graph[1].aop.op.dst_extents(1).start_block());
+ EXPECT_EQ(2, graph[1].aop.op.dst_extents(1).num_blocks());
// Ensure it only depends on the next node
EXPECT_EQ(1, graph[1].out_edges.size());
@@ -268,9 +283,18 @@
cuts.resize(3);
// Simple broken loop:
- GenVertex(&graph[0], VectOfExt(0, 1), VectOfExt(1, 1), "", OP_MOVE);
- GenVertex(&graph[1], VectOfExt(tmp, 1), VectOfExt(0, 1), "", OP_MOVE);
- GenVertex(&graph[2], VectOfExt(1, 1), VectOfExt(tmp, 1), "", OP_MOVE);
+ GenVertex(
+ &graph[0], VectOfExt(0, 1), VectOfExt(1, 1), "", InstallOperation::MOVE);
+ GenVertex(&graph[1],
+ VectOfExt(tmp, 1),
+ VectOfExt(0, 1),
+ "",
+ InstallOperation::MOVE);
+ GenVertex(&graph[2],
+ VectOfExt(1, 1),
+ VectOfExt(tmp, 1),
+ "",
+ InstallOperation::MOVE);
// Corresponding edges:
graph[0].out_edges[2] = EdgeWithReadDep(VectOfExt(1, 1));
graph[1].out_edges[2] = EdgeWithWriteDep(VectOfExt(tmp, 1));
@@ -283,11 +307,25 @@
tmp++;
// Slightly more complex pair of loops:
- GenVertex(&graph[3], VectOfExt(4, 2), VectOfExt(2, 2), "", OP_MOVE);
- GenVertex(&graph[4], VectOfExt(6, 1), VectOfExt(7, 1), "", OP_MOVE);
- GenVertex(&graph[5], VectOfExt(tmp, 3), VectOfExt(4, 3), kFilename, OP_MOVE);
- GenVertex(&graph[6], VectOfExt(2, 2), VectOfExt(tmp, 2), "", OP_MOVE);
- GenVertex(&graph[7], VectOfExt(7, 1), VectOfExt(tmp + 2, 1), "", OP_MOVE);
+ GenVertex(
+ &graph[3], VectOfExt(4, 2), VectOfExt(2, 2), "", InstallOperation::MOVE);
+ GenVertex(
+ &graph[4], VectOfExt(6, 1), VectOfExt(7, 1), "", InstallOperation::MOVE);
+ GenVertex(&graph[5],
+ VectOfExt(tmp, 3),
+ VectOfExt(4, 3),
+ kFilename,
+ InstallOperation::MOVE);
+ GenVertex(&graph[6],
+ VectOfExt(2, 2),
+ VectOfExt(tmp, 2),
+ "",
+ InstallOperation::MOVE);
+ GenVertex(&graph[7],
+ VectOfExt(7, 1),
+ VectOfExt(tmp + 2, 1),
+ "",
+ InstallOperation::MOVE);
// Corresponding edges:
graph[3].out_edges[6] = EdgeWithReadDep(VectOfExt(2, 2));
graph[4].out_edges[7] = EdgeWithReadDep(VectOfExt(7, 1));
@@ -306,7 +344,7 @@
cuts[2].tmp_extents = VectOfExt(tmp + 2, 1);
// Supplier of temp block:
- GenVertex(&graph[8], empt, VectOfExt(8, 1), "", OP_REPLACE);
+ GenVertex(&graph[8], empt, VectOfExt(8, 1), "", InstallOperation::REPLACE);
// Specify the final order:
vector<Vertex::Index> op_indexes;
@@ -324,50 +362,43 @@
InplaceGenerator::GenerateReverseTopoOrderMap(op_indexes,
&reverse_op_indexes);
- int fd;
- EXPECT_TRUE(utils::MakeTempFile("AssignTempBlocksReuseTest.XXXXXX",
- nullptr,
- &fd));
- ScopedFdCloser fd_closer(&fd);
- off_t data_file_size = 0;
-
+ CreateBlobFile();
EXPECT_TRUE(InplaceGenerator::AssignTempBlocks(&graph,
"/dev/zero",
- fd,
- &data_file_size,
+ blob_file_.get(),
&op_indexes,
&reverse_op_indexes,
cuts));
EXPECT_FALSE(graph[6].valid);
EXPECT_FALSE(graph[7].valid);
- EXPECT_EQ(1, graph[1].op.src_extents_size());
- EXPECT_EQ(2, graph[1].op.src_extents(0).start_block());
- EXPECT_EQ(1, graph[1].op.src_extents(0).num_blocks());
- EXPECT_EQ(OP_REPLACE_BZ, graph[5].op.type());
+ EXPECT_EQ(1, graph[1].aop.op.src_extents_size());
+ EXPECT_EQ(2, graph[1].aop.op.src_extents(0).start_block());
+ EXPECT_EQ(1, graph[1].aop.op.src_extents(0).num_blocks());
+ EXPECT_EQ(InstallOperation::REPLACE_BZ, graph[5].aop.op.type());
}
-TEST_F(InplaceGeneratorTest, MoveFullOpsToBackTest) {
+TEST_F(InplaceGeneratorTest, MoveAndSortFullOpsToBackTest) {
Graph graph(4);
- graph[0].file_name = "A";
- graph[0].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
- graph[1].file_name = "B";
- graph[1].op.set_type(DeltaArchiveManifest_InstallOperation_Type_BSDIFF);
- graph[2].file_name = "C";
- graph[2].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
- graph[3].file_name = "D";
- graph[3].op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
+ graph[0].aop.name = "A";
+ graph[0].aop.op.set_type(InstallOperation::REPLACE);
+ graph[1].aop.name = "B";
+ graph[1].aop.op.set_type(InstallOperation::BSDIFF);
+ graph[2].aop.name = "C";
+ graph[2].aop.op.set_type(InstallOperation::REPLACE_BZ);
+ graph[3].aop.name = "D";
+ graph[3].aop.op.set_type(InstallOperation::MOVE);
vector<Vertex::Index> vect(graph.size());
for (vector<Vertex::Index>::size_type i = 0; i < vect.size(); ++i) {
vect[i] = i;
}
- InplaceGenerator::MoveFullOpsToBack(&graph, &vect);
+ InplaceGenerator::MoveAndSortFullOpsToBack(&graph, &vect);
EXPECT_EQ(vect.size(), graph.size());
- EXPECT_EQ(graph[vect[0]].file_name, "B");
- EXPECT_EQ(graph[vect[1]].file_name, "D");
- EXPECT_EQ(graph[vect[2]].file_name, "A");
- EXPECT_EQ(graph[vect[3]].file_name, "C");
+ EXPECT_EQ(graph[vect[0]].aop.name, "B");
+ EXPECT_EQ(graph[vect[1]].aop.name, "D");
+ EXPECT_EQ(graph[vect[2]].aop.name, "A");
+ EXPECT_EQ(graph[vect[3]].aop.name, "C");
}
TEST_F(InplaceGeneratorTest, AssignTempBlocksTest) {
@@ -376,20 +407,36 @@
const string kFilename = "/foo";
// Some scratch space:
- GenVertex(&graph[0], empt, VectOfExt(200, 1), "", OP_REPLACE);
- GenVertex(&graph[1], empt, VectOfExt(210, 10), "", OP_REPLACE);
- GenVertex(&graph[2], empt, VectOfExt(220, 1), "", OP_REPLACE);
+ GenVertex(&graph[0], empt, VectOfExt(200, 1), "", InstallOperation::REPLACE);
+ GenVertex(&graph[1], empt, VectOfExt(210, 10), "", InstallOperation::REPLACE);
+ GenVertex(&graph[2], empt, VectOfExt(220, 1), "", InstallOperation::REPLACE);
// A cycle that requires 10 blocks to break:
- GenVertex(&graph[3], VectOfExt(10, 11), VectOfExt(0, 9), "", OP_BSDIFF);
+ GenVertex(&graph[3],
+ VectOfExt(10, 11),
+ VectOfExt(0, 9),
+ "",
+ InstallOperation::BSDIFF);
graph[3].out_edges[4] = EdgeWithReadDep(VectOfExt(0, 9));
- GenVertex(&graph[4], VectOfExt(0, 9), VectOfExt(10, 11), "", OP_BSDIFF);
+ GenVertex(&graph[4],
+ VectOfExt(0, 9),
+ VectOfExt(10, 11),
+ "",
+ InstallOperation::BSDIFF);
graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11));
// A cycle that requires 9 blocks to break:
- GenVertex(&graph[5], VectOfExt(40, 11), VectOfExt(30, 10), "", OP_BSDIFF);
+ GenVertex(&graph[5],
+ VectOfExt(40, 11),
+ VectOfExt(30, 10),
+ "",
+ InstallOperation::BSDIFF);
graph[5].out_edges[6] = EdgeWithReadDep(VectOfExt(30, 10));
- GenVertex(&graph[6], VectOfExt(30, 10), VectOfExt(40, 11), "", OP_BSDIFF);
+ GenVertex(&graph[6],
+ VectOfExt(30, 10),
+ VectOfExt(40, 11),
+ "",
+ InstallOperation::BSDIFF);
graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11));
// A cycle that requires 40 blocks to break (which is too many):
@@ -397,62 +444,67 @@
VectOfExt(120, 50),
VectOfExt(60, 40),
"",
- OP_BSDIFF);
+ InstallOperation::BSDIFF);
graph[7].out_edges[8] = EdgeWithReadDep(VectOfExt(60, 40));
GenVertex(&graph[8],
VectOfExt(60, 40),
VectOfExt(120, 50),
kFilename,
- OP_BSDIFF);
+ InstallOperation::BSDIFF);
graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50));
graph_utils::DumpGraph(graph);
vector<Vertex::Index> final_order;
- int fd;
- EXPECT_TRUE(utils::MakeTempFile("AssignTempBlocksTestData.XXXXXX",
- nullptr,
- &fd));
- ScopedFdCloser fd_closer(&fd);
- off_t data_file_size = 0;
-
+ CreateBlobFile();
EXPECT_TRUE(InplaceGenerator::ConvertGraphToDag(&graph,
"/dev/zero",
- fd,
- &data_file_size,
+ blob_file_.get(),
&final_order,
Vertex::kInvalidIndex));
Graph expected_graph(12);
- GenVertex(&expected_graph[0], empt, VectOfExt(200, 1), "", OP_REPLACE);
- GenVertex(&expected_graph[1], empt, VectOfExt(210, 10), "", OP_REPLACE);
- GenVertex(&expected_graph[2], empt, VectOfExt(220, 1), "", OP_REPLACE);
+ GenVertex(&expected_graph[0],
+ empt,
+ VectOfExt(200, 1),
+ "",
+ InstallOperation::REPLACE);
+ GenVertex(&expected_graph[1],
+ empt,
+ VectOfExt(210, 10),
+ "",
+ InstallOperation::REPLACE);
+ GenVertex(&expected_graph[2],
+ empt,
+ VectOfExt(220, 1),
+ "",
+ InstallOperation::REPLACE);
GenVertex(&expected_graph[3],
VectOfExt(10, 11),
VectOfExt(0, 9),
"",
- OP_BSDIFF);
+ InstallOperation::BSDIFF);
expected_graph[3].out_edges[9] = EdgeWithReadDep(VectOfExt(0, 9));
GenVertex(&expected_graph[4],
VectOfExt(60, 9),
VectOfExt(10, 11),
"",
- OP_BSDIFF);
+ InstallOperation::BSDIFF);
expected_graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11));
expected_graph[4].out_edges[9] = EdgeWithWriteDep(VectOfExt(60, 9));
GenVertex(&expected_graph[5],
VectOfExt(40, 11),
VectOfExt(30, 10),
"",
- OP_BSDIFF);
+ InstallOperation::BSDIFF);
expected_graph[5].out_edges[10] = EdgeWithReadDep(VectOfExt(30, 10));
GenVertex(&expected_graph[6],
VectOfExt(60, 10),
VectOfExt(40, 11),
"",
- OP_BSDIFF);
+ InstallOperation::BSDIFF);
expected_graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11));
expected_graph[6].out_edges[10] = EdgeWithWriteDep(VectOfExt(60, 10));
@@ -460,23 +512,27 @@
VectOfExt(120, 50),
VectOfExt(60, 40),
"",
- OP_BSDIFF);
+ InstallOperation::BSDIFF);
expected_graph[7].out_edges[6] = EdgeWithReadDep(VectOfExt(60, 10));
- GenVertex(&expected_graph[8], empt, VectOfExt(0, 50), "/foo", OP_REPLACE_BZ);
+ GenVertex(&expected_graph[8],
+ empt,
+ VectOfExt(0, 50),
+ "/foo",
+ InstallOperation::REPLACE_BZ);
expected_graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50));
GenVertex(&expected_graph[9],
VectOfExt(0, 9),
VectOfExt(60, 9),
"",
- OP_MOVE);
+ InstallOperation::MOVE);
GenVertex(&expected_graph[10],
VectOfExt(30, 10),
VectOfExt(60, 10),
"",
- OP_MOVE);
+ InstallOperation::MOVE);
expected_graph[10].out_edges[4] = EdgeWithReadDep(VectOfExt(60, 9));
EXPECT_EQ(12, graph.size());
@@ -494,13 +550,12 @@
TEST_F(InplaceGeneratorTest, CreateScratchNodeTest) {
Vertex vertex;
InplaceGenerator::CreateScratchNode(12, 34, &vertex);
- EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ,
- vertex.op.type());
- EXPECT_EQ(0, vertex.op.data_offset());
- EXPECT_EQ(0, vertex.op.data_length());
- EXPECT_EQ(1, vertex.op.dst_extents_size());
- EXPECT_EQ(12, vertex.op.dst_extents(0).start_block());
- EXPECT_EQ(34, vertex.op.dst_extents(0).num_blocks());
+ EXPECT_EQ(InstallOperation::REPLACE_BZ, vertex.aop.op.type());
+ EXPECT_EQ(0, vertex.aop.op.data_offset());
+ EXPECT_EQ(0, vertex.aop.op.data_length());
+ EXPECT_EQ(1, vertex.aop.op.dst_extents_size());
+ EXPECT_EQ(12, vertex.aop.op.dst_extents(0).start_block());
+ EXPECT_EQ(34, vertex.aop.op.dst_extents(0).num_blocks());
}
TEST_F(InplaceGeneratorTest, ApplyMapTest) {
@@ -515,4 +570,77 @@
EXPECT_EQ(expected_values, collection);
}
+// We can't produce MOVE operations with a source or destination in the block 0.
+// This test checks that the cycle breaker procedure doesn't produce such
+// operations.
+TEST_F(InplaceGeneratorTest, ResolveReadAfterWriteDependenciesAvoidMoveToZero) {
+ size_t block_size = 4096;
+ size_t num_blocks = 4;
+ vector<AnnotatedOperation> aops;
+
+ // Create a REPLACE_BZ for block 0, and a circular dependency among all other
+ // blocks. This situation would prefer to issue a MOVE to scratch space and
+ // the only available block is 0.
+ aops.emplace_back();
+ aops.back().name = base::StringPrintf("<bz-block-0>");
+ aops.back().op.set_type(InstallOperation::REPLACE_BZ);
+ StoreExtents({ExtentForRange(0, 1)}, aops.back().op.mutable_dst_extents());
+
+ for (size_t i = 1; i < num_blocks; i++) {
+ AnnotatedOperation aop;
+ aop.name = base::StringPrintf("<op-%" PRIuS ">", i);
+ aop.op.set_type(InstallOperation::BSDIFF);
+ StoreExtents({ExtentForRange(1 + i % (num_blocks - 1), 1)},
+ aop.op.mutable_src_extents());
+ StoreExtents({ExtentForRange(i, 1)}, aop.op.mutable_dst_extents());
+ aops.push_back(aop);
+ }
+
+ PartitionConfig part(PartitionName::kRootfs);
+ part.path = "/dev/zero";
+ part.size = num_blocks * block_size;
+
+ CreateBlobFile();
+
+ // We ran two tests here. The first one without enough blocks for the scratch
+ // space, forcing it to create a new full operation and the second case with
+ // one extra block in the partition that can be used for the move operation.
+ for (const auto part_blocks : vector<uint64_t>{num_blocks, num_blocks + 1}) {
+ SCOPED_TRACE(base::StringPrintf("Using partition_blocs=%" PRIu64,
+ part_blocks));
+ vector<AnnotatedOperation> result_aops = aops;
+ EXPECT_TRUE(InplaceGenerator::ResolveReadAfterWriteDependencies(
+ part, part_blocks * block_size, block_size, blob_file_.get(),
+ &result_aops));
+
+ size_t full_ops = 0;
+ for (const auto& aop : result_aops) {
+ if (aop.op.type() == InstallOperation::REPLACE ||
+ aop.op.type() == InstallOperation::REPLACE_BZ) {
+ full_ops++;
+ }
+
+ if (aop.op.type() != InstallOperation::MOVE)
+ continue;
+ for (const Extent& extent : aop.op.src_extents()) {
+ EXPECT_NE(0, extent.start_block()) << "On src extents for aop: " << aop;
+ }
+ for (const Extent& extent : aop.op.dst_extents()) {
+ EXPECT_NE(0, extent.start_block()) << "On dst extents for aop: " << aop;
+ }
+ }
+
+ // If there's extra space in the partition, it should not use a new full
+ // operation for it.
+ EXPECT_EQ(part_blocks == num_blocks ? 2 : 1, full_ops);
+
+ if (HasNonfatalFailure()) {
+ LOG(INFO) << "Result operation list:";
+ for (const auto& aop : result_aops) {
+ LOG(INFO) << aop;
+ }
+ }
+ }
+}
+
} // namespace chromeos_update_engine
diff --git a/payload_generator/operations_generator.h b/payload_generator/operations_generator.h
index 31d57b3..677f766 100644
--- a/payload_generator/operations_generator.h
+++ b/payload_generator/operations_generator.h
@@ -10,6 +10,7 @@
#include <base/macros.h>
#include "update_engine/payload_generator/annotated_operation.h"
+#include "update_engine/payload_generator/blob_file_writer.h"
#include "update_engine/payload_generator/payload_generation_config.h"
namespace chromeos_update_engine {
@@ -31,8 +32,7 @@
// |data_file_size|.
virtual bool GenerateOperations(
const PayloadGenerationConfig& config,
- int data_file_fd,
- off_t* data_file_size,
+ BlobFileWriter* blob_file,
std::vector<AnnotatedOperation>* rootfs_ops,
std::vector<AnnotatedOperation>* kernel_ops) = 0;
diff --git a/payload_generator/payload_file.cc b/payload_generator/payload_file.cc
index fe453a2..92fe724 100644
--- a/payload_generator/payload_file.cc
+++ b/payload_generator/payload_file.cc
@@ -128,11 +128,10 @@
{
for (int i = 0; i < (manifest_.install_operations_size() +
manifest_.kernel_install_operations_size()); i++) {
- DeltaArchiveManifest_InstallOperation* op =
- i < manifest_.install_operations_size() ?
- manifest_.mutable_install_operations(i) :
- manifest_.mutable_kernel_install_operations(
- i - manifest_.install_operations_size());
+ InstallOperation* op = i < manifest_.install_operations_size()
+ ? manifest_.mutable_install_operations(i)
+ : manifest_.mutable_kernel_install_operations(
+ i - manifest_.install_operations_size());
if (op->has_data_offset()) {
if (op->data_offset() != next_blob_offset) {
LOG(FATAL) << "bad blob offset! " << op->data_offset() << " != "
@@ -249,9 +248,8 @@
return true;
}
-bool PayloadFile::AddOperationHash(
- DeltaArchiveManifest_InstallOperation* op,
- const chromeos::Blob& buf) {
+bool PayloadFile::AddOperationHash(InstallOperation* op,
+ const chromeos::Blob& buf) {
OmahaHashCalculator hasher;
TEST_AND_RETURN_FALSE(hasher.Update(buf.data(), buf.size()));
TEST_AND_RETURN_FALSE(hasher.Finalize());
@@ -302,9 +300,8 @@
manifest->set_signatures_offset(signature_blob_offset);
LOG(INFO) << "set? " << manifest->has_signatures_offset();
// Add a dummy op at the end to appease older clients
- DeltaArchiveManifest_InstallOperation* dummy_op =
- manifest->add_kernel_install_operations();
- dummy_op->set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
+ InstallOperation* dummy_op = manifest->add_kernel_install_operations();
+ dummy_op->set_type(InstallOperation::REPLACE);
dummy_op->set_data_offset(signature_blob_offset);
manifest->set_signatures_offset(signature_blob_offset);
dummy_op->set_data_length(signature_blob_length);
diff --git a/payload_generator/payload_file.h b/payload_generator/payload_file.h
index 150b0b0..a8f815b 100644
--- a/payload_generator/payload_file.h
+++ b/payload_generator/payload_file.h
@@ -50,8 +50,7 @@
// dummy operation for signature blob because the contents of the signature
// blob will not be available at payload creation time. So, update_engine will
// gracefully ignore the dummy signature operation.
- static bool AddOperationHash(DeltaArchiveManifest_InstallOperation* op,
- const chromeos::Blob& buf);
+ static bool AddOperationHash(InstallOperation* op, const chromeos::Blob& buf);
// Install operations in the manifest may reference data blobs, which
// are in data_blobs_path. This function creates a new data blobs file
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
index d58c9a1..cfe1367 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -15,6 +15,16 @@
namespace chromeos_update_engine {
+std::string PartitionNameString(PartitionName name) {
+ switch (name) {
+ case PartitionName::kKernel:
+ return "kernel";
+ case PartitionName::kRootfs:
+ return "rootfs";
+ }
+ return "other";
+}
+
bool PartitionConfig::ValidateExists() const {
TEST_AND_RETURN_FALSE(!path.empty());
TEST_AND_RETURN_FALSE(utils::FileExists(path.c_str()));
@@ -36,17 +46,8 @@
if (!fs_interface) {
// Fall back to a RAW filesystem.
TEST_AND_RETURN_FALSE(size % kBlockSize == 0);
- std::string str_name = "other";
- switch (name) {
- case PartitionName::kKernel:
- str_name = "kernel";
- break;
- case PartitionName::kRootfs:
- str_name = "rootfs";
- break;
- }
fs_interface = RawFilesystem::Create(
- "<" + str_name + "-partition>",
+ "<" + PartitionNameString(name) + "-partition>",
kBlockSize,
size / kBlockSize);
}
@@ -141,7 +142,9 @@
TEST_AND_RETURN_FALSE(target.rootfs.size % block_size == 0);
TEST_AND_RETURN_FALSE(target.kernel.size % block_size == 0);
- TEST_AND_RETURN_FALSE(chunk_size == -1 || chunk_size % block_size == 0);
+ TEST_AND_RETURN_FALSE(hard_chunk_size == -1 ||
+ hard_chunk_size % block_size == 0);
+ TEST_AND_RETURN_FALSE(soft_chunk_size % block_size == 0);
TEST_AND_RETURN_FALSE(rootfs_partition_size % block_size == 0);
TEST_AND_RETURN_FALSE(rootfs_partition_size >= target.rootfs.size);
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
index f18055f..1846e5b 100644
--- a/payload_generator/payload_generation_config.h
+++ b/payload_generator/payload_generation_config.h
@@ -22,6 +22,9 @@
kRootfs,
};
+// Return a string name for the PartitionName.
+std::string PartitionNameString(PartitionName name);
+
struct PartitionConfig {
explicit PartitionConfig(PartitionName name) : name(name) {}
@@ -110,10 +113,20 @@
// after the partition used to store the verity hashes and or the bootcache.
uint64_t rootfs_partition_size = 0;
- // The chunk size is the maximum size that a single operation should write in
- // the destination. Operations bigger than chunk_size should be split. A value
- // of -1 means no chunk_size limit.
- off_t chunk_size = -1;
+ // The |hard_chunk_size| is the maximum size that a single operation should
+ // write in the destination. Operations bigger than chunk_size should be
+ // split. A value of -1 means no hard chunk size limit. A very low limit
+ // means more operations, and less of a chance to reuse the data.
+ ssize_t hard_chunk_size = -1;
+
+ // The |soft_chunk_size| is the preferred chunk size to use when there's no
+ // significant impact to the operations. For example, REPLACE, MOVE and
+ // SOURCE_COPY operations are not significantly impacted by the chunk size,
+ // except for a few bytes overhead in the manifest to describe extra
+ // operations. On the other hand, splitting BSDIFF operations impacts the
+ // payload size since it is not possible to use the redundancy *between*
+ // chunks.
+ size_t soft_chunk_size = 2 * 1024 * 1024;
// TODO(deymo): Remove the block_size member and maybe replace it with a
// minimum alignment size for blocks (if needed). Algorithms should be able to
diff --git a/payload_state.cc b/payload_state.cc
index ed7b775..aa4c73c 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -10,7 +10,6 @@
#include <base/logging.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <base/format_macros.h>
#include <policy/device_policy.h>
#include "update_engine/clock.h"
@@ -19,7 +18,6 @@
#include "update_engine/install_plan.h"
#include "update_engine/omaha_request_params.h"
#include "update_engine/prefs.h"
-#include "update_engine/real_dbus_wrapper.h"
#include "update_engine/system_state.h"
#include "update_engine/utils.h"
@@ -175,10 +173,9 @@
metrics::ConnectionType type;
NetworkConnectionType network_connection_type;
NetworkTethering tethering;
- RealDBusWrapper dbus_iface;
- ConnectionManager* connection_manager = system_state_->connection_manager();
- if (!connection_manager->GetConnectionProperties(&dbus_iface,
- &network_connection_type,
+ ConnectionManagerInterface* connection_manager =
+ system_state_->connection_manager();
+ if (!connection_manager->GetConnectionProperties(&network_connection_type,
&tethering)) {
LOG(ERROR) << "Failed to determine connection type.";
type = metrics::ConnectionType::kUnknown;
diff --git a/payload_state_unittest.cc b/payload_state_unittest.cc
index 08ed398..c4e66ac 100644
--- a/payload_state_unittest.cc
+++ b/payload_state_unittest.cc
@@ -4,8 +4,6 @@
#include "update_engine/payload_state.h"
-#include <glib.h>
-
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/strings/stringprintf.h>
diff --git a/postinstall_runner_action.cc b/postinstall_runner_action.cc
index f27aace..0760bd5 100644
--- a/postinstall_runner_action.cc
+++ b/postinstall_runner_action.cc
@@ -8,6 +8,8 @@
#include <sys/mount.h>
#include <vector>
+#include <base/bind.h>
+
#include "update_engine/action_processor.h"
#include "update_engine/subprocess.h"
#include "update_engine/utils.h"
@@ -83,12 +85,16 @@
command.push_back(kPostinstallScript);
}
command.push_back(install_device);
- if (!Subprocess::Get().Exec(command, StaticCompletePostinstall, this)) {
- CompletePostinstall(1);
+ if (!Subprocess::Get().Exec(command,
+ base::Bind(
+ &PostinstallRunnerAction::CompletePostinstall,
+ base::Unretained(this)))) {
+ CompletePostinstall(1, "Postinstall didn't launch");
}
}
-void PostinstallRunnerAction::CompletePostinstall(int return_code) {
+void PostinstallRunnerAction::CompletePostinstall(int return_code,
+ const string& output) {
ScopedActionCompleter completer(processor_, this);
ScopedTempUnmounter temp_unmounter(temp_rootfs_dir_);
if (return_code != 0) {
@@ -123,11 +129,4 @@
completer.set_code(ErrorCode::kSuccess);
}
-void PostinstallRunnerAction::StaticCompletePostinstall(int return_code,
- const string& output,
- void* p) {
- reinterpret_cast<PostinstallRunnerAction*>(p)->CompletePostinstall(
- return_code);
-}
-
} // namespace chromeos_update_engine
diff --git a/postinstall_runner_action.h b/postinstall_runner_action.h
index e7ac1c5..18a3691 100644
--- a/postinstall_runner_action.h
+++ b/postinstall_runner_action.h
@@ -32,10 +32,8 @@
private:
// Subprocess::Exec callback.
- void CompletePostinstall(int return_code);
- static void StaticCompletePostinstall(int return_code,
- const std::string& output,
- void* p);
+ void CompletePostinstall(int return_code,
+ const std::string& output);
InstallPlan install_plan_;
std::string temp_rootfs_dir_;
diff --git a/postinstall_runner_action_unittest.cc b/postinstall_runner_action_unittest.cc
index 4549272..056ae21 100644
--- a/postinstall_runner_action_unittest.cc
+++ b/postinstall_runner_action_unittest.cc
@@ -13,10 +13,11 @@
#include <vector>
#include <base/files/file_util.h>
+#include <base/message_loop/message_loop.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <chromeos/bind_lambda.h>
-#include <chromeos/message_loops/glib_message_loop.h>
+#include <chromeos/message_loops/base_message_loop.h>
#include <chromeos/message_loops/message_loop_utils.h>
#include <gtest/gtest.h>
@@ -37,10 +38,8 @@
protected:
void SetUp() override {
loop_.SetAsCurrent();
- }
-
- void TearDown() override {
- EXPECT_EQ(0, chromeos::MessageLoopRunMaxIterations(&loop_, 1));
+ async_signal_handler_.Init();
+ subprocess_.Init(&async_signal_handler_);
}
// DoTest with various combinations of do_losetup, err_code and
@@ -50,7 +49,10 @@
private:
static const char* kImageMountPointTemplate;
- chromeos::GlibMessageLoop loop_;
+ base::MessageLoopForIO base_loop_;
+ chromeos::BaseMessageLoop loop_{&base_loop_};
+ chromeos::AsynchronousSignalHandler async_signal_handler_;
+ Subprocess subprocess_;
};
class PostinstActionProcessorDelegate : public ActionProcessorDelegate {
diff --git a/real_dbus_wrapper.h b/real_dbus_wrapper.h
deleted file mode 100644
index 0c6847b..0000000
--- a/real_dbus_wrapper.h
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UPDATE_ENGINE_REAL_DBUS_WRAPPER_H_
-#define UPDATE_ENGINE_REAL_DBUS_WRAPPER_H_
-
-// A mockable interface for DBus.
-
-#include <base/macros.h>
-#include <dbus/dbus-glib.h>
-#include <dbus/dbus-glib-lowlevel.h>
-
-#include "update_engine/dbus_wrapper_interface.h"
-
-namespace chromeos_update_engine {
-
-class RealDBusWrapper : public DBusWrapperInterface {
- DBusGProxy* ProxyNewForName(DBusGConnection* connection,
- const char* name,
- const char* path,
- const char* interface) override {
- return dbus_g_proxy_new_for_name(connection,
- name,
- path,
- interface);
- }
-
- void ProxyUnref(DBusGProxy* proxy) override {
- g_object_unref(proxy);
- }
-
- DBusGConnection* BusGet(DBusBusType type, GError** error) override {
- return dbus_g_bus_get(type, error);
- }
-
- gboolean ProxyCall_0_1(DBusGProxy* proxy,
- const char* method,
- GError** error,
- GHashTable** out1) override {
- return dbus_g_proxy_call(proxy, method, error, G_TYPE_INVALID,
- dbus_g_type_get_map("GHashTable",
- G_TYPE_STRING,
- G_TYPE_VALUE),
- out1, G_TYPE_INVALID);
- }
-
- gboolean ProxyCall_0_1(DBusGProxy* proxy,
- const char* method,
- GError** error,
- gint* out1) override {
- return dbus_g_proxy_call(proxy, method, error, G_TYPE_INVALID,
- G_TYPE_INT, out1, G_TYPE_INVALID);
- }
-
- gboolean ProxyCall_1_0(DBusGProxy* proxy,
- const char* method,
- GError** error,
- gint in1) override {
- return dbus_g_proxy_call(proxy, method, error,
- G_TYPE_INT, in1,
- G_TYPE_INVALID, G_TYPE_INVALID);
- }
-
- gboolean ProxyCall_3_0(DBusGProxy* proxy,
- const char* method,
- GError** error,
- const char* in1,
- const char* in2,
- const char* in3) override {
- return dbus_g_proxy_call(
- proxy, method, error,
- G_TYPE_STRING, in1, G_TYPE_STRING, in2, G_TYPE_STRING, in3,
- G_TYPE_INVALID, G_TYPE_INVALID);
- }
-
- void ProxyAddSignal_1(DBusGProxy* proxy,
- const char* signal_name,
- GType type1) override {
- dbus_g_object_register_marshaller(
- g_cclosure_marshal_generic, G_TYPE_NONE, type1, G_TYPE_INVALID);
- dbus_g_proxy_add_signal(proxy, signal_name, type1, G_TYPE_INVALID);
- }
-
- void ProxyAddSignal_2(DBusGProxy* proxy,
- const char* signal_name,
- GType type1,
- GType type2) override {
- dbus_g_object_register_marshaller(
- g_cclosure_marshal_generic, G_TYPE_NONE, type1, type2, G_TYPE_INVALID);
- dbus_g_proxy_add_signal(proxy, signal_name, type1, type2, G_TYPE_INVALID);
- }
-
- void ProxyConnectSignal(DBusGProxy* proxy,
- const char* signal_name,
- GCallback handler,
- void* data,
- GClosureNotify free_data_func) override {
- dbus_g_proxy_connect_signal(proxy, signal_name, handler, data,
- free_data_func);
- }
-
- void ProxyDisconnectSignal(DBusGProxy* proxy,
- const char* signal_name,
- GCallback handler,
- void* data) override {
- dbus_g_proxy_disconnect_signal(proxy, signal_name, handler, data);
- }
-
- DBusConnection* ConnectionGetConnection(DBusGConnection* gbus) override {
- return dbus_g_connection_get_connection(gbus);
- }
-
- void DBusBusAddMatch(DBusConnection* connection,
- const char* rule,
- DBusError* error) override {
- dbus_bus_add_match(connection, rule, error);
- }
-
- dbus_bool_t DBusConnectionAddFilter(
- DBusConnection* connection,
- DBusHandleMessageFunction function,
- void* user_data,
- DBusFreeFunction free_data_function) override {
- return dbus_connection_add_filter(connection,
- function,
- user_data,
- free_data_function);
- }
-
- void DBusConnectionRemoveFilter(DBusConnection* connection,
- DBusHandleMessageFunction function,
- void* user_data) override {
- dbus_connection_remove_filter(connection, function, user_data);
- }
-
- dbus_bool_t DBusMessageIsSignal(DBusMessage* message,
- const char* interface,
- const char* signal_name) override {
- return dbus_message_is_signal(message, interface, signal_name);
- }
-
- dbus_bool_t DBusMessageGetArgs_3(DBusMessage* message,
- DBusError* error,
- char** out1,
- char** out2,
- char** out3) override {
- return dbus_message_get_args(message, error,
- DBUS_TYPE_STRING, out1,
- DBUS_TYPE_STRING, out2,
- DBUS_TYPE_STRING, out3,
- G_TYPE_INVALID);
- }
-};
-
-} // namespace chromeos_update_engine
-
-#endif // UPDATE_ENGINE_REAL_DBUS_WRAPPER_H_
diff --git a/real_system_state.cc b/real_system_state.cc
index f16f2f8..861a702 100644
--- a/real_system_state.cc
+++ b/real_system_state.cc
@@ -6,6 +6,7 @@
#include <base/files/file_util.h>
#include <base/time/time.h>
+#include <chromeos/dbus/service_constants.h>
#include "update_engine/constants.h"
#include "update_engine/update_manager/state_factory.h"
@@ -13,16 +14,22 @@
namespace chromeos_update_engine {
-RealSystemState::RealSystemState()
- : device_policy_(nullptr),
- connection_manager_(this),
- update_attempter_(this, &dbus_),
- request_params_(this),
- system_rebooted_(false) {}
+RealSystemState::RealSystemState(const scoped_refptr<dbus::Bus>& bus)
+ : debugd_proxy_(bus, debugd::kDebugdServiceName),
+ power_manager_proxy_(bus, power_manager::kPowerManagerServiceName),
+ session_manager_proxy_(bus, login_manager::kSessionManagerServiceName),
+ shill_proxy_(bus),
+ libcros_proxy_(bus) {
+}
bool RealSystemState::Initialize() {
metrics_lib_.Init();
+ if (!shill_proxy_.Init()) {
+ LOG(ERROR) << "Failed to initialize shill proxy.";
+ return false;
+ }
+
if (!prefs_.Init(base::FilePath(kPrefsDirectory))) {
LOG(ERROR) << "Failed to initialize preferences.";
return false;
@@ -44,7 +51,7 @@
// Initialize the Update Manager using the default state factory.
chromeos_update_manager::State* um_state =
chromeos_update_manager::DefaultStateFactory(
- &policy_provider_, &dbus_, this);
+ &policy_provider_, &shill_proxy_, &session_manager_proxy_, this);
if (!um_state) {
LOG(ERROR) << "Failed to initialize the Update Manager.";
return false;
diff --git a/real_system_state.h b/real_system_state.h
index 7e0c16d..be7778e 100644
--- a/real_system_state.h
+++ b/real_system_state.h
@@ -14,11 +14,12 @@
#include "update_engine/clock.h"
#include "update_engine/connection_manager.h"
+#include "update_engine/dbus_proxies.h"
#include "update_engine/hardware.h"
#include "update_engine/p2p_manager.h"
#include "update_engine/payload_state.h"
#include "update_engine/prefs.h"
-#include "update_engine/real_dbus_wrapper.h"
+#include "update_engine/shill_proxy.h"
#include "update_engine/update_attempter.h"
#include "update_engine/update_manager/update_manager.h"
@@ -30,7 +31,7 @@
public:
// Constructs all system objects that do not require separate initialization;
// see Initialize() below for the remaining ones.
- RealSystemState();
+ explicit RealSystemState(const scoped_refptr<dbus::Bus>& bus);
// Initializes and sets systems objects that require an initialization
// separately from construction. Returns |true| on success.
@@ -47,7 +48,7 @@
inline ClockInterface* clock() override { return &clock_; }
- inline ConnectionManager* connection_manager() override {
+ inline ConnectionManagerInterface* connection_manager() override {
return &connection_manager_;
}
@@ -81,18 +82,30 @@
return update_manager_.get();
}
+ inline org::chromium::PowerManagerProxyInterface* power_manager_proxy()
+ override {
+ return &power_manager_proxy_;
+ }
+
inline bool system_rebooted() override { return system_rebooted_; }
private:
+ // Real DBus proxies using the DBus connection.
+ org::chromium::debugdProxy debugd_proxy_;
+ org::chromium::PowerManagerProxy power_manager_proxy_;
+ org::chromium::SessionManagerInterfaceProxy session_manager_proxy_;
+ ShillProxy shill_proxy_;
+ LibCrosProxy libcros_proxy_;
+
// Interface for the clock.
Clock clock_;
// The latest device policy object from the policy provider.
- const policy::DevicePolicy* device_policy_;
+ const policy::DevicePolicy* device_policy_{nullptr};
- // The connection manager object that makes download
- // decisions depending on the current type of connection.
- ConnectionManager connection_manager_;
+ // The connection manager object that makes download decisions depending on
+ // the current type of connection.
+ ConnectionManager connection_manager_{&shill_proxy_, this};
// Interface for the hardware functions.
Hardware hardware_;
@@ -106,18 +119,15 @@
// Interface for persisted store that persists across powerwashes.
Prefs powerwash_safe_prefs_;
- // All state pertaining to payload state such as
- // response, URL, backoff states.
+ // All state pertaining to payload state such as response, URL, backoff
+ // states.
PayloadState payload_state_;
- // The dbus object used to initialize the update attempter.
- RealDBusWrapper dbus_;
-
// Pointer to the update attempter object.
- UpdateAttempter update_attempter_;
+ UpdateAttempter update_attempter_{this, &libcros_proxy_, &debugd_proxy_};
// Common parameters for all Omaha requests.
- OmahaRequestParams request_params_;
+ OmahaRequestParams request_params_{this};
std::unique_ptr<P2PManager> p2p_manager_;
@@ -128,7 +138,7 @@
// If true, this is the first instance of the update engine since the system
// rebooted. Important for tracking whether you are running instance of the
// update engine on first boot or due to a crash/restart.
- bool system_rebooted_;
+ bool system_rebooted_{false};
};
} // namespace chromeos_update_engine
diff --git a/sample_images/disk_ext2_1k.txt b/sample_images/disk_ext2_1k.txt
deleted file mode 100644
index 1494535..0000000
--- a/sample_images/disk_ext2_1k.txt
+++ /dev/null
@@ -1 +0,0 @@
-default 16777216 1024
diff --git a/sample_images/disk_ext2_4k.txt b/sample_images/disk_ext2_4k.txt
deleted file mode 100644
index cac718f..0000000
--- a/sample_images/disk_ext2_4k.txt
+++ /dev/null
@@ -1 +0,0 @@
-default 16777216 4096
diff --git a/sample_images/disk_ext2_ue_settings.txt b/sample_images/disk_ext2_ue_settings.txt
deleted file mode 100644
index 554f7da..0000000
--- a/sample_images/disk_ext2_ue_settings.txt
+++ /dev/null
@@ -1 +0,0 @@
-ue_settings 16777216 4096
diff --git a/sample_images/generate_image.sh b/sample_images/generate_images.sh
similarity index 83%
rename from sample_images/generate_image.sh
rename to sample_images/generate_images.sh
index c4f132a..d8d92f5 100755
--- a/sample_images/generate_image.sh
+++ b/sample_images/generate_images.sh
@@ -4,6 +4,11 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+# This script generates some sample images used in unittests and packages them
+# in the sample_images.tar.bz2 file. The list of generated images and their
+# options are described in the main() function. You need to manually run this
+# script to update the generated images whenever you modify this script.
+
set -e
# cleanup <path>
@@ -145,14 +150,28 @@
trap - INT TERM EXIT
}
-image_desc="${1:-}"
-output_dir="${2:-}"
+OUTPUT_DIR=$(dirname "$0")
+IMAGES=()
-if [[ ! -e "${image_desc}" || ! -d "${output_dir}" ]]; then
- echo "Use: $0 <image_description.txt> <output_dir>" >&2
- exit 1
-fi
+# generate_image <image_name> [<image args> ...]
+generate_image() {
+ echo "Generating image $1.img"
+ IMAGES+=( "$1.img" )
+ generate_fs "${OUTPUT_DIR}/$1.img" "${@:2}"
+}
-args=( $(cat ${image_desc}) )
-dest_image="${output_dir}/$(basename ${image_desc} .txt).img"
-generate_fs "${dest_image}" "${args[@]}"
+main() {
+ # Add more sample images here.
+ generate_image disk_ext2_1k default 16777216 1024
+ generate_image disk_ext2_4k default 16777216 4096
+ generate_image disk_ext2_ue_settings ue_settings 16777216 4096
+
+ # Generate the tarball and delete temporary images.
+ echo "Packing tar file sample_images.tar.bz2"
+ tar -jcf "${OUTPUT_DIR}/sample_images.tar.bz2" -C "${OUTPUT_DIR}" \
+ "${IMAGES[@]}"
+ cd "${OUTPUT_DIR}"
+ rm "${IMAGES[@]}"
+}
+
+main
diff --git a/sample_images/sample_images.tar.bz2 b/sample_images/sample_images.tar.bz2
new file mode 100644
index 0000000..0982271
--- /dev/null
+++ b/sample_images/sample_images.tar.bz2
Binary files differ
diff --git a/shill_proxy.cc b/shill_proxy.cc
new file mode 100644
index 0000000..f72ee8b
--- /dev/null
+++ b/shill_proxy.cc
@@ -0,0 +1,39 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/shill_proxy.h"
+
+#include <chromeos/dbus/service_constants.h>
+
+#include "update_engine/dbus_proxies.h"
+
+using org::chromium::flimflam::ManagerProxy;
+using org::chromium::flimflam::ManagerProxyInterface;
+using org::chromium::flimflam::ServiceProxy;
+using org::chromium::flimflam::ServiceProxyInterface;
+
+namespace chromeos_update_engine {
+
+ShillProxy::ShillProxy(const scoped_refptr<dbus::Bus>& bus) : bus_(bus) {}
+
+bool ShillProxy::Init() {
+ manager_proxy_.reset(
+ new ManagerProxy(bus_,
+ shill::kFlimflamServiceName,
+ dbus::ObjectPath(shill::kFlimflamServicePath)));
+ return true;
+}
+
+ManagerProxyInterface* ShillProxy::GetManagerProxy() {
+ return manager_proxy_.get();
+}
+
+std::unique_ptr<ServiceProxyInterface> ShillProxy::GetServiceForPath(
+ const std::string& path) {
+ DCHECK(bus_.get());
+ return std::unique_ptr<ServiceProxyInterface>(new ServiceProxy(
+ bus_, shill::kFlimflamServiceName, dbus::ObjectPath(path)));
+}
+
+} // namespace chromeos_update_engine
diff --git a/shill_proxy.h b/shill_proxy.h
new file mode 100644
index 0000000..3d1b376
--- /dev/null
+++ b/shill_proxy.h
@@ -0,0 +1,45 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UPDATE_ENGINE_SHILL_PROXY_H_
+#define UPDATE_ENGINE_SHILL_PROXY_H_
+
+#include <memory>
+#include <string>
+
+#include <base/macros.h>
+#include <dbus/bus.h>
+
+#include "update_engine/dbus_proxies.h"
+#include "update_engine/shill_proxy_interface.h"
+
+namespace chromeos_update_engine {
+
+// This class implements the connection to shill using real DBus calls.
+class ShillProxy : public ShillProxyInterface {
+ public:
+ explicit ShillProxy(const scoped_refptr<dbus::Bus>& bus);
+ ~ShillProxy() override = default;
+
+ // Initializes the ShillProxy instance creating the manager proxy from the
+ // |bus_|.
+ bool Init();
+
+ // ShillProxyInterface overrides.
+ org::chromium::flimflam::ManagerProxyInterface* GetManagerProxy() override;
+ std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface>
+ GetServiceForPath(const std::string& path) override;
+
+ private:
+ // A reference to the main bus for creating new ServiceProxy instances.
+ scoped_refptr<dbus::Bus> bus_;
+ std::unique_ptr<org::chromium::flimflam::ManagerProxyInterface>
+ manager_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShillProxy);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_SHILL_PROXY_H_
diff --git a/shill_proxy_interface.h b/shill_proxy_interface.h
new file mode 100644
index 0000000..a4d3aa3
--- /dev/null
+++ b/shill_proxy_interface.h
@@ -0,0 +1,44 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UPDATE_ENGINE_SHILL_PROXY_INTERFACE_H_
+#define UPDATE_ENGINE_SHILL_PROXY_INTERFACE_H_
+
+#include <memory>
+#include <string>
+
+#include <base/macros.h>
+
+#include "update_engine/dbus_proxies.h"
+
+namespace chromeos_update_engine {
+
+// This class handles the DBus connection with shill daemon. The DBus interface
+// with shill requires to monitor or request the current service by interacting
+// with the org::chromium::flimflam::ManagerProxy and then request or monitor
+// properties on the selected org::chromium::flimflam::ServiceProxy. This class
+// provides a mockable way to access that.
+class ShillProxyInterface {
+ public:
+ virtual ~ShillProxyInterface() = default;
+
+ // Return the ManagerProxy instance of the shill daemon. The instance is owned
+ // by this ShillProxyInterface instance.
+ virtual org::chromium::flimflam::ManagerProxyInterface* GetManagerProxy() = 0;
+
+ // Return a ServiceProxy for the given path. The ownership of the returned
+ // instance is transferred to the caller.
+ virtual std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface>
+ GetServiceForPath(const std::string& path) = 0;
+
+ protected:
+ ShillProxyInterface() = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ShillProxyInterface);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_SHILL_PROXY_INTERFACE_H_
diff --git a/subprocess.cc b/subprocess.cc
index 6a34641..fa7e4cb 100644
--- a/subprocess.cc
+++ b/subprocess.cc
@@ -18,47 +18,81 @@
#include <base/posix/eintr_wrapper.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-
-#include "update_engine/glib_utils.h"
+#include <chromeos/process.h>
+#include <chromeos/secure_blob.h>
using chromeos::MessageLoop;
-using std::shared_ptr;
using std::string;
using std::unique_ptr;
using std::vector;
namespace chromeos_update_engine {
-void Subprocess::GChildExitedCallback(GPid pid, gint status, gpointer data) {
- SubprocessRecord* record = reinterpret_cast<SubprocessRecord*>(data);
+namespace {
- // Make sure we read any remaining process output and then close the pipe.
- OnStdoutReady(record);
+bool SetupChild(const std::map<string, string>& env, uint32_t flags) {
+ // Setup the environment variables.
+ clearenv();
+ for (const auto& key_value : env) {
+ setenv(key_value.first.c_str(), key_value.second.c_str(), 0);
+ }
- MessageLoop::current()->CancelTask(record->task_id);
- record->task_id = MessageLoop::kTaskIdNull;
- if (IGNORE_EINTR(close(record->stdout_fd)) != 0) {
- PLOG(ERROR) << "Error closing fd " << record->stdout_fd;
+ if ((flags & Subprocess::kRedirectStderrToStdout) != 0) {
+ if (HANDLE_EINTR(dup2(STDOUT_FILENO, STDERR_FILENO)) != STDERR_FILENO)
+ return false;
}
- g_spawn_close_pid(pid);
- gint use_status = status;
- if (WIFEXITED(status))
- use_status = WEXITSTATUS(status);
- if (status) {
- LOG(INFO) << "Subprocess status: " << use_status;
- }
- if (!record->stdout.empty()) {
- LOG(INFO) << "Subprocess output:\n" << record->stdout;
- }
- if (record->callback) {
- record->callback(use_status, record->stdout, record->callback_data);
- }
- Get().subprocess_records_.erase(record->tag);
+ int fd = HANDLE_EINTR(open("/dev/null", O_RDONLY));
+ if (fd < 0)
+ return false;
+ if (HANDLE_EINTR(dup2(fd, STDIN_FILENO)) != STDIN_FILENO)
+ return false;
+ IGNORE_EINTR(close(fd));
+
+ return true;
}
-void Subprocess::GRedirectStderrToStdout(gpointer user_data) {
- dup2(1, 2);
+// Helper function to launch a process with the given Subprocess::Flags.
+// This function only sets up and starts the process according to the |flags|.
+// The caller is responsible for watching the termination of the subprocess.
+// Return whether the process was successfully launched and fills in the |proc|
+// Process.
+bool LaunchProcess(const vector<string>& cmd,
+ uint32_t flags,
+ chromeos::Process* proc) {
+ for (const string& arg : cmd)
+ proc->AddArg(arg);
+ proc->SetSearchPath((flags & Subprocess::kSearchPath) != 0);
+
+ // Create an environment for the child process with just the required PATHs.
+ std::map<string, string> env;
+ for (const char* key : {"LD_LIBRARY_PATH", "PATH"}) {
+ const char* value = getenv(key);
+ if (value)
+ env.emplace(key, value);
+ }
+
+ proc->RedirectUsingPipe(STDOUT_FILENO, false);
+ proc->SetPreExecCallback(base::Bind(&SetupChild, env, flags));
+
+ return proc->Start();
+}
+
+} // namespace
+
+void Subprocess::Init(
+ chromeos::AsynchronousSignalHandlerInterface* async_signal_handler) {
+ if (subprocess_singleton_ == this)
+ return;
+ CHECK(subprocess_singleton_ == nullptr);
+ subprocess_singleton_ = this;
+
+ process_reaper_.Register(async_signal_handler);
+}
+
+Subprocess::~Subprocess() {
+ if (subprocess_singleton_ == this)
+ subprocess_singleton_ = nullptr;
}
void Subprocess::OnStdoutReady(SubprocessRecord* record) {
@@ -71,200 +105,150 @@
// input as we are in non-blocking mode.
if (errno != EWOULDBLOCK && errno != EAGAIN) {
PLOG(ERROR) << "Error reading fd " << record->stdout_fd;
+ MessageLoop::current()->CancelTask(record->stdout_task_id);
+ record->stdout_task_id = MessageLoop::kTaskIdNull;
}
+ } else if (rc == 0) {
+ // A value of 0 means that the child closed its end of the pipe and there
+ // is nothing else to read from stdout.
+ MessageLoop::current()->CancelTask(record->stdout_task_id);
+ record->stdout_task_id = MessageLoop::kTaskIdNull;
} else {
record->stdout.append(buf, rc);
}
} while (rc > 0);
}
-namespace {
-void FreeArgv(char** argv) {
- for (int i = 0; argv[i]; i++) {
- free(argv[i]);
- argv[i] = nullptr;
+void Subprocess::ChildExitedCallback(const siginfo_t& info) {
+ auto pid_record = subprocess_records_.find(info.si_pid);
+ if (pid_record == subprocess_records_.end())
+ return;
+ SubprocessRecord* record = pid_record->second.get();
+
+ // Make sure we read any remaining process output and then close the pipe.
+ OnStdoutReady(record);
+
+ MessageLoop::current()->CancelTask(record->stdout_task_id);
+ record->stdout_task_id = MessageLoop::kTaskIdNull;
+
+ // Release and close all the pipes now.
+ record->proc.Release();
+ record->proc.Reset(0);
+
+ // Don't print any log if the subprocess exited with exit code 0.
+ if (info.si_code != CLD_EXITED) {
+ LOG(INFO) << "Subprocess terminated with si_code " << info.si_code;
+ } else if (info.si_status != 0) {
+ LOG(INFO) << "Subprocess exited with si_status: " << info.si_status;
}
-}
-void FreeArgvInError(char** argv) {
- FreeArgv(argv);
- LOG(ERROR) << "Ran out of memory copying args.";
-}
-
-// Note: Caller responsible for free()ing the returned value!
-// Will return null on failure and free any allocated memory.
-char** ArgPointer() {
- const char* keys[] = {"LD_LIBRARY_PATH", "PATH"};
- char** ret = new char*[arraysize(keys) + 1];
- int pointer = 0;
- for (size_t i = 0; i < arraysize(keys); i++) {
- if (getenv(keys[i])) {
- ret[pointer] = strdup(base::StringPrintf("%s=%s", keys[i],
- getenv(keys[i])).c_str());
- if (!ret[pointer]) {
- FreeArgv(ret);
- delete [] ret;
- return nullptr;
- }
- ++pointer;
- }
+ if (!record->stdout.empty()) {
+ LOG(INFO) << "Subprocess output:\n" << record->stdout;
}
- ret[pointer] = nullptr;
- return ret;
-}
-
-class ScopedFreeArgPointer {
- public:
- explicit ScopedFreeArgPointer(char** arr) : arr_(arr) {}
- ~ScopedFreeArgPointer() {
- if (!arr_)
- return;
- for (int i = 0; arr_[i]; i++)
- free(arr_[i]);
- delete[] arr_;
+ if (!record->callback.is_null()) {
+ record->callback.Run(info.si_status, record->stdout);
}
- private:
- char** arr_;
- DISALLOW_COPY_AND_ASSIGN(ScopedFreeArgPointer);
-};
-} // namespace
-
-uint32_t Subprocess::Exec(const vector<string>& cmd,
- ExecCallback callback,
- void* p) {
- return ExecFlags(cmd, static_cast<GSpawnFlags>(0), true, callback, p);
+ subprocess_records_.erase(pid_record);
}
-uint32_t Subprocess::ExecFlags(const vector<string>& cmd,
- GSpawnFlags flags,
- bool redirect_stderr_to_stdout,
- ExecCallback callback,
- void* p) {
- unique_ptr<gchar*, utils::GLibStrvFreeDeleter> argv(
- utils::StringVectorToGStrv(cmd));
+pid_t Subprocess::Exec(const vector<string>& cmd,
+ const ExecCallback& callback) {
+ return ExecFlags(cmd, kRedirectStderrToStdout, callback);
+}
- char** argp = ArgPointer();
- if (!argp) {
- FreeArgvInError(argv.get()); // null in argv[i] terminates argv.
+pid_t Subprocess::ExecFlags(const vector<string>& cmd,
+ uint32_t flags,
+ const ExecCallback& callback) {
+ unique_ptr<SubprocessRecord> record(new SubprocessRecord(callback));
+
+ if (!LaunchProcess(cmd, flags, &record->proc)) {
+ LOG(ERROR) << "Failed to launch subprocess";
return 0;
}
- ScopedFreeArgPointer argp_free(argp);
- shared_ptr<SubprocessRecord> record(new SubprocessRecord);
- record->callback = callback;
- record->callback_data = p;
- gint stdout_fd = -1;
- GError* error = nullptr;
- bool success = g_spawn_async_with_pipes(
- nullptr, // working directory
- argv.get(),
- argp,
- static_cast<GSpawnFlags>(flags | G_SPAWN_DO_NOT_REAP_CHILD), // flags
- // child setup function:
- redirect_stderr_to_stdout ? GRedirectStderrToStdout : nullptr,
- nullptr, // child setup data pointer
- &record->pid,
- nullptr,
- &stdout_fd,
- nullptr,
- &error);
- if (!success) {
- LOG(ERROR) << "g_spawn_async failed: " << utils::GetAndFreeGError(&error);
- return 0;
- }
- record->tag =
- g_child_watch_add(record->pid, GChildExitedCallback, record.get());
- record->stdout_fd = stdout_fd;
- subprocess_records_[record->tag] = record;
+ pid_t pid = record->proc.pid();
+ CHECK(process_reaper_.WatchForChild(FROM_HERE, pid, base::Bind(
+ &Subprocess::ChildExitedCallback,
+ base::Unretained(this))));
+ record->stdout_fd = record->proc.GetPipe(STDOUT_FILENO);
// Capture the subprocess output. Make our end of the pipe non-blocking.
- int fd_flags = fcntl(stdout_fd, F_GETFL, 0) | O_NONBLOCK;
+ int fd_flags = fcntl(record->stdout_fd, F_GETFL, 0) | O_NONBLOCK;
if (HANDLE_EINTR(fcntl(record->stdout_fd, F_SETFL, fd_flags)) < 0) {
LOG(ERROR) << "Unable to set non-blocking I/O mode on fd "
<< record->stdout_fd << ".";
}
- record->task_id = MessageLoop::current()->WatchFileDescriptor(
+ record->stdout_task_id = MessageLoop::current()->WatchFileDescriptor(
FROM_HERE,
record->stdout_fd,
MessageLoop::WatchMode::kWatchRead,
true,
base::Bind(&Subprocess::OnStdoutReady, record.get()));
- return record->tag;
+ subprocess_records_[pid].reset(record.release());
+ return pid;
}
-void Subprocess::KillExec(uint32_t tag) {
- const auto& record = subprocess_records_.find(tag);
- if (record == subprocess_records_.end())
+void Subprocess::KillExec(pid_t pid) {
+ auto pid_record = subprocess_records_.find(pid);
+ if (pid_record == subprocess_records_.end())
return;
- record->second->callback = nullptr;
- kill(record->second->pid, SIGTERM);
-}
-
-bool Subprocess::SynchronousExecFlags(const vector<string>& cmd,
- GSpawnFlags flags,
- int* return_code,
- string* stdout) {
- if (stdout) {
- *stdout = "";
- }
- GError* err = nullptr;
- unique_ptr<char*[]> argv(new char*[cmd.size() + 1]);
- for (unsigned int i = 0; i < cmd.size(); i++) {
- argv[i] = strdup(cmd[i].c_str());
- if (!argv[i]) {
- FreeArgvInError(argv.get()); // null in argv[i] terminates argv.
- return false;
- }
- }
- argv[cmd.size()] = nullptr;
-
- char** argp = ArgPointer();
- if (!argp) {
- FreeArgvInError(argv.get()); // null in argv[i] terminates argv.
- return false;
- }
- ScopedFreeArgPointer argp_free(argp);
-
- char* child_stdout;
- bool success = g_spawn_sync(
- nullptr, // working directory
- argv.get(),
- argp,
- static_cast<GSpawnFlags>(G_SPAWN_STDERR_TO_DEV_NULL |
- G_SPAWN_SEARCH_PATH | flags), // flags
- GRedirectStderrToStdout, // child setup function
- nullptr, // data for child setup function
- &child_stdout,
- nullptr,
- return_code,
- &err);
- FreeArgv(argv.get());
- LOG_IF(INFO, err) << utils::GetAndFreeGError(&err);
- if (child_stdout) {
- if (stdout) {
- *stdout = child_stdout;
- } else if (*child_stdout) {
- LOG(INFO) << "Subprocess output:\n" << child_stdout;
- }
- g_free(child_stdout);
- }
- return success;
+ pid_record->second->callback.Reset();
+ kill(pid, SIGTERM);
}
bool Subprocess::SynchronousExec(const vector<string>& cmd,
int* return_code,
string* stdout) {
- return SynchronousExecFlags(cmd,
- static_cast<GSpawnFlags>(0),
- return_code,
- stdout);
+ // The default for SynchronousExec is to use kSearchPath since the code relies
+ // on that.
+ return SynchronousExecFlags(
+ cmd,
+ kRedirectStderrToStdout | kSearchPath,
+ return_code,
+ stdout);
+}
+
+bool Subprocess::SynchronousExecFlags(const vector<string>& cmd,
+ uint32_t flags,
+ int* return_code,
+ string* stdout) {
+ chromeos::ProcessImpl proc;
+ if (!LaunchProcess(cmd, flags, &proc)) {
+ LOG(ERROR) << "Failed to launch subprocess";
+ return false;
+ }
+
+ if (stdout) {
+ stdout->clear();
+ }
+
+ int fd = proc.GetPipe(STDOUT_FILENO);
+ vector<char> buffer(32 * 1024);
+ while (true) {
+ int rc = HANDLE_EINTR(read(fd, buffer.data(), buffer.size()));
+ if (rc < 0) {
+ PLOG(ERROR) << "Reading from child's output";
+ break;
+ } else if (rc == 0) {
+ break;
+ } else {
+ if (stdout)
+ stdout->append(buffer.data(), rc);
+ }
+ }
+ // At this point, the subprocess already closed the output, so we only need to
+ // wait for it to finish.
+ int proc_return_code = proc.Wait();
+ if (return_code)
+ *return_code = proc_return_code;
+ return proc_return_code != chromeos::Process::kErrorExitStatus;
}
bool Subprocess::SubprocessInFlight() {
- for (const auto& tag_record_pair : subprocess_records_) {
- if (tag_record_pair.second->callback)
+ for (const auto& pid_record : subprocess_records_) {
+ if (!pid_record.second->callback.is_null())
return true;
}
return false;
diff --git a/subprocess.h b/subprocess.h
index 13f7eb7..775b95f 100644
--- a/subprocess.h
+++ b/subprocess.h
@@ -5,16 +5,20 @@
#ifndef UPDATE_ENGINE_SUBPROCESS_H_
#define UPDATE_ENGINE_SUBPROCESS_H_
-#include <glib.h>
+#include <unistd.h>
#include <map>
#include <memory>
#include <string>
#include <vector>
+#include <base/callback.h>
#include <base/logging.h>
#include <base/macros.h>
+#include <chromeos/asynchronous_signal_handler_interface.h>
#include <chromeos/message_loops/message_loop.h>
+#include <chromeos/process.h>
+#include <chromeos/process_reaper.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
// The Subprocess class is a singleton. It's used to spawn off a subprocess
@@ -23,42 +27,51 @@
// you know you won't call KillExec(), you may safely lose the return value
// from Exec().
+// To create the Subprocess singleton just instantiate it with and call Init().
+// You can't have two Subprocess instances initialized at the same time.
+
namespace chromeos_update_engine {
class Subprocess {
public:
- typedef void(*ExecCallback)(int return_code,
- const std::string& output,
- void *p);
+ enum Flags {
+ kSearchPath = 1 << 0,
+ kRedirectStderrToStdout = 1 << 1,
+ };
- static void Init() {
- CHECK(!subprocess_singleton_);
- subprocess_singleton_ = new Subprocess;
- }
+ // Callback type used when an async process terminates. It receives the exit
+ // code and the stdout output (and stderr if redirected).
+ using ExecCallback = base::Callback<void(int, const std::string&)>;
- // Returns a tag > 0 on success.
- uint32_t Exec(const std::vector<std::string>& cmd,
- ExecCallback callback,
- void* p);
- uint32_t ExecFlags(const std::vector<std::string>& cmd,
- GSpawnFlags flags,
- bool redirect_stderr_to_stdout,
- ExecCallback callback,
- void* p);
+ Subprocess() = default;
+
+ // Destroy and unregister the Subprocess singleton.
+ ~Subprocess();
+
+ // Initialize and register the Subprocess singleton.
+ void Init(chromeos::AsynchronousSignalHandlerInterface* async_signal_handler);
+
+ // Launches a process in the background and calls the passed |callback| when
+ // the process exits.
+ // Returns the process id of the new launched process or 0 in case of failure.
+ pid_t Exec(const std::vector<std::string>& cmd, const ExecCallback& callback);
+ pid_t ExecFlags(const std::vector<std::string>& cmd,
+ uint32_t flags,
+ const ExecCallback& callback);
// Kills the running process with SIGTERM and ignores the callback.
- void KillExec(uint32_t tag);
+ void KillExec(pid_t tag);
// Executes a command synchronously. Returns true on success. If |stdout| is
// non-null, the process output is stored in it, otherwise the output is
// logged. Note that stderr is redirected to stdout.
- static bool SynchronousExecFlags(const std::vector<std::string>& cmd,
- GSpawnFlags flags,
- int* return_code,
- std::string* stdout);
static bool SynchronousExec(const std::vector<std::string>& cmd,
int* return_code,
std::string* stdout);
+ static bool SynchronousExecFlags(const std::vector<std::string>& cmd,
+ uint32_t flags,
+ int* return_code,
+ std::string* stdout);
// Gets the one instance.
static Subprocess& Get() {
@@ -72,40 +85,41 @@
FRIEND_TEST(SubprocessTest, CancelTest);
struct SubprocessRecord {
- SubprocessRecord() = default;
+ explicit SubprocessRecord(const ExecCallback& callback)
+ : callback(callback) {}
- uint32_t tag{0};
- chromeos::MessageLoop::TaskId task_id{chromeos::MessageLoop::kTaskIdNull};
+ // The callback supplied by the caller.
+ ExecCallback callback;
- ExecCallback callback{nullptr};
- void* callback_data{nullptr};
+ // The ProcessImpl instance managing the child process. Destroying this
+ // will close our end of the pipes we have open.
+ chromeos::ProcessImpl proc;
- GPid pid;
-
+ // These are used to monitor the stdout of the running process, including
+ // the stderr if it was redirected.
+ chromeos::MessageLoop::TaskId stdout_task_id{
+ chromeos::MessageLoop::kTaskIdNull};
int stdout_fd{-1};
std::string stdout;
};
- Subprocess() {}
-
- // Callback for when any subprocess terminates. This calls the user
- // requested callback.
- static void GChildExitedCallback(GPid pid, gint status, gpointer data);
-
- // Callback which runs in the child before exec to redirect stderr onto
- // stdout.
- static void GRedirectStderrToStdout(gpointer user_data);
-
// Callback which runs whenever there is input available on the subprocess
// stdout pipe.
static void OnStdoutReady(SubprocessRecord* record);
+ // Callback for when any subprocess terminates. This calls the user
+ // requested callback.
+ void ChildExitedCallback(const siginfo_t& info);
+
// The global instance.
static Subprocess* subprocess_singleton_;
// A map from the asynchronous subprocess tag (see Exec) to the subprocess
// record structure for all active asynchronous subprocesses.
- std::map<uint32_t, std::shared_ptr<SubprocessRecord>> subprocess_records_;
+ std::map<pid_t, std::unique_ptr<SubprocessRecord>> subprocess_records_;
+
+ // Used to watch for child processes.
+ chromeos::ProcessReaper process_reaper_;
DISALLOW_COPY_AND_ASSIGN(Subprocess);
};
diff --git a/subprocess_unittest.cc b/subprocess_unittest.cc
index dba9e6c..d9b212b 100644
--- a/subprocess_unittest.cc
+++ b/subprocess_unittest.cc
@@ -13,19 +13,21 @@
#include <sys/types.h>
#include <unistd.h>
+#include <set>
#include <string>
#include <vector>
#include <base/bind.h>
#include <base/location.h>
+#include <base/message_loop/message_loop.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <base/time/time.h>
#include <chromeos/bind_lambda.h>
-#include <chromeos/message_loops/glib_message_loop.h>
+#include <chromeos/message_loops/base_message_loop.h>
#include <chromeos/message_loops/message_loop.h>
#include <chromeos/message_loops/message_loop_utils.h>
-#include <glib.h>
+#include <chromeos/strings/string_utils.h>
#include <gtest/gtest.h>
#include "update_engine/test_utils.h"
@@ -42,69 +44,84 @@
protected:
void SetUp() override {
loop_.SetAsCurrent();
+ async_signal_handler_.Init();
+ subprocess_.Init(&async_signal_handler_);
}
- void TearDown() override {
- EXPECT_EQ(0, chromeos::MessageLoopRunMaxIterations(&loop_, 1));
- }
-
- // TODO(deymo): Replace this with a FakeMessageLoop. Subprocess uses glib to
- // asynchronously spawn a process, so we need to run a GlibMessageLoop here.
- chromeos::GlibMessageLoop loop_;
+ base::MessageLoopForIO base_loop_;
+ chromeos::BaseMessageLoop loop_{&base_loop_};
+ chromeos::AsynchronousSignalHandler async_signal_handler_;
+ Subprocess subprocess_;
};
namespace {
+
int local_server_port = 0;
-void Callback(int return_code, const string& output, void* /* unused */) {
- EXPECT_EQ(1, return_code);
+void ExpectedResults(int expected_return_code, const string& expected_output,
+ int return_code, const string& output) {
+ EXPECT_EQ(expected_return_code, return_code);
+ EXPECT_EQ(expected_output, output);
MessageLoop::current()->BreakLoop();
}
-void CallbackEcho(int return_code, const string& output, void* /* unused */) {
+void ExpectedEnvVars(int return_code, const string& output) {
EXPECT_EQ(0, return_code);
- EXPECT_NE(string::npos, output.find("this is stdout"));
- EXPECT_NE(string::npos, output.find("this is stderr"));
- MessageLoop::current()->BreakLoop();
-}
-
-void CallbackStdoutOnlyEcho(int return_code,
- const string& output,
- void* /* unused */) {
- EXPECT_EQ(0, return_code);
- EXPECT_NE(string::npos, output.find("on stdout"));
- EXPECT_EQ(string::npos, output.find("on stderr"));
+ const std::set<string> allowed_envs = {"LD_LIBRARY_PATH", "PATH"};
+ for (string key_value : chromeos::string_utils::Split(output, "\n")) {
+ auto key_value_pair = chromeos::string_utils::SplitAtFirst(
+ key_value, "=", true);
+ EXPECT_NE(allowed_envs.end(), allowed_envs.find(key_value_pair.first));
+ }
MessageLoop::current()->BreakLoop();
}
} // namespace
+TEST_F(SubprocessTest, IsASingleton) {
+ EXPECT_EQ(&subprocess_, &Subprocess::Get());
+}
+
+TEST_F(SubprocessTest, InactiveInstancesDontChangeTheSingleton) {
+ std::unique_ptr<Subprocess> another_subprocess(new Subprocess());
+ EXPECT_EQ(&subprocess_, &Subprocess::Get());
+ another_subprocess.reset();
+ EXPECT_EQ(&subprocess_, &Subprocess::Get());
+}
+
TEST_F(SubprocessTest, SimpleTest) {
- Subprocess::Get().Exec(vector<string>{"/bin/false"}, Callback, nullptr);
+ EXPECT_TRUE(subprocess_.Exec({"/bin/false"},
+ base::Bind(&ExpectedResults, 1, "")));
loop_.Run();
}
TEST_F(SubprocessTest, EchoTest) {
- Subprocess::Get().Exec(
- vector<string>{
- "/bin/sh",
- "-c",
- "echo this is stdout; echo this is stderr > /dev/stderr"},
- CallbackEcho,
- nullptr);
+ EXPECT_TRUE(subprocess_.Exec(
+ {"/bin/sh", "-c", "echo this is stdout; echo this is stderr >&2"},
+ base::Bind(&ExpectedResults, 0, "this is stdout\nthis is stderr\n")));
loop_.Run();
}
TEST_F(SubprocessTest, StderrNotIncludedInOutputTest) {
- Subprocess::Get().ExecFlags(
- vector<string>{"/bin/sh", "-c", "echo on stdout; echo on stderr >&2"},
- static_cast<GSpawnFlags>(0),
- false, // don't redirect stderr
- CallbackStdoutOnlyEcho,
- nullptr);
+ EXPECT_TRUE(subprocess_.ExecFlags(
+ {"/bin/sh", "-c", "echo on stdout; echo on stderr >&2"},
+ 0,
+ base::Bind(&ExpectedResults, 0, "on stdout\n")));
loop_.Run();
}
+TEST_F(SubprocessTest, EnvVarsAreFiltered) {
+ EXPECT_TRUE(subprocess_.Exec({"/usr/bin/env"}, base::Bind(&ExpectedEnvVars)));
+ loop_.Run();
+}
+
+TEST_F(SubprocessTest, SynchronousTrueSearchsOnPath) {
+ int rc = -1;
+ EXPECT_TRUE(Subprocess::SynchronousExecFlags(
+ {"true"}, Subprocess::kSearchPath, &rc, nullptr));
+ EXPECT_EQ(0, rc);
+}
+
TEST_F(SubprocessTest, SynchronousEchoTest) {
vector<string> cmd = {
"/bin/sh",
@@ -118,15 +135,16 @@
}
TEST_F(SubprocessTest, SynchronousEchoNoOutputTest) {
- vector<string> cmd = {"/bin/sh", "-c", "echo test"};
int rc = -1;
- ASSERT_TRUE(Subprocess::SynchronousExec(cmd, &rc, nullptr));
+ ASSERT_TRUE(Subprocess::SynchronousExec(
+ {"/bin/sh", "-c", "echo test"},
+ &rc, nullptr));
EXPECT_EQ(0, rc);
}
namespace {
-void CallbackBad(int return_code, const string& output, void* p) {
- CHECK(false) << "should never be called.";
+void CallbackBad(int return_code, const string& output) {
+ ADD_FAILURE() << "should never be called.";
}
// TODO(garnold) this test method uses test_http_server as a representative for
@@ -148,7 +166,7 @@
vector<string> cmd;
cmd.push_back("./test_http_server");
cmd.push_back(temp_file_name);
- uint32_t tag = Subprocess::Get().Exec(cmd, CallbackBad, nullptr);
+ uint32_t tag = Subprocess::Get().Exec(cmd, base::Bind(&CallbackBad));
EXPECT_NE(0, tag);
*spawned = true;
printf("test http server spawned\n");
@@ -177,7 +195,7 @@
<< strerror(errno);
break;
}
- g_usleep(kSleepTime.InMicroseconds());
+ usleep(kSleepTime.InMicroseconds());
total_wait_time += kSleepTime;
}
close(temp_fd);
diff --git a/system_state.h b/system_state.h
index 21aeabd..e32aa0e 100644
--- a/system_state.h
+++ b/system_state.h
@@ -5,6 +5,8 @@
#ifndef UPDATE_ENGINE_SYSTEM_STATE_H_
#define UPDATE_ENGINE_SYSTEM_STATE_H_
+#include "update_engine/dbus_proxies.h"
+
class MetricsLibraryInterface;
namespace chromeos_update_manager {
@@ -25,8 +27,7 @@
// any circular references in header file inclusion. Hence forward-declaring
// the required classes.
class ClockInterface;
-class ConnectionManager;
-class GpioHandler;
+class ConnectionManagerInterface;
class HardwareInterface;
class OmahaRequestParams;
class P2PManager;
@@ -54,7 +55,7 @@
virtual ClockInterface* clock() = 0;
// Gets the connection manager object.
- virtual ConnectionManager* connection_manager() = 0;
+ virtual ConnectionManagerInterface* connection_manager() = 0;
// Gets the hardware interface object.
virtual HardwareInterface* hardware() = 0;
@@ -87,6 +88,9 @@
// Returns a pointer to the UpdateManager singleton.
virtual chromeos_update_manager::UpdateManager* update_manager() = 0;
+ // DBus proxies. Mocked during test.
+ virtual org::chromium::PowerManagerProxyInterface* power_manager_proxy() = 0;
+
// If true, this is the first instance of the update engine since the system
// restarted. Important for tracking whether you are running instance of the
// update engine on first boot or due to a crash/restart.
diff --git a/tar_bunzip2.gypi b/tar_bunzip2.gypi
new file mode 100644
index 0000000..4ba7528
--- /dev/null
+++ b/tar_bunzip2.gypi
@@ -0,0 +1,27 @@
+{
+ 'variables': {
+ 'out_dir': '<(SHARED_INTERMEDIATE_DIR)/<(image_out_dir)',
+ },
+ 'rules': [
+ {
+ 'rule_name': 'tar-bunzip2',
+ 'extension': 'bz2',
+ 'inputs': [
+ '<(RULE_INPUT_PATH)',
+ ],
+ 'outputs': [
+ # The .flag file is used to mark the timestamp of the file extraction
+ # and re-run this action if a new .bz2 file is generated.
+ '<(out_dir)/<(RULE_INPUT_ROOT).flag',
+ ],
+ 'action': [
+ 'sh',
+ '-c',
+ 'tar -xvf "<(RULE_INPUT_PATH)" -C "<(out_dir)" && touch <(out_dir)/<(RULE_INPUT_ROOT).flag',
+ ],
+ 'msvs_cygwin_shell': 0,
+ 'process_outputs_as_sources': 1,
+ 'message': 'Unpacking file <(RULE_INPUT_PATH)',
+ },
+ ],
+}
diff --git a/test_utils.cc b/test_utils.cc
index 0c02279..7965f85 100644
--- a/test_utils.cc
+++ b/test_utils.cc
@@ -18,9 +18,10 @@
#include <vector>
#include <base/files/file_util.h>
+#include <base/format_macros.h>
#include <base/logging.h>
-#include <base/strings/stringprintf.h>
#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
#include "update_engine/file_writer.h"
#include "update_engine/utils.h"
@@ -168,7 +169,7 @@
size_t size,
int block_size) {
EXPECT_EQ(0, System(StringPrintf("dd if=/dev/zero of=%s"
- " seek=%zu bs=1 count=1 status=none",
+ " seek=%" PRIuS " bs=1 count=1 status=none",
path.c_str(), size)));
EXPECT_EQ(0, System(StringPrintf("mkfs.ext3 -q -b %d -F %s",
block_size, path.c_str())));
@@ -302,19 +303,6 @@
return true;
}
-GValue* GValueNewString(const char* str) {
- GValue* gval = g_new0(GValue, 1);
- g_value_init(gval, G_TYPE_STRING);
- g_value_set_string(gval, str);
- return gval;
-}
-
-void GValueFree(gpointer arg) {
- auto gval = reinterpret_cast<GValue*>(arg);
- g_value_unset(gval);
- g_free(gval);
-}
-
base::FilePath GetBuildArtifactsPath() {
base::FilePath exe_path;
base::ReadSymbolicLink(base::FilePath("/proc/self/exe"), &exe_path);
diff --git a/test_utils.h b/test_utils.h
index 13bbd41..84aeae1 100644
--- a/test_utils.h
+++ b/test_utils.h
@@ -18,7 +18,6 @@
#include <base/callback.h>
#include <base/files/file_path.h>
-#include <glib-object.h>
#include <gtest/gtest.h>
#include "update_engine/action.h"
@@ -183,12 +182,6 @@
// This WILL cross filesystem boundaries.
bool RecursiveUnlinkDir(const std::string& path);
-// Allocates, initializes and returns a string GValue object.
-GValue* GValueNewString(const char* str);
-
-// Frees a GValue object and its allocated resources.
-void GValueFree(gpointer arg);
-
// Returns the path where the build artifacts are stored. This is the directory
// where the unittest executable is being run from.
base::FilePath GetBuildArtifactsPath();
diff --git a/testrunner.cc b/testrunner.cc
index 4786c74..845f16a 100644
--- a/testrunner.cc
+++ b/testrunner.cc
@@ -7,20 +7,12 @@
#include <base/at_exit.h>
#include <base/command_line.h>
#include <chromeos/test_helpers.h>
-#include <dbus/dbus-glib.h>
-#include <dbus/dbus-glib-bindings.h>
-#include <dbus/dbus-glib-lowlevel.h>
-#include <glib.h>
-#include <glib-object.h>
#include <gtest/gtest.h>
-#include "update_engine/subprocess.h"
#include "update_engine/terminator.h"
int main(int argc, char **argv) {
LOG(INFO) << "started";
- ::g_type_init();
- dbus_threads_init_default();
base::AtExitManager exit_manager;
// TODO(garnold) temporarily cause the unittest binary to exit with status
// code 2 upon catching a SIGTERM. This will help diagnose why the unittest
@@ -28,7 +20,6 @@
// the default exit status of 1. Corresponding reverts are necessary in
// terminator_unittest.cc.
chromeos_update_engine::Terminator::Init(2);
- chromeos_update_engine::Subprocess::Init();
LOG(INFO) << "parsing command line arguments";
base::CommandLine::Init(argc, argv);
LOG(INFO) << "initializing gtest";
diff --git a/update_attempter.cc b/update_attempter.cc
index a1b2f11..0e9a5c2 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -23,7 +23,6 @@
#include <chromeos/dbus/service_constants.h>
#include <chromeos/message_loops/message_loop.h>
-#include <glib.h>
#include <metrics/metrics_library.h>
#include <policy/device_policy.h>
#include <policy/libpolicy.h>
@@ -32,10 +31,8 @@
#include "update_engine/clock_interface.h"
#include "update_engine/constants.h"
#include "update_engine/dbus_service.h"
-#include "update_engine/dbus_wrapper_interface.h"
#include "update_engine/download_action.h"
#include "update_engine/filesystem_verifier_action.h"
-#include "update_engine/glib_utils.h"
#include "update_engine/hardware_interface.h"
#include "update_engine/libcurl_http_fetcher.h"
#include "update_engine/metrics.h"
@@ -134,18 +131,23 @@
return code;
}
-UpdateAttempter::UpdateAttempter(SystemState* system_state,
- DBusWrapperInterface* dbus_iface)
- : UpdateAttempter(system_state, dbus_iface, kUpdateCompletedMarker) {}
+UpdateAttempter::UpdateAttempter(
+ SystemState* system_state,
+ LibCrosProxy* libcros_proxy,
+ org::chromium::debugdProxyInterface* debugd_proxy)
+ : UpdateAttempter(system_state, libcros_proxy, debugd_proxy,
+ kUpdateCompletedMarker) {}
-UpdateAttempter::UpdateAttempter(SystemState* system_state,
- DBusWrapperInterface* dbus_iface,
- const string& update_completed_marker)
+UpdateAttempter::UpdateAttempter(
+ SystemState* system_state,
+ LibCrosProxy* libcros_proxy,
+ org::chromium::debugdProxyInterface* debugd_proxy,
+ const string& update_completed_marker)
: processor_(new ActionProcessor()),
system_state_(system_state),
- dbus_iface_(dbus_iface),
- chrome_proxy_resolver_(dbus_iface),
- update_completed_marker_(update_completed_marker) {
+ chrome_proxy_resolver_(libcros_proxy),
+ update_completed_marker_(update_completed_marker),
+ debugd_proxy_(debugd_proxy) {
if (!update_completed_marker_.empty() &&
utils::FileExists(update_completed_marker_.c_str())) {
status_ = UPDATE_STATUS_UPDATED_NEED_REBOOT;
@@ -879,35 +881,17 @@
}
bool UpdateAttempter::RequestPowerManagerReboot() {
- GError* error = nullptr;
- DBusGConnection* bus = dbus_iface_->BusGet(DBUS_BUS_SYSTEM, &error);
- if (!bus) {
- LOG(ERROR) << "Failed to get system bus: "
- << utils::GetAndFreeGError(&error);
+ org::chromium::PowerManagerProxyInterface* power_manager_proxy =
+ system_state_->power_manager_proxy();
+ if (!power_manager_proxy) {
+ LOG(WARNING) << "No PowerManager proxy defined, skipping reboot.";
return false;
}
-
LOG(INFO) << "Calling " << power_manager::kPowerManagerInterface << "."
<< power_manager::kRequestRestartMethod;
- DBusGProxy* proxy = dbus_iface_->ProxyNewForName(
- bus,
- power_manager::kPowerManagerServiceName,
- power_manager::kPowerManagerServicePath,
- power_manager::kPowerManagerInterface);
- const gboolean success = dbus_iface_->ProxyCall_1_0(
- proxy,
- power_manager::kRequestRestartMethod,
- &error,
- power_manager::REQUEST_RESTART_FOR_UPDATE);
- dbus_iface_->ProxyUnref(proxy);
-
- if (!success) {
- LOG(ERROR) << "Failed to call " << power_manager::kRequestRestartMethod
- << ": " << utils::GetAndFreeGError(&error);
- return false;
- }
-
- return true;
+ chromeos::ErrorPtr error;
+ return power_manager_proxy->RequestRestart(
+ power_manager::REQUEST_RESTART_FOR_UPDATE, &error);
}
bool UpdateAttempter::RebootDirectly() {
@@ -1220,13 +1204,21 @@
// the script runtime.
update_boot_flags_running_ = true;
LOG(INFO) << "Updating boot flags...";
- vector<string> cmd{set_good_kernel_cmd_};
- if (!Subprocess::Get().Exec(cmd, StaticCompleteUpdateBootFlags, this)) {
- CompleteUpdateBootFlags(1);
+ vector<string> cmd{"/usr/sbin/chromeos-setgoodkernel"};
+ if (skip_set_good_kernel_) {
+ CompleteUpdateBootFlags(1, "Skipping the call to set");
+ } else {
+ if (!Subprocess::Get().Exec(cmd,
+ Bind(&UpdateAttempter::CompleteUpdateBootFlags,
+ base::Unretained(this)))) {
+ CompleteUpdateBootFlags(
+ 1, "Failed to launch process to mark kernel as good");
+ }
}
}
-void UpdateAttempter::CompleteUpdateBootFlags(int return_code) {
+void UpdateAttempter::CompleteUpdateBootFlags(int return_code,
+ const string& output) {
update_boot_flags_running_ = false;
updated_boot_flags_ = true;
if (start_action_processor_) {
@@ -1234,20 +1226,11 @@
}
}
-void UpdateAttempter::StaticCompleteUpdateBootFlags(
- int return_code,
- const string& output,
- void* p) {
- reinterpret_cast<UpdateAttempter*>(p)->CompleteUpdateBootFlags(return_code);
-}
-
void UpdateAttempter::BroadcastStatus() {
- if (!dbus_service_) {
+ if (!dbus_adaptor_)
return;
- }
last_notify_time_ = TimeTicks::Now();
- update_engine_service_emit_status_update(
- dbus_service_,
+ dbus_adaptor_->SendStatusUpdateSignal(
last_checked_time_,
download_progress_,
UpdateStatusToString(status_),
@@ -1650,26 +1633,11 @@
// Official images in devmode are allowed a custom update source iff the
// debugd dev tools are enabled.
- GError* error = nullptr;
- DBusGConnection* bus = dbus_iface_->BusGet(DBUS_BUS_SYSTEM, &error);
- if (!bus) {
- LOG(ERROR) << "Failed to get system bus: "
- << utils::GetAndFreeGError(&error);
+ if (!debugd_proxy_)
return false;
- }
-
- gint dev_features = debugd::DEV_FEATURES_DISABLED;
- DBusGProxy* proxy = dbus_iface_->ProxyNewForName(
- bus,
- debugd::kDebugdServiceName,
- debugd::kDebugdServicePath,
- debugd::kDebugdInterface);
- const gboolean success = dbus_iface_->ProxyCall_0_1(
- proxy,
- debugd::kQueryDevFeatures,
- &error,
- &dev_features);
- dbus_iface_->ProxyUnref(proxy);
+ int32_t dev_features = debugd::DEV_FEATURES_DISABLED;
+ chromeos::ErrorPtr error;
+ bool success = debugd_proxy_->QueryDevFeatures(&dev_features, &error);
// Some boards may not include debugd so it's expected that this may fail,
// in which case we default to disallowing custom update sources.
diff --git a/update_attempter.h b/update_attempter.h
index 1326365..85b06f5 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -14,12 +14,12 @@
#include <base/bind.h>
#include <base/time/time.h>
-#include <glib.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
#include "update_engine/action_processor.h"
#include "update_engine/chrome_browser_proxy_resolver.h"
#include "update_engine/download_action.h"
+#include "update_engine/libcros_proxy.h"
#include "update_engine/omaha_request_params.h"
#include "update_engine/omaha_response_handler_action.h"
#include "update_engine/proxy_resolver.h"
@@ -28,7 +28,6 @@
#include "update_engine/update_manager/update_manager.h"
class MetricsLibraryInterface;
-struct UpdateEngineService;
namespace policy {
class PolicyProvider;
@@ -36,7 +35,7 @@
namespace chromeos_update_engine {
-class DBusWrapperInterface;
+class UpdateEngineAdaptor;
enum UpdateStatus {
UPDATE_STATUS_IDLE = 0,
@@ -59,7 +58,8 @@
static const int kMaxDeltaUpdateFailures;
UpdateAttempter(SystemState* system_state,
- DBusWrapperInterface* dbus_iface);
+ LibCrosProxy* libcros_proxy,
+ org::chromium::debugdProxyInterface* debugd_proxy);
~UpdateAttempter() override;
// Further initialization to be done post construction.
@@ -112,18 +112,15 @@
void UpdateBootFlags();
// Subprocess::Exec callback.
- void CompleteUpdateBootFlags(int return_code);
- static void StaticCompleteUpdateBootFlags(int return_code,
- const std::string& output,
- void* p);
+ void CompleteUpdateBootFlags(int return_code, const std::string& output);
UpdateStatus status() const { return status_; }
int http_response_code() const { return http_response_code_; }
void set_http_response_code(int code) { http_response_code_ = code; }
- void set_dbus_service(struct UpdateEngineService* dbus_service) {
- dbus_service_ = dbus_service;
+ void set_dbus_adaptor(UpdateEngineAdaptor* dbus_adaptor) {
+ dbus_adaptor_ = dbus_adaptor;
}
// This is the internal entry point for going through an
@@ -225,7 +222,8 @@
// Special ctor + friend declarations for testing purposes.
UpdateAttempter(SystemState* system_state,
- DBusWrapperInterface* dbus_iface,
+ LibCrosProxy* libcros_proxy,
+ org::chromium::debugdProxyInterface* debugd_proxy,
const std::string& update_completed_marker);
friend class UpdateAttempterUnderTest;
@@ -398,12 +396,9 @@
// carved out separately to mock out easily in unit tests.
SystemState* system_state_;
- // Interface for getting D-Bus connections.
- DBusWrapperInterface* dbus_iface_ = nullptr;
-
// If non-null, this UpdateAttempter will send status updates over this
// dbus service.
- UpdateEngineService* dbus_service_ = nullptr;
+ UpdateEngineAdaptor* dbus_adaptor_ = nullptr;
// Pointer to the OmahaResponseHandlerAction in the actions_ vector.
std::shared_ptr<OmahaResponseHandlerAction> response_handler_action_;
@@ -469,8 +464,9 @@
// True if UpdateBootFlags is running.
bool update_boot_flags_running_ = false;
- // The command to run to set the current kernel as good.
- std::string set_good_kernel_cmd_ = "/usr/sbin/chromeos-setgoodkernel";
+ // Whether we should skip the async call to "setgoodkernel" command. Used in
+ // unittests.
+ bool skip_set_good_kernel_ = false;
// True if the action processor needs to be started by the boot flag updater.
bool start_action_processor_ = false;
@@ -509,6 +505,8 @@
std::string forced_app_version_;
std::string forced_omaha_url_;
+ org::chromium::debugdProxyInterface* debugd_proxy_;
+
DISALLOW_COPY_AND_ASSIGN(UpdateAttempter);
};
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 41a8232..19f2081 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -9,9 +9,11 @@
#include <memory>
#include <base/files/file_util.h>
+#include <base/message_loop/message_loop.h>
#include <chromeos/bind_lambda.h>
#include <chromeos/dbus/service_constants.h>
-#include <chromeos/message_loops/glib_message_loop.h>
+#include <chromeos/make_unique_ptr.h>
+#include <chromeos/message_loops/base_message_loop.h>
#include <chromeos/message_loops/message_loop.h>
#include <chromeos/message_loops/message_loop_utils.h>
#include <gtest/gtest.h>
@@ -25,7 +27,6 @@
#include "update_engine/install_plan.h"
#include "update_engine/mock_action.h"
#include "update_engine/mock_action_processor.h"
-#include "update_engine/mock_dbus_wrapper.h"
#include "update_engine/mock_http_fetcher.h"
#include "update_engine/mock_p2p_manager.h"
#include "update_engine/mock_payload_state.h"
@@ -38,9 +39,10 @@
using base::Time;
using base::TimeDelta;
using chromeos::MessageLoop;
+using org::chromium::LibCrosServiceInterfaceProxyMock;
+using org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxyMock;
using std::string;
using std::unique_ptr;
-using testing::A;
using testing::DoAll;
using testing::InSequence;
using testing::Ne;
@@ -50,7 +52,6 @@
using testing::ReturnPointee;
using testing::SaveArg;
using testing::SetArgumentPointee;
-using testing::StrEq;
using testing::_;
namespace chromeos_update_engine {
@@ -60,17 +61,12 @@
// methods.
class UpdateAttempterUnderTest : public UpdateAttempter {
public:
- // We always feed an explicit update completed marker name; however, unless
- // explicitly specified, we feed an empty string, which causes the
- // UpdateAttempter class to ignore / not write the marker file.
UpdateAttempterUnderTest(SystemState* system_state,
- DBusWrapperInterface* dbus_iface)
- : UpdateAttempterUnderTest(system_state, dbus_iface, "") {}
-
- UpdateAttempterUnderTest(SystemState* system_state,
- DBusWrapperInterface* dbus_iface,
+ LibCrosProxy* libcros_proxy,
+ org::chromium::debugdProxyInterface* debugd_proxy,
const string& update_completed_marker)
- : UpdateAttempter(system_state, dbus_iface, update_completed_marker) {}
+ : UpdateAttempter(system_state, libcros_proxy, debugd_proxy,
+ update_completed_marker) {}
// Wrap the update scheduling method, allowing us to opt out of scheduled
// updates for testing purposes.
@@ -99,10 +95,12 @@
class UpdateAttempterTest : public ::testing::Test {
protected:
UpdateAttempterTest()
- : attempter_(&fake_system_state_, &dbus_),
- mock_connection_manager(&fake_system_state_),
- fake_dbus_system_bus_(reinterpret_cast<DBusGConnection*>(1)),
- fake_dbus_debugd_proxy_(reinterpret_cast<DBusGProxy*>(2)) {
+ : service_interface_mock_(new LibCrosServiceInterfaceProxyMock()),
+ ue_proxy_resolved_interface_mock_(
+ new NiceMock<UpdateEngineLibcrosProxyResolvedInterfaceProxyMock>()),
+ libcros_proxy_(
+ chromeos::make_unique_ptr(service_interface_mock_),
+ chromeos::make_unique_ptr(ue_proxy_resolved_interface_mock_)) {
// Override system state members.
fake_system_state_.set_connection_manager(&mock_connection_manager);
fake_system_state_.set_update_attempter(&attempter_);
@@ -110,18 +108,14 @@
// Finish initializing the attempter.
attempter_.Init();
-
- // We set the set_good_kernel command to a non-existent path so it fails to
- // run it. This avoids the async call to the command and continues the
- // update process right away. Tests testing that behavior can override the
- // default set_good_kernel command if needed.
- attempter_.set_good_kernel_cmd_ = "/path/to/non-existent/command";
+ // Don't run setgoodkernel command.
+ attempter_.skip_set_good_kernel_ = true;
}
void SetUp() override {
CHECK(utils::MakeTempDirectory("UpdateAttempterTest-XXXXXX", &test_dir_));
- EXPECT_EQ(nullptr, attempter_.dbus_service_);
+ EXPECT_EQ(nullptr, attempter_.dbus_adaptor_);
EXPECT_NE(nullptr, attempter_.system_state_);
EXPECT_EQ(0, attempter_.http_response_code_);
EXPECT_EQ(utils::kCpuSharesNormal, attempter_.shares_);
@@ -151,21 +145,10 @@
EXPECT_CALL(*fake_system_state_.mock_payload_state(),
GetUsingP2PForDownloading())
.WillRepeatedly(ReturnPointee(&actual_using_p2p_for_sharing_));
-
- // Set up mock debugd access over the system D-Bus. ProxyCall_0_1() also
- // needs to be mocked in any test using debugd to provide the desired value.
- ON_CALL(dbus_, BusGet(DBUS_BUS_SYSTEM, _))
- .WillByDefault(Return(fake_dbus_system_bus_));
- ON_CALL(dbus_, ProxyNewForName(fake_dbus_system_bus_,
- StrEq(debugd::kDebugdServiceName),
- StrEq(debugd::kDebugdServicePath),
- StrEq(debugd::kDebugdInterface)))
- .WillByDefault(Return(fake_dbus_debugd_proxy_));
}
void TearDown() override {
test_utils::RecursiveUnlinkDir(test_dir_);
- EXPECT_EQ(0, MessageLoopRunMaxIterations(&loop_, 1));
}
public:
@@ -193,20 +176,23 @@
return actual_using_p2p_for_sharing_;
}
- // TODO(deymo): Replace this with a FakeMessageLoop. Some of these tests use a
- // real LibcurlHttpFetcher, which still requires a GlibMessageLoop.
- chromeos::GlibMessageLoop loop_;
+ base::MessageLoopForIO base_loop_;
+ chromeos::BaseMessageLoop loop_{&base_loop_};
FakeSystemState fake_system_state_;
- NiceMock<MockDBusWrapper> dbus_;
- UpdateAttempterUnderTest attempter_;
+ org::chromium::debugdProxyMock debugd_proxy_mock_;
+ LibCrosServiceInterfaceProxyMock* service_interface_mock_;
+ UpdateEngineLibcrosProxyResolvedInterfaceProxyMock*
+ ue_proxy_resolved_interface_mock_;
+ LibCrosProxy libcros_proxy_;
+ UpdateAttempterUnderTest attempter_{&fake_system_state_,
+ &libcros_proxy_,
+ &debugd_proxy_mock_,
+ ""};
+
NiceMock<MockActionProcessor>* processor_;
NiceMock<MockPrefs>* prefs_; // Shortcut to fake_system_state_->mock_prefs().
NiceMock<MockConnectionManager> mock_connection_manager;
- // fake_dbus_xxx pointers will be non-null for comparison purposes, but won't
- // be valid objects so don't try to use them.
- DBusGConnection* fake_dbus_system_bus_;
- DBusGProxy* fake_dbus_debugd_proxy_;
string test_dir_;
@@ -257,15 +243,18 @@
ASSERT_TRUE(attempter_.error_event_.get() == nullptr);
}
-TEST_F(UpdateAttempterTest, RunAsRootConstructWithUpdatedMarkerTest) {
+TEST_F(UpdateAttempterTest, ConstructWithUpdatedMarkerTest) {
string test_update_completed_marker;
CHECK(utils::MakeTempFile(
- "update_attempter_unittest-update_completed_marker-XXXXXX",
- &test_update_completed_marker, nullptr));
+ "update_attempter_unittest-update_completed_marker-XXXXXX",
+ &test_update_completed_marker,
+ nullptr));
ScopedPathUnlinker completed_marker_unlinker(test_update_completed_marker);
const base::FilePath marker(test_update_completed_marker);
EXPECT_EQ(0, base::WriteFile(marker, "", 0));
- UpdateAttempterUnderTest attempter(&fake_system_state_, &dbus_,
+ UpdateAttempterUnderTest attempter(&fake_system_state_,
+ nullptr,
+ &debugd_proxy_mock_,
test_update_completed_marker);
EXPECT_EQ(UPDATE_STATUS_UPDATED_NEED_REBOOT, attempter.status());
}
@@ -514,9 +503,7 @@
}
TEST_F(UpdateAttempterTest, UpdateTest) {
- loop_.PostTask(FROM_HERE,
- base::Bind(&UpdateAttempterTest::UpdateTestStart,
- base::Unretained(this)));
+ UpdateTestStart();
loop_.Run();
}
@@ -938,8 +925,10 @@
TEST_F(UpdateAttempterTest, BootTimeInUpdateMarkerFile) {
const string update_completed_marker = test_dir_ + "/update-completed-marker";
- UpdateAttempterUnderTest attempter(&fake_system_state_, &dbus_,
- update_completed_marker);
+ UpdateAttempterUnderTest attempter{&fake_system_state_,
+ nullptr, // libcros_proxy
+ &debugd_proxy_mock_,
+ update_completed_marker};
FakeClock fake_clock;
fake_clock.SetBootTime(Time::FromTimeT(42));
@@ -962,11 +951,8 @@
TEST_F(UpdateAttempterTest, AnyUpdateSourceAllowedOfficialDevmode) {
fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
fake_system_state_.fake_hardware()->SetIsNormalBootMode(false);
- EXPECT_CALL(dbus_, ProxyCall_0_1(fake_dbus_debugd_proxy_,
- StrEq(debugd::kQueryDevFeatures),
- _, A<gint*>()))
- .WillRepeatedly(DoAll(SetArgumentPointee<3>(0),
- Return(true)));
+ EXPECT_CALL(debugd_proxy_mock_, QueryDevFeatures(_, _, _))
+ .WillRepeatedly(DoAll(SetArgumentPointee<0>(0), Return(true)));
EXPECT_TRUE(attempter_.IsAnyUpdateSourceAllowed());
}
@@ -974,10 +960,7 @@
fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
fake_system_state_.fake_hardware()->SetIsNormalBootMode(true);
// debugd should not be queried in this case.
- EXPECT_CALL(dbus_, ProxyCall_0_1(fake_dbus_debugd_proxy_,
- StrEq(debugd::kQueryDevFeatures),
- _, A<gint*>()))
- .Times(0);
+ EXPECT_CALL(debugd_proxy_mock_, QueryDevFeatures(_, _, _)).Times(0);
EXPECT_FALSE(attempter_.IsAnyUpdateSourceAllowed());
}
@@ -985,20 +968,16 @@
using debugd::DEV_FEATURES_DISABLED;
fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
fake_system_state_.fake_hardware()->SetIsNormalBootMode(false);
- EXPECT_CALL(dbus_, ProxyCall_0_1(fake_dbus_debugd_proxy_,
- StrEq(debugd::kQueryDevFeatures),
- _, A<gint*>()))
- .WillRepeatedly(DoAll(SetArgumentPointee<3>(DEV_FEATURES_DISABLED),
- Return(true)));
+ EXPECT_CALL(debugd_proxy_mock_, QueryDevFeatures(_, _, _))
+ .WillRepeatedly(
+ DoAll(SetArgumentPointee<0>(DEV_FEATURES_DISABLED), Return(true)));
EXPECT_FALSE(attempter_.IsAnyUpdateSourceAllowed());
}
TEST_F(UpdateAttempterTest, AnyUpdateSourceDisallowedDebugdFailure) {
fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
fake_system_state_.fake_hardware()->SetIsNormalBootMode(false);
- EXPECT_CALL(dbus_, ProxyCall_0_1(fake_dbus_debugd_proxy_,
- StrEq(debugd::kQueryDevFeatures),
- _, A<gint*>()))
+ EXPECT_CALL(debugd_proxy_mock_, QueryDevFeatures(_, _, _))
.WillRepeatedly(Return(false));
EXPECT_FALSE(attempter_.IsAnyUpdateSourceAllowed());
}
diff --git a/update_engine.gyp b/update_engine.gyp
index d3232a3..9bead7b 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -58,19 +58,40 @@
],
'includes': ['../common-mk/protoc.gypi'],
},
- # D-Bus glib bindings.
+ # Chrome D-Bus bindings.
{
- 'target_name': 'update_engine-dbus-server',
+ 'target_name': 'update_engine-dbus-adaptor',
'type': 'none',
'variables': {
- 'dbus_glib_type': 'server',
- 'dbus_glib_out_dir': 'include/update_engine',
- 'dbus_glib_prefix': 'update_engine_service',
+ 'dbus_adaptors_out_dir': 'include/update_engine/dbus_adaptor',
},
'sources': [
'dbus_bindings/org.chromium.UpdateEngineInterface.xml',
],
- 'includes': ['../common-mk/dbus_glib.gypi'],
+ 'includes': ['../common-mk/generate-dbus-adaptors.gypi'],
+ },
+ {
+ 'target_name': 'update_engine-dbus-proxies',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'update_engine-dbus-proxies-action',
+ 'variables': {
+ 'dbus_service_config': '',
+ 'mock_output_file': 'include/update_engine/dbus_mocks.h',
+ 'proxy_output_file': 'include/update_engine/dbus_proxies.h'
+ },
+ 'sources': [
+ '../debugd/share/org.chromium.debugd.xml',
+ '../login_manager/org.chromium.SessionManagerInterface.xml',
+ '../power_manager/dbus_bindings/org.chromium.PowerManager.xml',
+ '../shill/dbus_bindings/org.chromium.flimflam.Manager.xml',
+ '../shill/dbus_bindings/org.chromium.flimflam.Service.xml',
+ 'dbus_bindings/org.chromium.LibCrosService.xml',
+ ],
+ 'includes': ['../common-mk/generate-dbus-proxies.gypi'],
+ },
+ ],
},
# The main static_library with all the code.
{
@@ -78,13 +99,12 @@
'type': 'static_library',
'dependencies': [
'update_metadata-protos',
+ 'update_engine-dbus-adaptor',
+ 'update_engine-dbus-proxies',
],
'variables': {
'exported_deps': [
'dbus-1',
- 'dbus-glib-1',
- 'glib-2.0',
- 'gthread-2.0',
'libchrome-<(libbase_ver)',
'libchromeos-<(libbase_ver)',
'libcrypto',
@@ -125,6 +145,7 @@
'clock.cc',
'connection_manager.cc',
'constants.cc',
+ 'daemon.cc',
'dbus_service.cc',
'delta_performer.cc',
'download_action.cc',
@@ -132,12 +153,12 @@
'file_descriptor.cc',
'file_writer.cc',
'filesystem_verifier_action.cc',
- 'glib_utils.cc',
'hardware.cc',
'http_common.cc',
'http_fetcher.cc',
'hwid_override.cc',
'install_plan.cc',
+ 'libcros_proxy.cc',
'libcurl_http_fetcher.cc',
'metrics.cc',
'multi_range_http_fetcher.cc',
@@ -153,6 +174,7 @@
'prefs.cc',
'proxy_resolver.cc',
'real_system_state.cc',
+ 'shill_proxy.cc',
'subprocess.cc',
'terminator.cc',
'update_attempter.cc',
@@ -191,7 +213,6 @@
'type': 'executable',
'dependencies': [
'libupdate_engine',
- 'update_engine-dbus-server',
],
'sources': [
'main.cc',
@@ -220,10 +241,10 @@
],
'actions': [
{
- 'action_name': 'update_engine-dbus-proxies',
+ 'action_name': 'update_engine_client-dbus-proxies',
'variables': {
'dbus_service_config': 'dbus_bindings/dbus-service-config.json',
- 'proxy_output_file': 'include/update_engine/dbus_proxies.h'
+ 'proxy_output_file': 'include/update_engine/client_dbus_proxies.h'
},
'sources': [
'dbus_bindings/org.chromium.UpdateEngineInterface.xml',
@@ -266,6 +287,8 @@
'sources': [
'payload_generator/ab_generator.cc',
'payload_generator/annotated_operation.cc',
+ 'payload_generator/blob_file_writer.cc',
+ 'payload_generator/block_mapping.cc',
'payload_generator/cycle_breaker.cc',
'payload_generator/delta_diff_generator.cc',
'payload_generator/delta_diff_utils.cc',
@@ -320,7 +343,7 @@
],
'includes': ['../common-mk/openssl_pem.gypi'],
},
- # Sample images used for testing.
+ # Unpacks sample images used for testing.
{
'target_name': 'update_engine-test_images',
'type': 'none',
@@ -328,11 +351,9 @@
'image_out_dir': '.',
},
'sources': [
- 'sample_images/disk_ext2_1k.txt',
- 'sample_images/disk_ext2_4k.txt',
- 'sample_images/disk_ext2_ue_settings.txt',
+ 'sample_images/sample_images.tar.bz2',
],
- 'includes': ['generate_image.gypi'],
+ 'includes': ['tar_bunzip2.gypi'],
},
# Test HTTP Server.
{
@@ -367,10 +388,12 @@
'certificate_checker_unittest.cc',
'chrome_browser_proxy_resolver_unittest.cc',
'connection_manager_unittest.cc',
+ 'dbus_service_unittest.cc',
'delta_performer_unittest.cc',
'download_action_unittest.cc',
'extent_writer_unittest.cc',
'fake_prefs.cc',
+ 'fake_shill_proxy.cc',
'fake_system_state.cc',
'file_writer_unittest.cc',
'filesystem_verifier_action_unittest.cc',
@@ -383,6 +406,8 @@
'omaha_response_handler_action_unittest.cc',
'p2p_manager_unittest.cc',
'payload_generator/ab_generator_unittest.cc',
+ 'payload_generator/blob_file_writer_unittest.cc',
+ 'payload_generator/block_mapping_unittest.cc',
'payload_generator/cycle_breaker_unittest.cc',
'payload_generator/delta_diff_utils_unittest.cc',
'payload_generator/ext2_filesystem_unittest.cc',
diff --git a/update_engine_client.cc b/update_engine_client.cc
index d0fee7a..a2470f3 100644
--- a/update_engine_client.cc
+++ b/update_engine_client.cc
@@ -17,8 +17,8 @@
#include <chromeos/flag_helper.h>
#include <dbus/bus.h>
+#include "update_engine/client_dbus_proxies.h"
#include "update_engine/dbus_constants.h"
-#include "update_engine/dbus_proxies.h"
using chromeos_update_engine::kAttemptUpdateFlagNonInteractive;
using chromeos_update_engine::kUpdateEngineServiceName;
diff --git a/update_manager/real_device_policy_provider.cc b/update_manager/real_device_policy_provider.cc
index 6f484f7..3586cc8 100644
--- a/update_manager/real_device_policy_provider.cc
+++ b/update_manager/real_device_policy_provider.cc
@@ -12,7 +12,6 @@
#include <chromeos/dbus/service_constants.h>
#include <policy/device_policy.h>
-#include "update_engine/glib_utils.h"
#include "update_engine/update_manager/generic_variables.h"
#include "update_engine/update_manager/real_shill_provider.h"
#include "update_engine/utils.h"
@@ -33,12 +32,6 @@
RealDevicePolicyProvider::~RealDevicePolicyProvider() {
MessageLoop::current()->CancelTask(scheduled_refresh_);
- // Detach signal handler, free manager proxy.
- dbus_->ProxyDisconnectSignal(manager_proxy_,
- login_manager::kPropertyChangeCompleteSignal,
- G_CALLBACK(HandlePropertyChangedCompletedStatic),
- this);
- dbus_->ProxyUnref(manager_proxy_);
}
bool RealDevicePolicyProvider::Init() {
@@ -49,38 +42,39 @@
// We also listen for signals from the session manager to force a device
// policy refresh.
- GError* error = nullptr;
- DBusGConnection* connection = dbus_->BusGet(DBUS_BUS_SYSTEM, &error);
- if (!connection) {
- LOG(ERROR) << "Failed to initialize DBus connection: "
- << chromeos_update_engine::utils::GetAndFreeGError(&error);
- return false;
- }
- manager_proxy_ = dbus_->ProxyNewForName(
- connection,
- login_manager::kSessionManagerServiceName,
- login_manager::kSessionManagerServicePath,
- login_manager::kSessionManagerInterface);
-
- // Subscribe to the session manager's PropertyChangeComplete signal.
- dbus_->ProxyAddSignal_1(manager_proxy_,
- login_manager::kPropertyChangeCompleteSignal,
- G_TYPE_STRING);
- dbus_->ProxyConnectSignal(manager_proxy_,
- login_manager::kPropertyChangeCompleteSignal,
- G_CALLBACK(HandlePropertyChangedCompletedStatic),
- this, nullptr);
+ session_manager_proxy_->RegisterPropertyChangeCompleteSignalHandler(
+ base::Bind(&RealDevicePolicyProvider::OnPropertyChangedCompletedSignal,
+ base::Unretained(this)),
+ base::Bind(&RealDevicePolicyProvider::OnSignalConnected,
+ base::Unretained(this)));
return true;
}
-// static
-void RealDevicePolicyProvider::HandlePropertyChangedCompletedStatic(
- DBusGProxy* proxy, const char* /* payload */, void* data) {
+void RealDevicePolicyProvider::OnPropertyChangedCompletedSignal(
+ const string& success) {
+ if (success != "success") {
+ LOG(WARNING) << "Received device policy updated signal with a failure.";
+ }
// We refresh the policy file even if the payload string is kSignalFailure.
- RealDevicePolicyProvider* policy_provider =
- reinterpret_cast<RealDevicePolicyProvider*>(data);
- LOG(INFO) << "Reloading device policy due to signal received.";
- policy_provider->RefreshDevicePolicy();
+ LOG(INFO) << "Reloading and re-scheduling device policy due to signal "
+ "received.";
+ MessageLoop::current()->CancelTask(scheduled_refresh_);
+ scheduled_refresh_ = MessageLoop::kTaskIdNull;
+ RefreshDevicePolicyAndReschedule();
+}
+
+void RealDevicePolicyProvider::OnSignalConnected(const string& interface_name,
+ const string& signal_name,
+ bool successful) {
+ if (!successful) {
+ LOG(WARNING) << "We couldn't connect to SessionManager signal for updates "
+ "on the device policy blob. We will reload the policy file "
+ "periodically.";
+ }
+ // We do a one-time refresh of the DevicePolicy just in case we missed a
+ // signal between the first refresh and the time the signal handler was
+ // actually connected.
+ RefreshDevicePolicy();
}
void RealDevicePolicyProvider::RefreshDevicePolicyAndReschedule() {
diff --git a/update_manager/real_device_policy_provider.h b/update_manager/real_device_policy_provider.h
index b052d59..41b67c2 100644
--- a/update_manager/real_device_policy_provider.h
+++ b/update_manager/real_device_policy_provider.h
@@ -12,7 +12,7 @@
#include <gtest/gtest_prod.h> // for FRIEND_TEST
#include <policy/libpolicy.h>
-#include "update_engine/dbus_wrapper_interface.h"
+#include "update_engine/dbus_proxies.h"
#include "update_engine/update_manager/device_policy_provider.h"
#include "update_engine/update_manager/generic_variables.h"
@@ -21,11 +21,11 @@
// DevicePolicyProvider concrete implementation.
class RealDevicePolicyProvider : public DevicePolicyProvider {
public:
- RealDevicePolicyProvider(
- chromeos_update_engine::DBusWrapperInterface* const dbus,
- policy::PolicyProvider* policy_provider)
+ RealDevicePolicyProvider(org::chromium::SessionManagerInterfaceProxyInterface*
+ session_manager_proxy,
+ policy::PolicyProvider* policy_provider)
: policy_provider_(policy_provider),
- dbus_(dbus) {}
+ session_manager_proxy_(session_manager_proxy) {}
~RealDevicePolicyProvider();
// Initializes the provider and returns whether it succeeded.
@@ -79,9 +79,13 @@
// A static handler for the PropertyChangedCompleted signal from the session
// manager used as a callback.
- static void HandlePropertyChangedCompletedStatic(DBusGProxy* proxy,
- const char* payload,
- void* data);
+ void OnPropertyChangedCompletedSignal(const std::string& success);
+
+ // Called when the signal in UpdateEngineLibcrosProxyResolvedInterface is
+ // connected.
+ void OnSignalConnected(const std::string& interface_name,
+ const std::string& signal_name,
+ bool successful);
// Schedules a call to periodically refresh the device policy.
void RefreshDevicePolicyAndReschedule();
@@ -116,12 +120,12 @@
policy::PolicyProvider* policy_provider_;
// Used to schedule refreshes of the device policy.
- chromeos::MessageLoop::TaskId scheduled_refresh_ =
- chromeos::MessageLoop::kTaskIdNull;
+ chromeos::MessageLoop::TaskId scheduled_refresh_{
+ chromeos::MessageLoop::kTaskIdNull};
- // The DBus interface (mockable) and a session manager proxy.
- chromeos_update_engine::DBusWrapperInterface* const dbus_;
- DBusGProxy* manager_proxy_ = nullptr;
+ // The DBus (mockable) session manager proxy, owned by the caller.
+ org::chromium::SessionManagerInterfaceProxyInterface* session_manager_proxy_{
+ nullptr};
// Variable exposing whether the policy is loaded.
AsyncCopyVariable<bool> var_device_policy_is_loaded_{
diff --git a/update_manager/real_device_policy_provider_unittest.cc b/update_manager/real_device_policy_provider_unittest.cc
index 75d7c48..e909b9c 100644
--- a/update_manager/real_device_policy_provider_unittest.cc
+++ b/update_manager/real_device_policy_provider_unittest.cc
@@ -10,17 +10,19 @@
#include <chromeos/message_loops/fake_message_loop.h>
#include <chromeos/message_loops/message_loop.h>
#include <chromeos/message_loops/message_loop_utils.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <policy/mock_device_policy.h>
#include <policy/mock_libpolicy.h>
-#include "update_engine/mock_dbus_wrapper.h"
+#include "update_engine/dbus_mocks.h"
+#include "update_engine/dbus_test_utils.h"
#include "update_engine/test_utils.h"
#include "update_engine/update_manager/umtest_utils.h"
using base::TimeDelta;
using chromeos::MessageLoop;
-using chromeos::MessageLoopRunMaxIterations;
+using chromeos_update_engine::dbus_test_utils::MockSignalHandler;
using std::set;
using std::string;
using std::unique_ptr;
@@ -28,50 +30,31 @@
using testing::Mock;
using testing::Return;
using testing::ReturnRef;
-using testing::SaveArg;
-using testing::SetArgumentPointee;
-using testing::StrEq;
+using testing::SetArgPointee;
using testing::_;
-namespace {
-
-// Fake dbus-glib objects. These should be different values, to ease diagnosis
-// of errors.
-DBusGConnection* const kFakeConnection = reinterpret_cast<DBusGConnection*>(1);
-DBusGProxy* const kFakeManagerProxy = reinterpret_cast<DBusGProxy*>(2);
-
-} // namespace
-
namespace chromeos_update_manager {
class UmRealDevicePolicyProviderTest : public ::testing::Test {
protected:
void SetUp() override {
loop_.SetAsCurrent();
- provider_.reset(new RealDevicePolicyProvider(&mock_dbus_,
+ provider_.reset(new RealDevicePolicyProvider(&session_manager_proxy_mock_,
&mock_policy_provider_));
// By default, we have a device policy loaded. Tests can call
// SetUpNonExistentDevicePolicy() to override this.
SetUpExistentDevicePolicy();
- SetUpDBusSignalExpectations();
+ // Setup the session manager_proxy such that it will accept the signal
+ // handler and store it in the |property_change_complete_| once registered.
+ MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER(property_change_complete_,
+ session_manager_proxy_mock_,
+ PropertyChangeComplete);
}
void TearDown() override {
- // Check for leaked callbacks on the main loop.
- EXPECT_EQ(0, MessageLoopRunMaxIterations(MessageLoop::current(), 100));
-
- // We need to set these expectation before the object is destroyed but
- // after it finished running the test so the values of signal_callback_ and
- // signal_callback_data_ are correct.
- EXPECT_CALL(mock_dbus_, ProxyDisconnectSignal(
- kFakeManagerProxy,
- StrEq(login_manager::kPropertyChangeCompleteSignal),
- signal_callback_,
- signal_callback_data_));
- EXPECT_CALL(mock_dbus_, ProxyUnref(kFakeManagerProxy));
-
provider_.reset();
+ // Check for leaked callbacks on the main loop.
EXPECT_FALSE(loop_.PendingTasks());
}
@@ -93,58 +76,40 @@
.WillByDefault(ReturnRef(mock_device_policy_));
}
- void SetUpDBusSignalExpectations() {
- // Setup the DBus connection with default actions that should be performed
- // once.
- EXPECT_CALL(mock_dbus_, BusGet(_, _)).WillOnce(
- Return(kFakeConnection));
- EXPECT_CALL(mock_dbus_, ProxyNewForName(
- kFakeConnection, StrEq(login_manager::kSessionManagerServiceName),
- StrEq(login_manager::kSessionManagerServicePath),
- StrEq(login_manager::kSessionManagerInterface)))
- .WillOnce(Return(kFakeManagerProxy));
-
- // Expect the signal to be added, registered and released.
- EXPECT_CALL(mock_dbus_, ProxyAddSignal_1(
- kFakeManagerProxy,
- StrEq(login_manager::kPropertyChangeCompleteSignal),
- G_TYPE_STRING));
- EXPECT_CALL(mock_dbus_, ProxyConnectSignal(
- kFakeManagerProxy,
- StrEq(login_manager::kPropertyChangeCompleteSignal),
- _ /* callback */, _ /* data */, _ /* free function */))
- .WillOnce(DoAll(SaveArg<2>(&signal_callback_),
- SaveArg<3>(&signal_callback_data_)));
- }
-
chromeos::FakeMessageLoop loop_{nullptr};
- chromeos_update_engine::MockDBusWrapper mock_dbus_;
+ org::chromium::SessionManagerInterfaceProxyMock session_manager_proxy_mock_;
testing::NiceMock<policy::MockDevicePolicy> mock_device_policy_;
testing::NiceMock<policy::MockPolicyProvider> mock_policy_provider_;
unique_ptr<RealDevicePolicyProvider> provider_;
// The registered signal handler for the signal.
- GCallback signal_callback_ = nullptr;
- void* signal_callback_data_ = nullptr;
+ MockSignalHandler<void(const string&)> property_change_complete_;
};
TEST_F(UmRealDevicePolicyProviderTest, RefreshScheduledTest) {
- // Check that the RefreshPolicy gets scheduled by checking the EventId.
+ // Check that the RefreshPolicy gets scheduled by checking the TaskId.
EXPECT_TRUE(provider_->Init());
EXPECT_NE(MessageLoop::kTaskIdNull, provider_->scheduled_refresh_);
+ loop_.RunOnce(false);
}
TEST_F(UmRealDevicePolicyProviderTest, FirstReload) {
- // Checks that the policy is reloaded and the DevicePolicy is consulted.
+ // Checks that the policy is reloaded and the DevicePolicy is consulted twice:
+ // once on Init() and once again when the signal is connected.
EXPECT_CALL(mock_policy_provider_, Reload());
EXPECT_TRUE(provider_->Init());
+ Mock::VerifyAndClearExpectations(&mock_policy_provider_);
+
+ EXPECT_CALL(mock_policy_provider_, Reload());
+ loop_.RunOnce(false);
}
TEST_F(UmRealDevicePolicyProviderTest, NonExistentDevicePolicyReloaded) {
// Checks that the policy is reloaded by RefreshDevicePolicy().
SetUpNonExistentDevicePolicy();
- EXPECT_CALL(mock_policy_provider_, Reload()).Times(2);
+ EXPECT_CALL(mock_policy_provider_, Reload()).Times(3);
EXPECT_TRUE(provider_->Init());
+ loop_.RunOnce(false);
// Force the policy refresh.
provider_->RefreshDevicePolicy();
}
@@ -154,22 +119,19 @@
SetUpNonExistentDevicePolicy();
EXPECT_CALL(mock_policy_provider_, Reload()).Times(2);
EXPECT_TRUE(provider_->Init());
+ loop_.RunOnce(false);
+ Mock::VerifyAndClearExpectations(&mock_policy_provider_);
- ASSERT_NE(nullptr, signal_callback_);
- // Convert the GCallback to a function pointer and call it. GCallback is just
- // a void function pointer to ensure that the type of the passed callback is a
- // pointer. We need to cast it back to the right function type before calling
- // it.
- typedef void (*StaticSignalHandler)(DBusGProxy*, const char*, void*);
- StaticSignalHandler signal_handler = reinterpret_cast<StaticSignalHandler>(
- signal_callback_);
- (*signal_handler)(kFakeManagerProxy, "success", signal_callback_data_);
+ EXPECT_CALL(mock_policy_provider_, Reload());
+ ASSERT_TRUE(property_change_complete_.IsHandlerRegistered());
+ property_change_complete_.signal_callback().Run("success");
}
TEST_F(UmRealDevicePolicyProviderTest, NonExistentDevicePolicyEmptyVariables) {
SetUpNonExistentDevicePolicy();
EXPECT_CALL(mock_policy_provider_, GetDevicePolicy()).Times(0);
EXPECT_TRUE(provider_->Init());
+ loop_.RunOnce(false);
UmTestUtils::ExpectVariableHasValue(false,
provider_->var_device_policy_is_loaded());
@@ -189,14 +151,14 @@
TEST_F(UmRealDevicePolicyProviderTest, ValuesUpdated) {
SetUpNonExistentDevicePolicy();
EXPECT_TRUE(provider_->Init());
+ loop_.RunOnce(false);
Mock::VerifyAndClearExpectations(&mock_policy_provider_);
// Reload the policy with a good one and set some values as present. The
// remaining values are false.
SetUpExistentDevicePolicy();
EXPECT_CALL(mock_device_policy_, GetReleaseChannel(_))
- .WillOnce(DoAll(SetArgumentPointee<0>(string("mychannel")),
- Return(true)));
+ .WillOnce(DoAll(SetArgPointee<0>(string("mychannel")), Return(true)));
EXPECT_CALL(mock_device_policy_, GetAllowedConnectionTypesForUpdate(_))
.WillOnce(Return(false));
@@ -215,8 +177,10 @@
TEST_F(UmRealDevicePolicyProviderTest, ScatterFactorConverted) {
SetUpExistentDevicePolicy();
EXPECT_CALL(mock_device_policy_, GetScatterFactorInSeconds(_))
- .WillOnce(DoAll(SetArgumentPointee<0>(1234), Return(true)));
+ .Times(2)
+ .WillRepeatedly(DoAll(SetArgPointee<0>(1234), Return(true)));
EXPECT_TRUE(provider_->Init());
+ loop_.RunOnce(false);
UmTestUtils::ExpectVariableHasValue(TimeDelta::FromSeconds(1234),
provider_->var_scatter_factor());
@@ -225,8 +189,10 @@
TEST_F(UmRealDevicePolicyProviderTest, NegativeScatterFactorIgnored) {
SetUpExistentDevicePolicy();
EXPECT_CALL(mock_device_policy_, GetScatterFactorInSeconds(_))
- .WillOnce(DoAll(SetArgumentPointee<0>(-1), Return(true)));
+ .Times(2)
+ .WillRepeatedly(DoAll(SetArgPointee<0>(-1), Return(true)));
EXPECT_TRUE(provider_->Init());
+ loop_.RunOnce(false);
UmTestUtils::ExpectVariableNotSet(provider_->var_scatter_factor());
}
@@ -234,10 +200,12 @@
TEST_F(UmRealDevicePolicyProviderTest, AllowedTypesConverted) {
SetUpExistentDevicePolicy();
EXPECT_CALL(mock_device_policy_, GetAllowedConnectionTypesForUpdate(_))
- .WillOnce(DoAll(SetArgumentPointee<0>(
- set<string>{"bluetooth", "wifi", "not-a-type"}),
- Return(true)));
+ .Times(2)
+ .WillRepeatedly(DoAll(
+ SetArgPointee<0>(set<string>{"bluetooth", "wifi", "not-a-type"}),
+ Return(true)));
EXPECT_TRUE(provider_->Init());
+ loop_.RunOnce(false);
UmTestUtils::ExpectVariableHasValue(
set<ConnectionType>{ConnectionType::kWifi, ConnectionType::kBluetooth},
diff --git a/update_manager/real_shill_provider.cc b/update_manager/real_shill_provider.cc
index 609fd01..dcf2eb2 100644
--- a/update_manager/real_shill_provider.cc
+++ b/update_manager/real_shill_provider.cc
@@ -10,182 +10,158 @@
#include <base/strings/stringprintf.h>
#include <chromeos/dbus/service_constants.h>
-#include "update_engine/glib_utils.h"
-
+using org::chromium::flimflam::ManagerProxyInterface;
+using org::chromium::flimflam::ServiceProxyInterface;
using std::string;
-namespace {
-
-// Looks up a |key| in a GLib |hash_table| and returns the unboxed string from
-// the corresponding GValue, if found.
-const char* GetStrProperty(GHashTable* hash_table, const char* key) {
- auto gval = reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table, key));
- return (gval ? g_value_get_string(gval) : nullptr);
-}
-
-}; // namespace
-
-
namespace chromeos_update_manager {
-RealShillProvider::~RealShillProvider() {
- // Detach signal handler, free manager proxy.
- dbus_->ProxyDisconnectSignal(manager_proxy_, shill::kMonitorPropertyChanged,
- G_CALLBACK(HandlePropertyChangedStatic),
- this);
- dbus_->ProxyUnref(manager_proxy_);
-}
-
-ConnectionType RealShillProvider::ParseConnectionType(const char* type_str) {
- if (!strcmp(type_str, shill::kTypeEthernet))
+ConnectionType RealShillProvider::ParseConnectionType(const string& type_str) {
+ if (type_str == shill::kTypeEthernet) {
return ConnectionType::kEthernet;
- if (!strcmp(type_str, shill::kTypeWifi))
+ } else if (type_str == shill::kTypeWifi) {
return ConnectionType::kWifi;
- if (!strcmp(type_str, shill::kTypeWimax))
+ } else if (type_str == shill::kTypeWimax) {
return ConnectionType::kWimax;
- if (!strcmp(type_str, shill::kTypeBluetooth))
+ } else if (type_str == shill::kTypeBluetooth) {
return ConnectionType::kBluetooth;
- if (!strcmp(type_str, shill::kTypeCellular))
+ } else if (type_str == shill::kTypeCellular) {
return ConnectionType::kCellular;
-
+ }
return ConnectionType::kUnknown;
}
ConnectionTethering RealShillProvider::ParseConnectionTethering(
- const char* tethering_str) {
- if (!strcmp(tethering_str, shill::kTetheringNotDetectedState))
+ const string& tethering_str) {
+ if (tethering_str == shill::kTetheringNotDetectedState) {
return ConnectionTethering::kNotDetected;
- if (!strcmp(tethering_str, shill::kTetheringSuspectedState))
+ } else if (tethering_str == shill::kTetheringSuspectedState) {
return ConnectionTethering::kSuspected;
- if (!strcmp(tethering_str, shill::kTetheringConfirmedState))
+ } else if (tethering_str == shill::kTetheringConfirmedState) {
return ConnectionTethering::kConfirmed;
-
+ }
return ConnectionTethering::kUnknown;
}
bool RealShillProvider::Init() {
- // Obtain a DBus connection.
- GError* error = nullptr;
- connection_ = dbus_->BusGet(DBUS_BUS_SYSTEM, &error);
- if (!connection_) {
- LOG(ERROR) << "Failed to initialize DBus connection: "
- << chromeos_update_engine::utils::GetAndFreeGError(&error);
+ ManagerProxyInterface* manager_proxy = shill_proxy_->GetManagerProxy();
+ if (!manager_proxy)
return false;
- }
-
- // Allocate a shill manager proxy.
- manager_proxy_ = GetProxy(shill::kFlimflamServicePath,
- shill::kFlimflamManagerInterface);
// Subscribe to the manager's PropertyChanged signal.
- dbus_->ProxyAddSignal_2(manager_proxy_, shill::kMonitorPropertyChanged,
- G_TYPE_STRING, G_TYPE_VALUE);
- dbus_->ProxyConnectSignal(manager_proxy_, shill::kMonitorPropertyChanged,
- G_CALLBACK(HandlePropertyChangedStatic),
- this, nullptr);
+ manager_proxy->RegisterPropertyChangedSignalHandler(
+ base::Bind(&RealShillProvider::OnManagerPropertyChanged,
+ base::Unretained(this)),
+ base::Bind(&RealShillProvider::OnSignalConnected,
+ base::Unretained(this)));
// Attempt to read initial connection status. Even if this fails because shill
// is not responding (e.g. it is down) we'll be notified via "PropertyChanged"
// signal as soon as it comes up, so this is not a critical step.
- GHashTable* hash_table = nullptr;
- if (GetProperties(manager_proxy_, &hash_table)) {
- GValue* value = reinterpret_cast<GValue*>(
- g_hash_table_lookup(hash_table, shill::kDefaultServiceProperty));
- ProcessDefaultService(value);
- g_hash_table_unref(hash_table);
+ chromeos::VariantDictionary properties;
+ chromeos::ErrorPtr error;
+ if (!manager_proxy->GetProperties(&properties, &error))
+ return true;
+
+ const auto& prop_default_service =
+ properties.find(shill::kDefaultServiceProperty);
+ if (prop_default_service != properties.end()) {
+ OnManagerPropertyChanged(prop_default_service->first,
+ prop_default_service->second);
}
return true;
}
-DBusGProxy* RealShillProvider::GetProxy(const char* path,
- const char* interface) {
- return dbus_->ProxyNewForName(connection_, shill::kFlimflamServiceName,
- path, interface);
+void RealShillProvider::OnManagerPropertyChanged(const string& name,
+ const chromeos::Any& value) {
+ if (name == shill::kDefaultServiceProperty)
+ ProcessDefaultService(value.TryGet<dbus::ObjectPath>().value());
}
-bool RealShillProvider::GetProperties(DBusGProxy* proxy,
- GHashTable** result_p) {
- GError* error = nullptr;
- if (!dbus_->ProxyCall_0_1(proxy, shill::kGetPropertiesFunction, &error,
- result_p)) {
- LOG(ERROR) << "Calling shill via DBus proxy failed: "
- << chromeos_update_engine::utils::GetAndFreeGError(&error);
- return false;
+void RealShillProvider::OnSignalConnected(const string& interface_name,
+ const string& signal_name,
+ bool successful) {
+ if (!successful) {
+ LOG(ERROR) << "Couldn't connect to the signal " << interface_name << "."
+ << signal_name;
}
- return true;
}
-bool RealShillProvider::ProcessDefaultService(GValue* value) {
- // Decode the string from the boxed value.
- const char* default_service_path_str = nullptr;
- if (!(value && (default_service_path_str = g_value_get_string(value))))
- return false;
-
- // Anything changed?
- if (default_service_path_ == default_service_path_str)
+bool RealShillProvider::ProcessDefaultService(
+ const string& default_service_path) {
+ // We assume that if the service path didn't change, then the connection
+ // type and the tethering status of it also didn't change.
+ if (default_service_path_ == default_service_path)
return true;
// Update the connection status.
- default_service_path_ = default_service_path_str;
+ default_service_path_ = default_service_path;
bool is_connected = (default_service_path_ != "/");
var_is_connected_.SetValue(is_connected);
var_conn_last_changed_.SetValue(clock_->GetWallclockTime());
- // Update the connection attributes.
- if (is_connected) {
- DBusGProxy* service_proxy = GetProxy(default_service_path_.c_str(),
- shill::kFlimflamServiceInterface);
- GHashTable* hash_table = nullptr;
- if (GetProperties(service_proxy, &hash_table)) {
- // Get the connection type.
- const char* type_str = GetStrProperty(hash_table, shill::kTypeProperty);
- if (type_str && !strcmp(type_str, shill::kTypeVPN)) {
- type_str = GetStrProperty(hash_table,
- shill::kPhysicalTechnologyProperty);
- }
- if (type_str) {
- var_conn_type_.SetValue(ParseConnectionType(type_str));
- } else {
- var_conn_type_.UnsetValue();
- LOG(ERROR) << "Could not find connection type ("
- << default_service_path_ << ")";
- }
-
- // Get the connection tethering mode.
- const char* tethering_str = GetStrProperty(hash_table,
- shill::kTetheringProperty);
- if (tethering_str) {
- var_conn_tethering_.SetValue(ParseConnectionTethering(tethering_str));
- } else {
- var_conn_tethering_.UnsetValue();
- LOG(ERROR) << "Could not find connection tethering mode ("
- << default_service_path_ << ")";
- }
-
- g_hash_table_unref(hash_table);
- }
- dbus_->ProxyUnref(service_proxy);
- } else {
+ if (!is_connected) {
var_conn_type_.UnsetValue();
var_conn_tethering_.UnsetValue();
+ return true;
+ }
+
+ // We create and dispose the ServiceProxyInterface on every request.
+ std::unique_ptr<ServiceProxyInterface> service =
+ shill_proxy_->GetServiceForPath(default_service_path_);
+
+ // Get the connection properties synchronously.
+ chromeos::VariantDictionary properties;
+ chromeos::ErrorPtr error;
+ if (!service->GetProperties(&properties, &error)) {
+ var_conn_type_.UnsetValue();
+ var_conn_tethering_.UnsetValue();
+ return false;
+ }
+
+ // Get the connection tethering mode.
+ const auto& prop_tethering = properties.find(shill::kTetheringProperty);
+ if (prop_tethering == properties.end()) {
+ // Remove the value if not present on the service. This most likely means an
+ // error in shill and the policy will handle it, but we will print a log
+ // message as well for accessing an unused variable.
+ var_conn_tethering_.UnsetValue();
+ LOG(ERROR) << "Could not find connection type (" << default_service_path_
+ << ")";
+ } else {
+ // If the property doesn't contain a string value, the empty string will
+ // become kUnknown.
+ var_conn_tethering_.SetValue(
+ ParseConnectionTethering(prop_tethering->second.TryGet<string>()));
+ }
+
+ // Get the connection type.
+ const auto& prop_type = properties.find(shill::kTypeProperty);
+ if (prop_type == properties.end()) {
+ var_conn_type_.UnsetValue();
+ LOG(ERROR) << "Could not find connection tethering mode ("
+ << default_service_path_ << ")";
+ } else {
+ string type_str = prop_type->second.TryGet<string>();
+ if (type_str == shill::kTypeVPN) {
+ const auto& prop_physical =
+ properties.find(shill::kPhysicalTechnologyProperty);
+ if (prop_physical == properties.end()) {
+ LOG(ERROR) << "No PhysicalTechnology property found for a VPN"
+ << " connection (service: " << default_service_path
+ << "). Using default kUnknown value.";
+ var_conn_type_.SetValue(ConnectionType::kUnknown);
+ } else {
+ var_conn_type_.SetValue(
+ ParseConnectionType(prop_physical->second.TryGet<string>()));
+ }
+ } else {
+ var_conn_type_.SetValue(ParseConnectionType(type_str));
+ }
}
return true;
}
-void RealShillProvider::HandlePropertyChanged(DBusGProxy* proxy,
- const char* name, GValue* value) {
- if (!strcmp(name, shill::kDefaultServiceProperty))
- ProcessDefaultService(value);
-}
-
-void RealShillProvider::HandlePropertyChangedStatic(DBusGProxy* proxy,
- const char* name,
- GValue* value,
- void* data) {
- auto obj = reinterpret_cast<RealShillProvider*>(data);
- obj->HandlePropertyChanged(proxy, name, value);
-}
-
} // namespace chromeos_update_manager
diff --git a/update_manager/real_shill_provider.h b/update_manager/real_shill_provider.h
index 5b50bf4..5864319 100644
--- a/update_manager/real_shill_provider.h
+++ b/update_manager/real_shill_provider.h
@@ -14,22 +14,21 @@
#include <base/time/time.h>
#include "update_engine/clock_interface.h"
-#include "update_engine/dbus_wrapper_interface.h"
+#include "update_engine/dbus_proxies.h"
+#include "update_engine/shill_proxy_interface.h"
#include "update_engine/update_manager/generic_variables.h"
#include "update_engine/update_manager/shill_provider.h"
-using chromeos_update_engine::ClockInterface;
-using chromeos_update_engine::DBusWrapperInterface;
-
namespace chromeos_update_manager {
// ShillProvider concrete implementation.
class RealShillProvider : public ShillProvider {
public:
- RealShillProvider(DBusWrapperInterface* dbus, ClockInterface* clock)
- : dbus_(dbus), clock_(clock) {}
+ RealShillProvider(chromeos_update_engine::ShillProxyInterface* shill_proxy,
+ chromeos_update_engine::ClockInterface* clock)
+ : shill_proxy_(shill_proxy), clock_(clock) {}
- ~RealShillProvider() override;
+ ~RealShillProvider() override = default;
// Initializes the provider and returns whether it succeeded.
bool Init();
@@ -51,37 +50,33 @@
}
// Helper methods for converting shill strings into symbolic values.
- static ConnectionType ParseConnectionType(const char* type_str);
+ static ConnectionType ParseConnectionType(const std::string& type_str);
static ConnectionTethering ParseConnectionTethering(
- const char* tethering_str);
+ const std::string& tethering_str);
private:
- // Return a DBus proxy for a given |path| and |interface| within shill.
- DBusGProxy* GetProxy(const char* path, const char* interface);
+ // A handler for ManagerProxy.PropertyChanged signal.
+ void OnManagerPropertyChanged(const std::string& name,
+ const chromeos::Any& value);
- // Issues a GetProperties call through a given |proxy|, storing the result to
- // |*result_p|. Returns true on success.
- bool GetProperties(DBusGProxy* proxy, GHashTable** result_p);
+ // Called when the signal in ManagerProxy.PropertyChanged is connected.
+ void OnSignalConnected(const std::string& interface_name,
+ const std::string& signal_name,
+ bool successful);
- // Process a default connection value, update last change time as needed.
- bool ProcessDefaultService(GValue* value);
-
- // A handler for manager PropertyChanged signal, and a static version.
- void HandlePropertyChanged(DBusGProxy* proxy, const char* name,
- GValue* value);
- static void HandlePropertyChangedStatic(DBusGProxy* proxy, const char* name,
- GValue* value, void* data);
+ // Get the connection and populate the type and tethering status of the given
+ // default connection.
+ bool ProcessDefaultService(const std::string& default_service_path);
// The current default service path, if connected.
std::string default_service_path_;
- // The DBus interface (mockable), connection, and a shill manager proxy.
- DBusWrapperInterface* const dbus_;
- DBusGConnection* connection_ = nullptr;
- DBusGProxy* manager_proxy_ = nullptr;
+ // The mockable interface to access the shill DBus proxies, owned by the
+ // caller.
+ chromeos_update_engine::ShillProxyInterface* shill_proxy_;
// A clock abstraction (mockable).
- ClockInterface* const clock_;
+ chromeos_update_engine::ClockInterface* const clock_;
// The provider's variables.
AsyncCopyVariable<bool> var_is_connected_{"is_connected"};
diff --git a/update_manager/real_shill_provider_unittest.cc b/update_manager/real_shill_provider_unittest.cc
index 78bdd93..5b81e91 100644
--- a/update_manager/real_shill_provider_unittest.cc
+++ b/update_manager/real_shill_provider_unittest.cc
@@ -1,7 +1,6 @@
// Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-
#include "update_engine/update_manager/real_shill_provider.h"
#include <memory>
@@ -9,46 +8,32 @@
#include <base/time/time.h>
#include <chromeos/dbus/service_constants.h>
-#include <glib.h>
+#include <chromeos/make_unique_ptr.h>
+#include <chromeos/message_loops/fake_message_loop.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include "update_engine/dbus_mocks.h"
+#include "update_engine/dbus_proxies.h"
+#include "update_engine/dbus_test_utils.h"
#include "update_engine/fake_clock.h"
-#include "update_engine/mock_dbus_wrapper.h"
+#include "update_engine/fake_shill_proxy.h"
#include "update_engine/test_utils.h"
#include "update_engine/update_manager/umtest_utils.h"
using base::Time;
using base::TimeDelta;
using chromeos_update_engine::FakeClock;
-using chromeos_update_engine::MockDBusWrapper;
-using chromeos_update_engine::test_utils::GValueFree;
-using chromeos_update_engine::test_utils::GValueNewString;
-using std::pair;
+using org::chromium::flimflam::ManagerProxyMock;
+using org::chromium::flimflam::ServiceProxyMock;
using std::unique_ptr;
-using testing::A;
-using testing::Eq;
using testing::Mock;
using testing::Return;
-using testing::SaveArg;
using testing::SetArgPointee;
-using testing::StrEq;
-using testing::StrictMock;
using testing::_;
namespace {
-// Fake dbus-glib objects. These should be different values, to ease diagnosis
-// of errors.
-DBusGConnection* const kFakeConnection = reinterpret_cast<DBusGConnection*>(1);
-DBusGProxy* const kFakeManagerProxy = reinterpret_cast<DBusGProxy*>(2);
-DBusGProxy* const kFakeEthernetServiceProxy = reinterpret_cast<DBusGProxy*>(3);
-DBusGProxy* const kFakeWifiServiceProxy = reinterpret_cast<DBusGProxy*>(4);
-DBusGProxy* const kFakeWimaxServiceProxy = reinterpret_cast<DBusGProxy*>(5);
-DBusGProxy* const kFakeBluetoothServiceProxy = reinterpret_cast<DBusGProxy*>(6);
-DBusGProxy* const kFakeCellularServiceProxy = reinterpret_cast<DBusGProxy*>(7);
-DBusGProxy* const kFakeVpnServiceProxy = reinterpret_cast<DBusGProxy*>(8);
-DBusGProxy* const kFakeUnknownServiceProxy = reinterpret_cast<DBusGProxy*>(9);
-
// Fake service paths.
const char* const kFakeEthernetServicePath = "/fake-ethernet-service";
const char* const kFakeWifiServicePath = "/fake-wifi-service";
@@ -64,89 +49,23 @@
class UmRealShillProviderTest : public ::testing::Test {
protected:
+ // Initialize the RealShillProvider under test.
void SetUp() override {
- // By default, initialize the provider so that it gets an initial connection
- // status from shill. This simulates the common case where shill is
- // available and responding during RealShillProvider initialization.
- Init(true);
+ fake_clock_.SetWallclockTime(InitTime());
+ loop_.SetAsCurrent();
+ provider_.reset(new RealShillProvider(&fake_shill_proxy_, &fake_clock_));
+
+ ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_.GetManagerProxy();
+
+ // The PropertyChanged signal should be subscribed to.
+ MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER(
+ manager_property_changed_, *manager_proxy_mock, PropertyChanged);
}
void TearDown() override {
- Shutdown();
- }
-
- // Initialize the RealShillProvider under test. If |do_init_conn_status| is
- // true, configure mocks to respond to the initial connection status check
- // with shill. Otherwise, the initial check will fail.
- void Init(bool do_init_conn_status) {
- // Properly shutdown a previously initialized provider.
- if (provider_.get())
- Shutdown();
-
- provider_.reset(new RealShillProvider(&mock_dbus_, &fake_clock_));
- ASSERT_NE(nullptr, provider_.get());
- fake_clock_.SetWallclockTime(InitTime());
-
- // A DBus connection should only be obtained once.
- EXPECT_CALL(mock_dbus_, BusGet(_, _)).WillOnce(
- Return(kFakeConnection));
-
- // A manager proxy should only be obtained once.
- EXPECT_CALL(mock_dbus_, ProxyNewForName(
- kFakeConnection, StrEq(shill::kFlimflamServiceName),
- StrEq(shill::kFlimflamServicePath),
- StrEq(shill::kFlimflamManagerInterface)))
- .WillOnce(Return(kFakeManagerProxy));
-
- // The PropertyChanged signal should be subscribed to.
- EXPECT_CALL(mock_dbus_, ProxyAddSignal_2(
- kFakeManagerProxy, StrEq(shill::kMonitorPropertyChanged),
- G_TYPE_STRING, G_TYPE_VALUE))
- .WillOnce(Return());
- EXPECT_CALL(mock_dbus_, ProxyConnectSignal(
- kFakeManagerProxy, StrEq(shill::kMonitorPropertyChanged),
- _, _, _))
- .WillOnce(
- DoAll(SaveArg<2>(reinterpret_cast<void (**)()>(&signal_handler_)),
- SaveArg<3>(&signal_data_),
- Return()));
-
- // Mock a response to an initial connection check (optional).
- GHashTable* manager_properties = nullptr;
- if (do_init_conn_status) {
- pair<const char*, const char*> manager_pairs[] = {
- {shill::kDefaultServiceProperty, "/"},
- };
- manager_properties = SetupGetPropertiesOkay(
- kFakeManagerProxy, arraysize(manager_pairs), manager_pairs);
- } else {
- SetupGetPropertiesFail(kFakeManagerProxy);
- }
-
- // Check that provider initializes correctly.
- ASSERT_TRUE(provider_->Init());
-
- // All mocked calls should have been exercised by now.
- Mock::VerifyAndClear(&mock_dbus_);
-
- // Release properties hash table (if provided).
- if (manager_properties)
- g_hash_table_unref(manager_properties);
- }
-
- // Deletes the RealShillProvider under test.
- void Shutdown() {
- // Make sure that DBus resources get freed.
- EXPECT_CALL(mock_dbus_, ProxyDisconnectSignal(
- kFakeManagerProxy, StrEq(shill::kMonitorPropertyChanged),
- Eq(reinterpret_cast<void (*)()>(signal_handler_)),
- Eq(signal_data_)))
- .WillOnce(Return());
- EXPECT_CALL(mock_dbus_, ProxyUnref(kFakeManagerProxy)).WillOnce(Return());
provider_.reset();
-
- // All mocked calls should have been exercised by now.
- Mock::VerifyAndClear(&mock_dbus_);
+ // Check for leaked callbacks on the main loop.
+ EXPECT_FALSE(loop_.PendingTasks());
}
// These methods generate fixed timestamps for use in faking the current time.
@@ -167,53 +86,41 @@
return InitTime() + TimeDelta::FromSeconds(10);
}
- // Sets up a successful mock "GetProperties" call on |proxy|, writing a hash
- // table containing |num_entries| entries formed by key/value pairs from
- // |key_val_pairs| and returning true. Keys and values are plain C strings
- // (const char*). The proxy call is expected to be made exactly once. Returns
- // a pointer to a newly allocated hash table, which should be unreffed with
- // g_hash_table_unref() when done.
- GHashTable* SetupGetPropertiesOkay(
- DBusGProxy* proxy, size_t num_entries,
- pair<const char*, const char*>* key_val_pairs) {
- // Allocate and populate the hash table.
- GHashTable* properties = g_hash_table_new_full(g_str_hash, g_str_equal,
- free, GValueFree);
- for (size_t i = 0; i < num_entries; i++) {
- g_hash_table_insert(properties, strdup(key_val_pairs[i].first),
- GValueNewString(key_val_pairs[i].second));
- }
+ // Sets the default_service object path in the response from the
+ // ManagerProxyMock instance.
+ void SetManagerReply(const char* default_service, bool reply_succeeds);
- // Set mock expectations.
- EXPECT_CALL(mock_dbus_,
- ProxyCall_0_1(proxy, StrEq(shill::kGetPropertiesFunction),
- _, A<GHashTable**>()))
- .WillOnce(DoAll(SetArgPointee<3>(g_hash_table_ref(properties)),
- Return(true)));
+ // Sets the |service_type|, |physical_technology| and |service_tethering|
+ // properties in the mocked service |service_path|. If any of the three
+ // const char* is a nullptr, the corresponding property will not be included
+ // in the response.
+ // Returns the mock object pointer, owned by the |fake_shill_proxy_|.
+ ServiceProxyMock* SetServiceReply(const std::string& service_path,
+ const char* service_type,
+ const char* physical_technology,
+ const char* service_tethering);
- return properties;
- }
-
- // Sets up a failing mock "GetProperties" call on |proxy|, returning false.
- // The proxy call is expected to be made exactly once.
- void SetupGetPropertiesFail(DBusGProxy* proxy) {
- EXPECT_CALL(mock_dbus_,
- ProxyCall_0_1(proxy, StrEq(shill::kGetPropertiesFunction),
- _, A<GHashTable**>()))
- .WillOnce(Return(false));
+ void InitWithDefaultService(const char* default_service) {
+ SetManagerReply(default_service, true);
+ // Check that provider initializes correctly.
+ EXPECT_TRUE(provider_->Init());
+ // RunOnce to notify the signal handler was connected properly.
+ EXPECT_TRUE(loop_.RunOnce(false));
}
// Sends a signal informing the provider about a default connection
- // |service_path|. Returns the fake connection change time.
- Time SendDefaultServiceSignal(const char* service_path) {
- auto default_service_gval = GValueNewString(service_path);
+ // |service_path|. Sets the fake connection change time in
+ // |conn_change_time_p| if provided.
+ void SendDefaultServiceSignal(const std::string& service_path,
+ Time* conn_change_time_p) {
const Time conn_change_time = ConnChangedTime();
fake_clock_.SetWallclockTime(conn_change_time);
- signal_handler_(kFakeManagerProxy, shill::kDefaultServiceProperty,
- default_service_gval, signal_data_);
+ ASSERT_TRUE(manager_property_changed_.IsHandlerRegistered());
+ manager_property_changed_.signal_callback().Run(
+ shill::kDefaultServiceProperty, dbus::ObjectPath(service_path));
fake_clock_.SetWallclockTime(conn_change_time + TimeDelta::FromSeconds(5));
- GValueFree(default_service_gval);
- return conn_change_time;
+ if (conn_change_time_p)
+ *conn_change_time_p = conn_change_time;
}
// Sets up expectations for detection of a connection |service_path| with type
@@ -221,31 +128,18 @@
// new connection status and change time are properly detected by the
// provider. Writes the fake connection change time to |conn_change_time_p|,
// if provided.
- void SetupConnectionAndAttrs(const char* service_path,
- DBusGProxy* service_proxy,
- const char* shill_type_str,
- const char* shill_tethering_str,
+ void SetupConnectionAndAttrs(const std::string& service_path,
+ const char* shill_type,
+ const char* shill_tethering,
Time* conn_change_time_p) {
- // Mock logic for querying the default service attributes.
- EXPECT_CALL(mock_dbus_,
- ProxyNewForName(
- kFakeConnection, StrEq(shill::kFlimflamServiceName),
- StrEq(service_path),
- StrEq(shill::kFlimflamServiceInterface)))
- .WillOnce(Return(service_proxy));
- EXPECT_CALL(mock_dbus_, ProxyUnref(service_proxy)).WillOnce(Return());
- pair<const char*, const char*> service_pairs[] = {
- {shill::kTypeProperty, shill_type_str},
- {shill::kTetheringProperty, shill_tethering_str},
- };
- auto service_properties = SetupGetPropertiesOkay(
- service_proxy, arraysize(service_pairs), service_pairs);
+ SetServiceReply(service_path, shill_type, nullptr, shill_tethering);
+ // Note: We don't setup this |service_path| as the default service path but
+ // we instead send a signal notifying the change since the code won't call
+ // GetProperties on the Manager object at this point.
// Send a signal about a new default service.
- auto conn_change_time = SendDefaultServiceSignal(service_path);
-
- // Release the service properties hash tables.
- g_hash_table_unref(service_properties);
+ Time conn_change_time;
+ SendDefaultServiceSignal(service_path, &conn_change_time);
// Query the connection status, ensure last change time reported correctly.
UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected());
@@ -260,12 +154,12 @@
// Sets up a connection and tests that its type is being properly detected by
// the provider.
void SetupConnectionAndTestType(const char* service_path,
- DBusGProxy* service_proxy,
- const char* shill_type_str,
+ const char* shill_type,
ConnectionType expected_conn_type) {
// Set up and test the connection, record the change time.
Time conn_change_time;
- SetupConnectionAndAttrs(service_path, service_proxy, shill_type_str,
+ SetupConnectionAndAttrs(service_path,
+ shill_type,
shill::kTetheringNotDetectedState,
&conn_change_time);
@@ -279,13 +173,13 @@
// Sets up a connection and tests that its tethering mode is being properly
// detected by the provider.
void SetupConnectionAndTestTethering(
- const char* service_path, DBusGProxy* service_proxy,
- const char* shill_tethering_str,
+ const char* service_path,
+ const char* shill_tethering,
ConnectionTethering expected_conn_tethering) {
// Set up and test the connection, record the change time.
Time conn_change_time;
- SetupConnectionAndAttrs(service_path, service_proxy, shill::kTypeEthernet,
- shill_tethering_str, &conn_change_time);
+ SetupConnectionAndAttrs(
+ service_path, shill::kTypeEthernet, shill_tethering, &conn_change_time);
// Query the connection tethering, ensure last change time did not change.
UmTestUtils::ExpectVariableHasValue(expected_conn_tethering,
@@ -294,16 +188,74 @@
provider_->var_conn_last_changed());
}
- StrictMock<MockDBusWrapper> mock_dbus_;
+ chromeos::FakeMessageLoop loop_{nullptr};
FakeClock fake_clock_;
+ chromeos_update_engine::FakeShillProxy fake_shill_proxy_;
+
+ // The registered signal handler for the signal Manager.PropertyChanged.
+ chromeos_update_engine::dbus_test_utils::MockSignalHandler<
+ void(const std::string&, const chromeos::Any&)> manager_property_changed_;
+
unique_ptr<RealShillProvider> provider_;
- void (*signal_handler_)(DBusGProxy*, const char*, GValue*, void*);
- void* signal_data_;
};
+void UmRealShillProviderTest::SetManagerReply(const char* default_service,
+ bool reply_succeeds) {
+ ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_.GetManagerProxy();
+ if (!reply_succeeds) {
+ EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _))
+ .WillOnce(Return(false));
+ return;
+ }
+
+ // Create a dictionary of properties and optionally include the default
+ // service.
+ chromeos::VariantDictionary reply_dict;
+ reply_dict["SomeOtherProperty"] = 0xC0FFEE;
+
+ if (default_service) {
+ reply_dict[shill::kDefaultServiceProperty] =
+ dbus::ObjectPath(default_service);
+ }
+ EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _))
+ .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true)));
+}
+
+ServiceProxyMock* UmRealShillProviderTest::SetServiceReply(
+ const std::string& service_path,
+ const char* service_type,
+ const char* physical_technology,
+ const char* service_tethering) {
+ chromeos::VariantDictionary reply_dict;
+ reply_dict["SomeOtherProperty"] = 0xC0FFEE;
+
+ if (service_type)
+ reply_dict[shill::kTypeProperty] = std::string(service_type);
+
+ if (physical_technology) {
+ reply_dict[shill::kPhysicalTechnologyProperty] =
+ std::string(physical_technology);
+ }
+
+ if (service_tethering)
+ reply_dict[shill::kTetheringProperty] = std::string(service_tethering);
+
+ ServiceProxyMock* service_proxy_mock = new ServiceProxyMock();
+
+ // Plumb return value into mock object.
+ EXPECT_CALL(*service_proxy_mock, GetProperties(_, _, _))
+ .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true)));
+
+ fake_shill_proxy_.SetServiceForPath(
+ service_path, chromeos::make_unique_ptr(service_proxy_mock));
+ return service_proxy_mock;
+}
+
+
// Query the connection status, type and time last changed, as they were set
// during initialization (no signals).
TEST_F(UmRealShillProviderTest, ReadBaseValues) {
+ InitWithDefaultService("/");
// Query the provider variables.
UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_connected());
UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type());
@@ -313,86 +265,77 @@
// Test that Ethernet connection is identified correctly.
TEST_F(UmRealShillProviderTest, ReadConnTypeEthernet) {
+ InitWithDefaultService("/");
SetupConnectionAndTestType(kFakeEthernetServicePath,
- kFakeEthernetServiceProxy,
shill::kTypeEthernet,
ConnectionType::kEthernet);
}
// Test that Wifi connection is identified correctly.
TEST_F(UmRealShillProviderTest, ReadConnTypeWifi) {
+ InitWithDefaultService("/");
SetupConnectionAndTestType(kFakeWifiServicePath,
- kFakeWifiServiceProxy,
shill::kTypeWifi,
ConnectionType::kWifi);
}
// Test that Wimax connection is identified correctly.
TEST_F(UmRealShillProviderTest, ReadConnTypeWimax) {
+ InitWithDefaultService("/");
SetupConnectionAndTestType(kFakeWimaxServicePath,
- kFakeWimaxServiceProxy,
shill::kTypeWimax,
ConnectionType::kWimax);
}
// Test that Bluetooth connection is identified correctly.
TEST_F(UmRealShillProviderTest, ReadConnTypeBluetooth) {
+ InitWithDefaultService("/");
SetupConnectionAndTestType(kFakeBluetoothServicePath,
- kFakeBluetoothServiceProxy,
shill::kTypeBluetooth,
ConnectionType::kBluetooth);
}
// Test that Cellular connection is identified correctly.
TEST_F(UmRealShillProviderTest, ReadConnTypeCellular) {
+ InitWithDefaultService("/");
SetupConnectionAndTestType(kFakeCellularServicePath,
- kFakeCellularServiceProxy,
shill::kTypeCellular,
ConnectionType::kCellular);
}
// Test that an unknown connection is identified as such.
TEST_F(UmRealShillProviderTest, ReadConnTypeUnknown) {
+ InitWithDefaultService("/");
SetupConnectionAndTestType(kFakeUnknownServicePath,
- kFakeUnknownServiceProxy,
"FooConnectionType",
ConnectionType::kUnknown);
}
// Tests that VPN connection is identified correctly.
TEST_F(UmRealShillProviderTest, ReadConnTypeVpn) {
+ InitWithDefaultService("/");
// Mock logic for returning a default service path and its type.
- EXPECT_CALL(mock_dbus_, ProxyNewForName(
- kFakeConnection, StrEq(shill::kFlimflamServiceName),
- StrEq(kFakeVpnServicePath), StrEq(shill::kFlimflamServiceInterface)))
- .WillOnce(Return(kFakeVpnServiceProxy));
- EXPECT_CALL(mock_dbus_, ProxyUnref(kFakeVpnServiceProxy)).WillOnce(Return());
- pair<const char*, const char*> service_pairs[] = {
- {shill::kTypeProperty, shill::kTypeVPN},
- {shill::kPhysicalTechnologyProperty, shill::kTypeWifi},
- };
- auto service_properties = SetupGetPropertiesOkay(kFakeVpnServiceProxy,
- arraysize(service_pairs),
- service_pairs);
+ SetServiceReply(kFakeVpnServicePath,
+ shill::kTypeVPN,
+ shill::kTypeWifi,
+ shill::kTetheringNotDetectedState);
// Send a signal about a new default service.
- Time conn_change_time = SendDefaultServiceSignal(kFakeVpnServicePath);
+ Time conn_change_time;
+ SendDefaultServiceSignal(kFakeVpnServicePath, &conn_change_time);
// Query the connection type, ensure last change time reported correctly.
UmTestUtils::ExpectVariableHasValue(ConnectionType::kWifi,
provider_->var_conn_type());
UmTestUtils::ExpectVariableHasValue(conn_change_time,
provider_->var_conn_last_changed());
-
- // Release properties hash tables.
- g_hash_table_unref(service_properties);
}
// Ensure that the connection type is properly cached in the provider through
// subsequent variable readings.
TEST_F(UmRealShillProviderTest, ConnTypeCacheUsed) {
+ InitWithDefaultService("/");
SetupConnectionAndTestType(kFakeEthernetServicePath,
- kFakeEthernetServiceProxy,
shill::kTypeEthernet,
ConnectionType::kEthernet);
@@ -403,12 +346,12 @@
// Ensure that the cached connection type remains valid even when a default
// connection signal occurs but the connection is not changed.
TEST_F(UmRealShillProviderTest, ConnTypeCacheRemainsValid) {
+ InitWithDefaultService("/");
SetupConnectionAndTestType(kFakeEthernetServicePath,
- kFakeEthernetServiceProxy,
shill::kTypeEthernet,
ConnectionType::kEthernet);
- SendDefaultServiceSignal(kFakeEthernetServicePath);
+ SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr);
UmTestUtils::ExpectVariableHasValue(ConnectionType::kEthernet,
provider_->var_conn_type());
@@ -417,53 +360,52 @@
// Ensure that the cached connection type is invalidated and re-read when the
// default connection changes.
TEST_F(UmRealShillProviderTest, ConnTypeCacheInvalidated) {
+ InitWithDefaultService("/");
SetupConnectionAndTestType(kFakeEthernetServicePath,
- kFakeEthernetServiceProxy,
shill::kTypeEthernet,
ConnectionType::kEthernet);
SetupConnectionAndTestType(kFakeWifiServicePath,
- kFakeWifiServiceProxy,
shill::kTypeWifi,
ConnectionType::kWifi);
}
// Test that a non-tethering mode is identified correctly.
TEST_F(UmRealShillProviderTest, ReadConnTetheringNotDetected) {
+ InitWithDefaultService("/");
SetupConnectionAndTestTethering(kFakeWifiServicePath,
- kFakeWifiServiceProxy,
shill::kTetheringNotDetectedState,
ConnectionTethering::kNotDetected);
}
// Test that a suspected tethering mode is identified correctly.
TEST_F(UmRealShillProviderTest, ReadConnTetheringSuspected) {
+ InitWithDefaultService("/");
SetupConnectionAndTestTethering(kFakeWifiServicePath,
- kFakeWifiServiceProxy,
shill::kTetheringSuspectedState,
ConnectionTethering::kSuspected);
}
// Test that a confirmed tethering mode is identified correctly.
TEST_F(UmRealShillProviderTest, ReadConnTetheringConfirmed) {
+ InitWithDefaultService("/");
SetupConnectionAndTestTethering(kFakeWifiServicePath,
- kFakeWifiServiceProxy,
shill::kTetheringConfirmedState,
ConnectionTethering::kConfirmed);
}
// Test that an unknown tethering mode is identified as such.
TEST_F(UmRealShillProviderTest, ReadConnTetheringUnknown) {
+ InitWithDefaultService("/");
SetupConnectionAndTestTethering(kFakeWifiServicePath,
- kFakeWifiServiceProxy,
"FooConnTethering",
ConnectionTethering::kUnknown);
}
// Ensure that the connection tethering mode is properly cached in the provider.
TEST_F(UmRealShillProviderTest, ConnTetheringCacheUsed) {
+ InitWithDefaultService("/");
SetupConnectionAndTestTethering(kFakeEthernetServicePath,
- kFakeEthernetServiceProxy,
shill::kTetheringNotDetectedState,
ConnectionTethering::kNotDetected);
@@ -474,12 +416,12 @@
// Ensure that the cached connection tethering mode remains valid even when a
// default connection signal occurs but the connection is not changed.
TEST_F(UmRealShillProviderTest, ConnTetheringCacheRemainsValid) {
+ InitWithDefaultService("/");
SetupConnectionAndTestTethering(kFakeEthernetServicePath,
- kFakeEthernetServiceProxy,
shill::kTetheringNotDetectedState,
ConnectionTethering::kNotDetected);
- SendDefaultServiceSignal(kFakeEthernetServicePath);
+ SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr);
UmTestUtils::ExpectVariableHasValue(ConnectionTethering::kNotDetected,
provider_->var_conn_tethering());
@@ -488,13 +430,12 @@
// Ensure that the cached connection tethering mode is invalidated and re-read
// when the default connection changes.
TEST_F(UmRealShillProviderTest, ConnTetheringCacheInvalidated) {
+ InitWithDefaultService("/");
SetupConnectionAndTestTethering(kFakeEthernetServicePath,
- kFakeEthernetServiceProxy,
shill::kTetheringNotDetectedState,
ConnectionTethering::kNotDetected);
SetupConnectionAndTestTethering(kFakeWifiServicePath,
- kFakeWifiServiceProxy,
shill::kTetheringConfirmedState,
ConnectionTethering::kConfirmed);
}
@@ -504,14 +445,19 @@
// changed, making sure that it is the time when the first signal was sent (and
// not the second).
TEST_F(UmRealShillProviderTest, ReadLastChangedTimeTwoSignals) {
+ InitWithDefaultService("/");
// Send a default service signal twice, advancing the clock in between.
Time conn_change_time;
- SetupConnectionAndAttrs(kFakeEthernetServicePath, kFakeEthernetServiceProxy,
+ SetupConnectionAndAttrs(kFakeEthernetServicePath,
shill::kTypeEthernet,
- shill::kTetheringNotDetectedState, &conn_change_time);
- SendDefaultServiceSignal(kFakeEthernetServicePath);
+ shill::kTetheringNotDetectedState,
+ &conn_change_time);
+ // This will set the service path to the same value, so it should not call
+ // GetProperties() again.
+ SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr);
- // Query the connection status, ensure last change time reported correctly.
+ // Query the connection status, ensure last change time reported as the first
+ // time the signal was sent.
UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected());
UmTestUtils::ExpectVariableHasValue(conn_change_time,
provider_->var_conn_last_changed());
@@ -521,8 +467,10 @@
// responding, that variables can be obtained, and that they all return a null
// value (indicating that the underlying values were not set).
TEST_F(UmRealShillProviderTest, NoInitConnStatusReadBaseValues) {
- // Re-initialize the provider, no initial connection status response.
- Init(false);
+ // Initialize the provider, no initial connection status response.
+ SetManagerReply(nullptr, false);
+ EXPECT_TRUE(provider_->Init());
+ EXPECT_TRUE(loop_.RunOnce(false));
UmTestUtils::ExpectVariableNotSet(provider_->var_is_connected());
UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type());
UmTestUtils::ExpectVariableNotSet(provider_->var_conn_last_changed());
@@ -531,12 +479,16 @@
// Test that, once a signal is received, the connection status and other info
// can be read correctly.
TEST_F(UmRealShillProviderTest, NoInitConnStatusReadConnTypeEthernet) {
- // Re-initialize the provider, no initial connection status response.
- Init(false);
- SetupConnectionAndTestType(kFakeEthernetServicePath,
- kFakeEthernetServiceProxy,
- shill::kTypeEthernet,
- ConnectionType::kEthernet);
+ // Initialize the provider with no initial connection status response.
+ SetManagerReply(nullptr, false);
+ EXPECT_TRUE(provider_->Init());
+ EXPECT_TRUE(loop_.RunOnce(false));
+
+ SetupConnectionAndAttrs(kFakeEthernetServicePath,
+ shill::kTypeEthernet,
+ shill::kTetheringNotDetectedState,
+ nullptr);
+ UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected());
}
} // namespace chromeos_update_manager
diff --git a/update_manager/state_factory.cc b/update_manager/state_factory.cc
index 54c0205..acefff4 100644
--- a/update_manager/state_factory.cc
+++ b/update_manager/state_factory.cc
@@ -22,17 +22,19 @@
namespace chromeos_update_manager {
-State* DefaultStateFactory(policy::PolicyProvider* policy_provider,
- chromeos_update_engine::DBusWrapperInterface* dbus,
- chromeos_update_engine::SystemState* system_state) {
+State* DefaultStateFactory(
+ policy::PolicyProvider* policy_provider,
+ chromeos_update_engine::ShillProxy* shill_proxy,
+ org::chromium::SessionManagerInterfaceProxyInterface* session_manager_proxy,
+ chromeos_update_engine::SystemState* system_state) {
chromeos_update_engine::ClockInterface* const clock = system_state->clock();
unique_ptr<RealConfigProvider> config_provider(
new RealConfigProvider(system_state->hardware()));
unique_ptr<RealDevicePolicyProvider> device_policy_provider(
- new RealDevicePolicyProvider(dbus, policy_provider));
+ new RealDevicePolicyProvider(session_manager_proxy, policy_provider));
unique_ptr<RealRandomProvider> random_provider(new RealRandomProvider());
unique_ptr<RealShillProvider> shill_provider(
- new RealShillProvider(dbus, clock));
+ new RealShillProvider(shill_proxy, clock));
unique_ptr<RealSystemProvider> system_provider(
new RealSystemProvider(system_state->hardware()));
unique_ptr<RealTimeProvider> time_provider(new RealTimeProvider(clock));
diff --git a/update_manager/state_factory.h b/update_manager/state_factory.h
index fa1164c..fd37ea7 100644
--- a/update_manager/state_factory.h
+++ b/update_manager/state_factory.h
@@ -5,7 +5,8 @@
#ifndef UPDATE_ENGINE_UPDATE_MANAGER_STATE_FACTORY_H_
#define UPDATE_ENGINE_UPDATE_MANAGER_STATE_FACTORY_H_
-#include "update_engine/dbus_wrapper_interface.h"
+#include "update_engine/dbus_proxies.h"
+#include "update_engine/shill_proxy.h"
#include "update_engine/system_state.h"
#include "update_engine/update_manager/state.h"
@@ -18,7 +19,8 @@
// to initialize.
State* DefaultStateFactory(
policy::PolicyProvider* policy_provider,
- chromeos_update_engine::DBusWrapperInterface* dbus,
+ chromeos_update_engine::ShillProxy* shill_proxy,
+ org::chromium::SessionManagerInterfaceProxyInterface* session_manager_proxy,
chromeos_update_engine::SystemState* system_state);
} // namespace chromeos_update_manager
diff --git a/update_metadata.proto b/update_metadata.proto
index bf0cb45..39373d5 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -110,44 +110,87 @@
optional string build_version = 6;
}
-message DeltaArchiveManifest {
- message InstallOperation {
- enum Type {
- REPLACE = 0; // Replace destination extents w/ attached data
- REPLACE_BZ = 1; // Replace destination extents w/ attached bzipped data
- MOVE = 2; // Move source extents to destination extents
- BSDIFF = 3; // The data is a bsdiff binary diff
- // SOURCE_COPY and SOURCE_BSDIFF are only supported on minor version 2.
- SOURCE_COPY = 4; // Copy from source to target partition
- SOURCE_BSDIFF = 5; // Like BSDIFF, but read from source partition
- }
- required Type type = 1;
- // The offset into the delta file (after the protobuf)
- // where the data (if any) is stored
- optional uint32 data_offset = 2;
- // The length of the data in the delta file
- optional uint32 data_length = 3;
+message InstallOperation {
+ enum Type {
+ REPLACE = 0; // Replace destination extents w/ attached data
+ REPLACE_BZ = 1; // Replace destination extents w/ attached bzipped data
+ MOVE = 2; // Move source extents to destination extents
+ BSDIFF = 3; // The data is a bsdiff binary diff
- // Ordered list of extents that are read from (if any) and written to.
- repeated Extent src_extents = 4;
- // Byte length of src, equal to the number of blocks in src_extents *
- // block_size. It is used for BSDIFF, because we need to pass that
- // external program the number of bytes to read from the blocks we pass it.
- // This is not used in any other operation.
- optional uint64 src_length = 5;
+ // SOURCE_COPY and SOURCE_BSDIFF are only supported on minor version 2 or
+ // higher.
+ SOURCE_COPY = 4; // Copy from source to target partition
+ SOURCE_BSDIFF = 5; // Like BSDIFF, but read from source partition
- repeated Extent dst_extents = 6;
- // Byte length of dst, equal to the number of blocks in dst_extents *
- // block_size. Used for BSDIFF, but not in any other operation.
- optional uint64 dst_length = 7;
-
- // Optional SHA 256 hash of the blob associated with this operation.
- // This is used as a primary validation for http-based downloads and
- // as a defense-in-depth validation for https-based downloads. If
- // the operation doesn't refer to any blob, this field will have
- // zero bytes.
- optional bytes data_sha256_hash = 8;
+ // ZERO and DISCARD are only supported on minor version 3.
+ ZERO = 6; // Write zeros in the destination.
+ DISCARD = 7; // Discard the destination blocks, reading as undefined.
}
+ required Type type = 1;
+ // The offset into the delta file (after the protobuf)
+ // where the data (if any) is stored
+ optional uint32 data_offset = 2;
+ // The length of the data in the delta file
+ optional uint32 data_length = 3;
+
+ // Ordered list of extents that are read from (if any) and written to.
+ repeated Extent src_extents = 4;
+ // Byte length of src, equal to the number of blocks in src_extents *
+ // block_size. It is used for BSDIFF, because we need to pass that
+ // external program the number of bytes to read from the blocks we pass it.
+ // This is not used in any other operation.
+ optional uint64 src_length = 5;
+
+ repeated Extent dst_extents = 6;
+ // Byte length of dst, equal to the number of blocks in dst_extents *
+ // block_size. Used for BSDIFF, but not in any other operation.
+ optional uint64 dst_length = 7;
+
+ // Optional SHA 256 hash of the blob associated with this operation.
+ // This is used as a primary validation for http-based downloads and
+ // as a defense-in-depth validation for https-based downloads. If
+ // the operation doesn't refer to any blob, this field will have
+ // zero bytes.
+ optional bytes data_sha256_hash = 8;
+
+ // Indicates the SHA 256 hash of the source data referenced in src_extents at
+ // the time of applying the operation. If present, the update_engine daemon
+ // MUST read and verify the source data before applying the operation.
+ optional bytes src_sha256_hash = 9;
+}
+
+// Describes the update to apply to a single partition.
+message PartitionUpdate {
+ // A platform-specific name to identify the partition set being updated. For
+ // example, in Chrome OS this could be "ROOT" or "KERNEL".
+ required string partition_name = 1;
+
+ // Whether this partition carries a filesystem with a "/postinstall" script
+ // that must be run to finalize the update process.
+ optional bool run_postinstall = 2;
+
+ // If present, a list of signatures of the new_partition_info.hash signed with
+ // different keys. If the update_engine daemon requires vendor-signed images
+ // and has its public key installed, one of the signatures should be valid
+ // for /postinstall to run.
+ repeated Signatures.Signature new_partition_signature = 3;
+
+ optional PartitionInfo old_partition_info = 4;
+ optional PartitionInfo new_partition_info = 5;
+
+ // The list of operations to be performed to apply this PartitionUpdate. The
+ // associated operation blobs (in operations[i].data_offset, data_length)
+ // should be stored contiguously and in the same order.
+ repeated InstallOperation operations = 6;
+}
+
+message DeltaArchiveManifest {
+ // DEPRECATED. List of install operations for the kernel and rootfs
+ // partitions. This information is now present in the |partitions| field. If
+ // you set these fields instead of the ones in the PartitionUpdate, they will
+ // be migrated to the corresponding entries in the PartitionUpdate to support
+ // full payloads on legacy daemons. Delta payloads starting with minor_version
+ // 3 MAY NOT include these fields.
repeated InstallOperation install_operations = 1;
repeated InstallOperation kernel_install_operations = 2;
@@ -162,7 +205,12 @@
optional uint64 signatures_offset = 4;
optional uint64 signatures_size = 5;
- // Partition data that can be used to validate the update.
+ // DEPRECATED. Partition metadata used to validate the update.
+ // This information is now present in the |partitions| field. If you set
+ // these fields instead of the ones in the PartitionUpdate, they will be
+ // migrated to the corresponding entries in the PartitionUpdate to support
+ // full payloads on legacy daemons. Delta payloads starting with minor_version
+ // 3 MAY NOT include these fields.
optional PartitionInfo old_kernel_info = 6;
optional PartitionInfo new_kernel_info = 7;
optional PartitionInfo old_rootfs_info = 8;
@@ -174,4 +222,7 @@
optional ImageInfo new_image_info = 11;
optional uint32 minor_version = 12 [default = 0];
+
+ // List of partitions that will be updated, in the order they will be updated.
+ repeated PartitionUpdate partitions = 13;
}
diff --git a/utils.cc b/utils.cc
index 97ce712..1366a39 100644
--- a/utils.cc
+++ b/utils.cc
@@ -29,6 +29,7 @@
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/scoped_file.h>
+#include <base/format_macros.h>
#include <base/location.h>
#include <base/logging.h>
#include <base/posix/eintr_wrapper.h>
@@ -1240,32 +1241,33 @@
NetworkConnectionType type,
NetworkTethering tethering) {
switch (type) {
- case kNetUnknown:
+ case NetworkConnectionType::kUnknown:
return metrics::ConnectionType::kUnknown;
- case kNetEthernet:
+ case NetworkConnectionType::kEthernet:
if (tethering == NetworkTethering::kConfirmed)
return metrics::ConnectionType::kTetheredEthernet;
else
return metrics::ConnectionType::kEthernet;
- case kNetWifi:
+ case NetworkConnectionType::kWifi:
if (tethering == NetworkTethering::kConfirmed)
return metrics::ConnectionType::kTetheredWifi;
else
return metrics::ConnectionType::kWifi;
- case kNetWimax:
+ case NetworkConnectionType::kWimax:
return metrics::ConnectionType::kWimax;
- case kNetBluetooth:
+ case NetworkConnectionType::kBluetooth:
return metrics::ConnectionType::kBluetooth;
- case kNetCellular:
+ case NetworkConnectionType::kCellular:
return metrics::ConnectionType::kCellular;
}
- LOG(ERROR) << "Unexpected network connection type: type=" << type
+ LOG(ERROR) << "Unexpected network connection type: type="
+ << static_cast<int>(type)
<< ", tethering=" << static_cast<int>(tethering);
return metrics::ConnectionType::kUnknown;
@@ -1517,7 +1519,7 @@
string CalculateP2PFileId(const string& payload_hash, size_t payload_size) {
string encoded_hash = chromeos::data_encoding::Base64Encode(payload_hash);
- return base::StringPrintf("cros_update_size_%zu_hash_%s",
+ return base::StringPrintf("cros_update_size_%" PRIuS "_hash_%s",
payload_size,
encoded_hash.c_str());
}
diff --git a/utils.h b/utils.h
index b661b43..efea204 100644
--- a/utils.h
+++ b/utils.h
@@ -18,13 +18,13 @@
#include <base/files/file_path.h>
#include <base/posix/eintr_wrapper.h>
#include <base/time/time.h>
-#include <chromeos/secure_blob.h>
#include <chromeos/key_value_store.h>
+#include <chromeos/secure_blob.h>
#include "metrics/metrics_library.h"
#include "update_engine/action.h"
#include "update_engine/action_processor.h"
-#include "update_engine/connection_manager.h"
+#include "update_engine/connection_manager_interface.h"
#include "update_engine/constants.h"
#include "update_engine/file_descriptor.h"
#include "update_engine/metrics.h"
@@ -397,9 +397,7 @@
// (NOTE: This function does not currently take daylight savings time
// into account so the result may up to one hour off. This is because
// the glibc date and timezone routines depend on the TZ environment
-// variable and changing environment variables is not thread-safe. An
-// alternative, thread-safe API to use would be GDateTime/GTimeZone
-// available in GLib 2.26 or later.)
+// variable and changing environment variables is not thread-safe.
bool ConvertToOmahaInstallDate(base::Time time, int *out_num_days);
// This function returns the duration on the wallclock since the last
diff --git a/utils_unittest.cc b/utils_unittest.cc
index 4787e84..83ef485 100644
--- a/utils_unittest.cc
+++ b/utils_unittest.cc
@@ -682,50 +682,50 @@
TEST(UtilsTest, GetConnectionType) {
// Check that expected combinations map to the right value.
EXPECT_EQ(metrics::ConnectionType::kUnknown,
- utils::GetConnectionType(kNetUnknown,
+ utils::GetConnectionType(NetworkConnectionType::kUnknown,
NetworkTethering::kUnknown));
EXPECT_EQ(metrics::ConnectionType::kEthernet,
- utils::GetConnectionType(kNetEthernet,
+ utils::GetConnectionType(NetworkConnectionType::kEthernet,
NetworkTethering::kUnknown));
EXPECT_EQ(metrics::ConnectionType::kWifi,
- utils::GetConnectionType(kNetWifi,
+ utils::GetConnectionType(NetworkConnectionType::kWifi,
NetworkTethering::kUnknown));
EXPECT_EQ(metrics::ConnectionType::kWimax,
- utils::GetConnectionType(kNetWimax,
+ utils::GetConnectionType(NetworkConnectionType::kWimax,
NetworkTethering::kUnknown));
EXPECT_EQ(metrics::ConnectionType::kBluetooth,
- utils::GetConnectionType(kNetBluetooth,
+ utils::GetConnectionType(NetworkConnectionType::kBluetooth,
NetworkTethering::kUnknown));
EXPECT_EQ(metrics::ConnectionType::kCellular,
- utils::GetConnectionType(kNetCellular,
+ utils::GetConnectionType(NetworkConnectionType::kCellular,
NetworkTethering::kUnknown));
EXPECT_EQ(metrics::ConnectionType::kTetheredEthernet,
- utils::GetConnectionType(kNetEthernet,
+ utils::GetConnectionType(NetworkConnectionType::kEthernet,
NetworkTethering::kConfirmed));
EXPECT_EQ(metrics::ConnectionType::kTetheredWifi,
- utils::GetConnectionType(kNetWifi,
+ utils::GetConnectionType(NetworkConnectionType::kWifi,
NetworkTethering::kConfirmed));
// Ensure that we don't report tethered ethernet unless it's confirmed.
EXPECT_EQ(metrics::ConnectionType::kEthernet,
- utils::GetConnectionType(kNetEthernet,
+ utils::GetConnectionType(NetworkConnectionType::kEthernet,
NetworkTethering::kNotDetected));
EXPECT_EQ(metrics::ConnectionType::kEthernet,
- utils::GetConnectionType(kNetEthernet,
+ utils::GetConnectionType(NetworkConnectionType::kEthernet,
NetworkTethering::kSuspected));
EXPECT_EQ(metrics::ConnectionType::kEthernet,
- utils::GetConnectionType(kNetEthernet,
+ utils::GetConnectionType(NetworkConnectionType::kEthernet,
NetworkTethering::kUnknown));
// Ditto for tethered wifi.
EXPECT_EQ(metrics::ConnectionType::kWifi,
- utils::GetConnectionType(kNetWifi,
+ utils::GetConnectionType(NetworkConnectionType::kWifi,
NetworkTethering::kNotDetected));
EXPECT_EQ(metrics::ConnectionType::kWifi,
- utils::GetConnectionType(kNetWifi,
+ utils::GetConnectionType(NetworkConnectionType::kWifi,
NetworkTethering::kSuspected));
EXPECT_EQ(metrics::ConnectionType::kWifi,
- utils::GetConnectionType(kNetWifi,
+ utils::GetConnectionType(NetworkConnectionType::kWifi,
NetworkTethering::kUnknown));
}