Merge changes Id9534a59,I3ee81536
* changes:
init: run property service in a thread
Revert "init: Handle properties in the background of calling fs_mgr"
diff --git a/adb/Android.bp b/adb/Android.bp
index 06cfcbf..57872b0 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -142,6 +142,10 @@
"sysdeps/posix/network.cpp",
]
+libadb_linux_srcs = [
+ "fdevent/fdevent_epoll.cpp",
+]
+
libadb_test_srcs = [
"adb_io_test.cpp",
"adb_listeners_test.cpp",
@@ -170,12 +174,11 @@
target: {
linux: {
- srcs: ["client/usb_linux.cpp"],
+ srcs: ["client/usb_linux.cpp"] + libadb_linux_srcs,
},
darwin: {
srcs: ["client/usb_osx.cpp"],
},
-
not_windows: {
srcs: libadb_posix_srcs,
},
@@ -342,7 +345,7 @@
// libminadbd wants both, as it's used to build native tests.
compile_multilib: "both",
- srcs: libadb_srcs + libadb_posix_srcs + [
+ srcs: libadb_srcs + libadb_linux_srcs + libadb_posix_srcs + [
"daemon/auth.cpp",
"daemon/jdwp_service.cpp",
],
diff --git a/adb/daemon/usb_ffs.cpp b/adb/daemon/usb_ffs.cpp
index a64ce40..338d776 100644
--- a/adb/daemon/usb_ffs.cpp
+++ b/adb/daemon/usb_ffs.cpp
@@ -299,7 +299,6 @@
}
// Signal only when writing the descriptors to ffs
android::base::SetProperty("sys.usb.ffs.ready", "1");
- *out_control = std::move(control);
}
bulk_out.reset(adb_open(USB_FFS_ADB_OUT, O_RDONLY));
@@ -314,6 +313,7 @@
return false;
}
+ *out_control = std::move(control);
*out_bulk_in = std::move(bulk_in);
*out_bulk_out = std::move(bulk_out);
return true;
diff --git a/adb/fdevent/fdevent.cpp b/adb/fdevent/fdevent.cpp
index 28b8f37..562f587 100644
--- a/adb/fdevent/fdevent.cpp
+++ b/adb/fdevent/fdevent.cpp
@@ -26,16 +26,24 @@
#include "adb_utils.h"
#include "fdevent.h"
+#include "fdevent_epoll.h"
#include "fdevent_poll.h"
+using namespace std::chrono_literals;
+using std::chrono::duration_cast;
+
+void invoke_fde(struct fdevent* fde, unsigned events) {
+ if (auto f = std::get_if<fd_func>(&fde->func)) {
+ (*f)(fde->fd.get(), events, fde->arg);
+ } else if (auto f = std::get_if<fd_func2>(&fde->func)) {
+ (*f)(fde, events, fde->arg);
+ } else {
+ __builtin_unreachable();
+ }
+}
+
std::string dump_fde(const fdevent* fde) {
std::string state;
- if (fde->state & FDE_ACTIVE) {
- state += "A";
- }
- if (fde->state & FDE_PENDING) {
- state += "P";
- }
if (fde->state & FDE_READ) {
state += "R";
}
@@ -53,9 +61,11 @@
CheckMainThread();
CHECK_GE(fd.get(), 0);
+ int fd_num = fd.get();
+
fdevent* fde = new fdevent();
fde->id = fdevent_id_++;
- fde->state = FDE_ACTIVE;
+ fde->state = 0;
fde->fd = std::move(fd);
fde->func = func;
fde->arg = arg;
@@ -66,6 +76,10 @@
LOG(ERROR) << "failed to set non-blocking mode for fd " << fde->fd.get();
}
+ auto [it, inserted] = this->installed_fdevents_.emplace(fd_num, fde);
+ CHECK(inserted);
+ UNUSED(it);
+
this->Register(fde);
return fde;
}
@@ -78,18 +92,22 @@
this->Unregister(fde);
+ auto erased = this->installed_fdevents_.erase(fde->fd.get());
+ CHECK_EQ(1UL, erased);
+
unique_fd result = std::move(fde->fd);
delete fde;
return result;
}
void fdevent_context::Add(fdevent* fde, unsigned events) {
- Set(fde, (fde->state & FDE_EVENTMASK) | events);
+ CHECK(!(events & FDE_TIMEOUT));
+ Set(fde, fde->state | events);
}
void fdevent_context::Del(fdevent* fde, unsigned events) {
CHECK(!(events & FDE_TIMEOUT));
- Set(fde, (fde->state & FDE_EVENTMASK) & ~events);
+ Set(fde, fde->state & ~events);
}
void fdevent_context::SetTimeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) {
@@ -98,6 +116,56 @@
fde->last_active = std::chrono::steady_clock::now();
}
+std::optional<std::chrono::milliseconds> fdevent_context::CalculatePollDuration() {
+ std::optional<std::chrono::milliseconds> result = std::nullopt;
+ auto now = std::chrono::steady_clock::now();
+ CheckMainThread();
+
+ for (const auto& [fd, fde] : this->installed_fdevents_) {
+ UNUSED(fd);
+ auto timeout_opt = fde->timeout;
+ if (timeout_opt) {
+ auto deadline = fde->last_active + *timeout_opt;
+ auto time_left = duration_cast<std::chrono::milliseconds>(deadline - now);
+ if (time_left < 0ms) {
+ time_left = 0ms;
+ }
+
+ if (!result) {
+ result = time_left;
+ } else {
+ result = std::min(*result, time_left);
+ }
+ }
+ }
+
+ return result;
+}
+
+void fdevent_context::HandleEvents(const std::vector<fdevent_event>& events) {
+ for (const auto& event : events) {
+ invoke_fde(event.fde, event.events);
+ }
+ FlushRunQueue();
+}
+
+void fdevent_context::FlushRunQueue() {
+ // We need to be careful around reentrancy here, since a function we call can queue up another
+ // function.
+ while (true) {
+ std::function<void()> fn;
+ {
+ std::lock_guard<std::mutex> lock(this->run_queue_mutex_);
+ if (this->run_queue_.empty()) {
+ break;
+ }
+ fn = std::move(this->run_queue_.front());
+ this->run_queue_.pop_front();
+ }
+ fn();
+ }
+}
+
void fdevent_context::CheckMainThread() {
if (main_thread_id_) {
CHECK_EQ(*main_thread_id_, android::base::GetThreadId());
@@ -118,25 +186,16 @@
Interrupt();
}
-void fdevent_context::FlushRunQueue() {
- // We need to be careful around reentrancy here, since a function we call can queue up another
- // function.
- while (true) {
- std::function<void()> fn;
- {
- std::lock_guard<std::mutex> lock(this->run_queue_mutex_);
- if (this->run_queue_.empty()) {
- break;
- }
- fn = this->run_queue_.front();
- this->run_queue_.pop_front();
- }
- fn();
- }
+static std::unique_ptr<fdevent_context> fdevent_create_context() {
+#if defined(__linux__)
+ return std::make_unique<fdevent_context_epoll>();
+#else
+ return std::make_unique<fdevent_context_poll>();
+#endif
}
static auto& g_ambient_fdevent_context =
- *new std::unique_ptr<fdevent_context>(new fdevent_context_poll());
+ *new std::unique_ptr<fdevent_context>(fdevent_create_context());
static fdevent_context* fdevent_get_ambient() {
return g_ambient_fdevent_context.get();
@@ -197,5 +256,5 @@
}
void fdevent_reset() {
- g_ambient_fdevent_context.reset(new fdevent_context_poll());
+ g_ambient_fdevent_context = fdevent_create_context();
}
diff --git a/adb/fdevent/fdevent.h b/adb/fdevent/fdevent.h
index 2424252..86814d7 100644
--- a/adb/fdevent/fdevent.h
+++ b/adb/fdevent/fdevent.h
@@ -26,6 +26,7 @@
#include <functional>
#include <mutex>
#include <optional>
+#include <unordered_map>
#include <variant>
#include <android-base/thread_annotations.h>
@@ -38,19 +39,19 @@
#define FDE_ERROR 0x0004
#define FDE_TIMEOUT 0x0008
-// Internal states.
-#define FDE_EVENTMASK 0x00ff
-#define FDE_STATEMASK 0xff00
-
-#define FDE_ACTIVE 0x0100
-#define FDE_PENDING 0x0200
+struct fdevent;
typedef void (*fd_func)(int fd, unsigned events, void *userdata);
typedef void (*fd_func2)(struct fdevent* fde, unsigned events, void* userdata);
-struct fdevent;
+void invoke_fde(struct fdevent* fde, unsigned events);
std::string dump_fde(const fdevent* fde);
+struct fdevent_event {
+ fdevent* fde;
+ unsigned events;
+};
+
struct fdevent_context {
public:
virtual ~fdevent_context() = default;
@@ -59,14 +60,13 @@
fdevent* Create(unique_fd fd, std::variant<fd_func, fd_func2> func, void* arg);
// Deallocate an fdevent object, returning the file descriptor that was owned by it.
+ // Note that this calls Set, which is a virtual method, so destructors that call this must be
+ // final.
unique_fd Destroy(fdevent* fde);
protected:
- // Register an fdevent that is being created by Create with the fdevent_context.
- virtual void Register(fdevent* fde) = 0;
-
- // Unregister an fdevent that is being destroyed by Destroy with the fdevent_context.
- virtual void Unregister(fdevent* fde) = 0;
+ virtual void Register(fdevent*) {}
+ virtual void Unregister(fdevent*) {}
public:
// Change which events should cause notifications.
@@ -80,6 +80,15 @@
// trigger repeatedly every |timeout| ms.
void SetTimeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout);
+ protected:
+ std::optional<std::chrono::milliseconds> CalculatePollDuration();
+ void HandleEvents(const std::vector<fdevent_event>& events);
+
+ private:
+ // Run all pending functions enqueued via Run().
+ void FlushRunQueue() EXCLUDES(run_queue_mutex_);
+
+ public:
// Loop until TerminateLoop is called, handling events.
// Implementations should call FlushRunQueue on every iteration, and check the value of
// terminate_loop_ to determine whether to stop.
@@ -100,12 +109,12 @@
// Interrupt the run loop.
virtual void Interrupt() = 0;
- // Run all pending functions enqueued via Run().
- void FlushRunQueue() EXCLUDES(run_queue_mutex_);
-
std::optional<uint64_t> main_thread_id_ = std::nullopt;
std::atomic<bool> terminate_loop_ = false;
+ protected:
+ std::unordered_map<int, fdevent*> installed_fdevents_;
+
private:
uint64_t fdevent_id_ = 0;
std::mutex run_queue_mutex_;
@@ -119,7 +128,6 @@
int force_eof = 0;
uint16_t state = 0;
- uint16_t events = 0;
std::optional<std::chrono::milliseconds> timeout;
std::chrono::steady_clock::time_point last_active;
diff --git a/adb/fdevent/fdevent_epoll.cpp b/adb/fdevent/fdevent_epoll.cpp
new file mode 100644
index 0000000..e3d1674
--- /dev/null
+++ b/adb/fdevent/fdevent_epoll.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fdevent_epoll.h"
+
+#if defined(__linux__)
+
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+
+#include <android-base/logging.h>
+#include <android-base/threads.h>
+
+#include "adb_unique_fd.h"
+#include "fdevent.h"
+
+static void fdevent_interrupt(int fd, unsigned, void*) {
+ uint64_t buf;
+ ssize_t rc = TEMP_FAILURE_RETRY(adb_read(fd, &buf, sizeof(buf)));
+ if (rc == -1) {
+ PLOG(FATAL) << "failed to read from fdevent interrupt fd";
+ }
+}
+
+fdevent_context_epoll::fdevent_context_epoll() {
+ epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
+
+ unique_fd interrupt_fd(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+ if (interrupt_fd == -1) {
+ PLOG(FATAL) << "failed to create fdevent interrupt eventfd";
+ }
+
+ unique_fd interrupt_fd_dup(fcntl(interrupt_fd.get(), F_DUPFD_CLOEXEC, 3));
+ if (interrupt_fd_dup == -1) {
+ PLOG(FATAL) << "failed to dup fdevent interrupt eventfd";
+ }
+
+ this->interrupt_fd_ = std::move(interrupt_fd_dup);
+ fdevent* fde = this->Create(std::move(interrupt_fd), fdevent_interrupt, nullptr);
+ CHECK(fde != nullptr);
+ this->Add(fde, FDE_READ);
+}
+
+fdevent_context_epoll::~fdevent_context_epoll() {
+ // Destroy calls virtual methods, but this class is final, so that's okay.
+ this->Destroy(this->interrupt_fde_);
+}
+
+static epoll_event calculate_epoll_event(fdevent* fde) {
+ epoll_event result;
+ result.events = 0;
+ if (fde->state & FDE_READ) {
+ result.events |= EPOLLIN;
+ }
+ if (fde->state & FDE_WRITE) {
+ result.events |= EPOLLOUT;
+ }
+ if (fde->state & FDE_ERROR) {
+ result.events |= EPOLLERR;
+ }
+ result.events |= EPOLLRDHUP;
+ result.data.ptr = fde;
+ return result;
+}
+
+void fdevent_context_epoll::Register(fdevent* fde) {
+ epoll_event ev = calculate_epoll_event(fde);
+ if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, fde->fd.get(), &ev) != 0) {
+ PLOG(FATAL) << "failed to register fd " << fde->fd.get() << " with epoll";
+ }
+}
+
+void fdevent_context_epoll::Unregister(fdevent* fde) {
+ if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_DEL, fde->fd.get(), nullptr) != 0) {
+ PLOG(FATAL) << "failed to unregister fd " << fde->fd.get() << " with epoll";
+ }
+}
+
+void fdevent_context_epoll::Set(fdevent* fde, unsigned events) {
+ unsigned previous_state = fde->state;
+ fde->state = events;
+
+ // If the state is the same, or only differed by FDE_TIMEOUT, we don't need to modify epoll.
+ if ((previous_state & ~FDE_TIMEOUT) == (events & ~FDE_TIMEOUT)) {
+ return;
+ }
+
+ epoll_event ev = calculate_epoll_event(fde);
+ if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_MOD, fde->fd.get(), &ev) != 0) {
+ PLOG(FATAL) << "failed to modify fd " << fde->fd.get() << " with epoll";
+ }
+}
+
+void fdevent_context_epoll::Loop() {
+ main_thread_id_ = android::base::GetThreadId();
+
+ std::vector<fdevent_event> fde_events;
+ std::vector<epoll_event> epoll_events;
+ epoll_events.resize(this->installed_fdevents_.size());
+
+ while (true) {
+ if (terminate_loop_) {
+ break;
+ }
+
+ int rc = -1;
+ while (rc == -1) {
+ std::optional<std::chrono::milliseconds> timeout = CalculatePollDuration();
+ int timeout_ms;
+ if (!timeout) {
+ timeout_ms = -1;
+ } else {
+ timeout_ms = timeout->count();
+ }
+
+ rc = epoll_wait(epoll_fd_.get(), epoll_events.data(), epoll_events.size(), timeout_ms);
+ if (rc == -1 && errno != EINTR) {
+ PLOG(FATAL) << "epoll_wait failed";
+ }
+ }
+
+ auto post_poll = std::chrono::steady_clock::now();
+ std::unordered_map<fdevent*, unsigned> event_map;
+ for (int i = 0; i < rc; ++i) {
+ fdevent* fde = static_cast<fdevent*>(epoll_events[i].data.ptr);
+
+ unsigned events = 0;
+ if (epoll_events[i].events & EPOLLIN) {
+ CHECK(fde->state & FDE_READ);
+ events |= FDE_READ;
+ }
+ if (epoll_events[i].events & EPOLLOUT) {
+ CHECK(fde->state & FDE_WRITE);
+ events |= FDE_WRITE;
+ }
+ if (epoll_events[i].events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) {
+ // We fake a read, as the rest of the code assumes that errors will
+ // be detected at that point.
+ events |= FDE_READ | FDE_ERROR;
+ }
+
+ event_map[fde] = events;
+ }
+
+ for (const auto& [fd, fde] : installed_fdevents_) {
+ unsigned events = 0;
+ if (auto it = event_map.find(fde); it != event_map.end()) {
+ events = it->second;
+ }
+
+ if (events == 0) {
+ if (fde->timeout) {
+ auto deadline = fde->last_active + *fde->timeout;
+ if (deadline < post_poll) {
+ events |= FDE_TIMEOUT;
+ }
+ }
+ }
+
+ if (events != 0) {
+ LOG(DEBUG) << dump_fde(fde) << " got events " << std::hex << std::showbase
+ << events;
+ fde_events.push_back({fde, events});
+ fde->last_active = post_poll;
+ }
+ }
+ this->HandleEvents(std::move(fde_events));
+ fde_events.clear();
+ }
+
+ main_thread_id_.reset();
+}
+
+size_t fdevent_context_epoll::InstalledCount() {
+ // We always have an installed fde for interrupt.
+ return this->installed_fdevents_.size() - 1;
+}
+
+void fdevent_context_epoll::Interrupt() {
+ uint64_t i = 1;
+ ssize_t rc = TEMP_FAILURE_RETRY(adb_write(this->interrupt_fd_, &i, sizeof(i)));
+ if (rc != sizeof(i)) {
+ PLOG(FATAL) << "failed to write to fdevent interrupt eventfd";
+ }
+}
+
+#endif // defined(__linux__)
diff --git a/adb/fdevent/fdevent_epoll.h b/adb/fdevent/fdevent_epoll.h
new file mode 100644
index 0000000..684fa32
--- /dev/null
+++ b/adb/fdevent/fdevent_epoll.h
@@ -0,0 +1,61 @@
+#pragma once
+
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(__linux__)
+
+#include "sysdeps.h"
+
+#include <sys/epoll.h>
+
+#include <deque>
+#include <list>
+#include <mutex>
+#include <unordered_map>
+
+#include <android-base/thread_annotations.h>
+
+#include "adb_unique_fd.h"
+#include "fdevent.h"
+
+struct fdevent_context_epoll final : public fdevent_context {
+ fdevent_context_epoll();
+ virtual ~fdevent_context_epoll();
+
+ virtual void Register(fdevent* fde) final;
+ virtual void Unregister(fdevent* fde) final;
+
+ virtual void Set(fdevent* fde, unsigned events) final;
+
+ virtual void Loop() final;
+ size_t InstalledCount() final;
+
+ protected:
+ virtual void Interrupt() final;
+
+ public:
+ // All operations to fdevent should happen only in the main thread.
+ // That's why we don't need a lock for fdevent.
+ std::unordered_map<int, fdevent*> epoll_node_map_;
+ std::list<fdevent*> pending_list_;
+
+ unique_fd epoll_fd_;
+ unique_fd interrupt_fd_;
+ fdevent* interrupt_fde_ = nullptr;
+};
+
+#endif // defined(__linux__)
diff --git a/adb/fdevent/fdevent_poll.cpp b/adb/fdevent/fdevent_poll.cpp
index 75ea081..cc4a7a1 100644
--- a/adb/fdevent/fdevent_poll.cpp
+++ b/adb/fdevent/fdevent_poll.cpp
@@ -75,60 +75,14 @@
}
fdevent_context_poll::~fdevent_context_poll() {
+ // Destroy calls virtual methods, but this class is final, so that's okay.
this->Destroy(this->interrupt_fde_);
}
-void fdevent_context_poll::Register(fdevent* fde) {
- auto pair = poll_node_map_.emplace(fde->fd.get(), PollNode(fde));
- CHECK(pair.second) << "install existing fd " << fde->fd.get();
-}
-
-void fdevent_context_poll::Unregister(fdevent* fde) {
- if (fde->state & FDE_ACTIVE) {
- poll_node_map_.erase(fde->fd.get());
-
- if (fde->state & FDE_PENDING) {
- pending_list_.remove(fde);
- }
- fde->state = 0;
- fde->events = 0;
- }
-}
-
void fdevent_context_poll::Set(fdevent* fde, unsigned events) {
CheckMainThread();
- events &= FDE_EVENTMASK;
- if ((fde->state & FDE_EVENTMASK) == events) {
- return;
- }
- CHECK(fde->state & FDE_ACTIVE);
-
- auto it = poll_node_map_.find(fde->fd.get());
- CHECK(it != poll_node_map_.end());
- PollNode& node = it->second;
- if (events & FDE_READ) {
- node.pollfd.events |= POLLIN;
- } else {
- node.pollfd.events &= ~POLLIN;
- }
-
- if (events & FDE_WRITE) {
- node.pollfd.events |= POLLOUT;
- } else {
- node.pollfd.events &= ~POLLOUT;
- }
- fde->state = (fde->state & FDE_STATEMASK) | events;
-
+ fde->state = events;
D("fdevent_set: %s, events = %u", dump_fde(fde).c_str(), events);
-
- if (fde->state & FDE_PENDING) {
- // If we are pending, make sure we don't signal an event that is no longer wanted.
- fde->events &= events;
- if (fde->events == 0) {
- pending_list_.remove(fde);
- fde->state &= ~FDE_PENDING;
- }
- }
}
static std::string dump_pollfds(const std::vector<adb_pollfd>& pollfds) {
@@ -146,204 +100,94 @@
return result;
}
-static std::optional<std::chrono::milliseconds> calculate_timeout(fdevent_context_poll* ctx) {
- std::optional<std::chrono::milliseconds> result = std::nullopt;
- auto now = std::chrono::steady_clock::now();
- ctx->CheckMainThread();
-
- for (const auto& [fd, pollnode] : ctx->poll_node_map_) {
- UNUSED(fd);
- auto timeout_opt = pollnode.fde->timeout;
- if (timeout_opt) {
- auto deadline = pollnode.fde->last_active + *timeout_opt;
- auto time_left = std::chrono::duration_cast<std::chrono::milliseconds>(deadline - now);
- if (time_left < std::chrono::milliseconds::zero()) {
- time_left = std::chrono::milliseconds::zero();
- }
-
- if (!result) {
- result = time_left;
- } else {
- result = std::min(*result, time_left);
- }
- }
- }
-
- return result;
-}
-
-static void fdevent_process(fdevent_context_poll* ctx) {
- std::vector<adb_pollfd> pollfds;
- for (const auto& pair : ctx->poll_node_map_) {
- pollfds.push_back(pair.second.pollfd);
- }
- CHECK_GT(pollfds.size(), 0u);
- D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
-
- auto timeout = calculate_timeout(ctx);
- int timeout_ms;
- if (!timeout) {
- timeout_ms = -1;
- } else {
- timeout_ms = timeout->count();
- }
-
- int ret = adb_poll(&pollfds[0], pollfds.size(), timeout_ms);
- if (ret == -1) {
- PLOG(ERROR) << "poll(), ret = " << ret;
- return;
- }
-
- auto post_poll = std::chrono::steady_clock::now();
-
- for (const auto& pollfd : pollfds) {
- if (pollfd.revents != 0) {
- D("for fd %d, revents = %x", pollfd.fd, pollfd.revents);
- }
- unsigned events = 0;
- if (pollfd.revents & POLLIN) {
- events |= FDE_READ;
- }
- if (pollfd.revents & POLLOUT) {
- events |= FDE_WRITE;
- }
- if (pollfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
- // We fake a read, as the rest of the code assumes that errors will
- // be detected at that point.
- events |= FDE_READ | FDE_ERROR;
- }
-#if defined(__linux__)
- if (pollfd.revents & POLLRDHUP) {
- events |= FDE_READ | FDE_ERROR;
- }
-#endif
- auto it = ctx->poll_node_map_.find(pollfd.fd);
- CHECK(it != ctx->poll_node_map_.end());
- fdevent* fde = it->second.fde;
-
- if (events == 0) {
- // Check for timeout.
- if (fde->timeout) {
- auto deadline = fde->last_active + *fde->timeout;
- if (deadline < post_poll) {
- events |= FDE_TIMEOUT;
- }
- }
- }
-
- if (events != 0) {
- CHECK_EQ(fde->fd.get(), pollfd.fd);
- fde->events |= events;
- fde->last_active = post_poll;
- D("%s got events %x", dump_fde(fde).c_str(), events);
- fde->state |= FDE_PENDING;
- ctx->pending_list_.push_back(fde);
- }
- }
-}
-
-template <class T>
-struct always_false : std::false_type {};
-
-static void fdevent_call_fdfunc(fdevent* fde) {
- unsigned events = fde->events;
- fde->events = 0;
- CHECK(fde->state & FDE_PENDING);
- fde->state &= (~FDE_PENDING);
- D("fdevent_call_fdfunc %s", dump_fde(fde).c_str());
- std::visit(
- [&](auto&& f) {
- using F = std::decay_t<decltype(f)>;
- if constexpr (std::is_same_v<fd_func, F>) {
- f(fde->fd.get(), events, fde->arg);
- } else if constexpr (std::is_same_v<fd_func2, F>) {
- f(fde, events, fde->arg);
- } else {
- static_assert(always_false<F>::value, "non-exhaustive visitor");
- }
- },
- fde->func);
-}
-
-static void fdevent_check_spin(fdevent_context_poll* ctx, uint64_t cycle) {
- // Check to see if we're spinning because we forgot about an fdevent
- // by keeping track of how long fdevents have been continuously pending.
- struct SpinCheck {
- fdevent* fde;
- android::base::boot_clock::time_point timestamp;
- uint64_t cycle;
- };
-
- // TODO: Move this into the base fdevent_context.
- static auto& g_continuously_pending = *new std::unordered_map<uint64_t, SpinCheck>();
- static auto last_cycle = android::base::boot_clock::now();
-
- auto now = android::base::boot_clock::now();
- if (now - last_cycle > 10ms) {
- // We're not spinning.
- g_continuously_pending.clear();
- last_cycle = now;
- return;
- }
- last_cycle = now;
-
- for (auto* fde : ctx->pending_list_) {
- auto it = g_continuously_pending.find(fde->id);
- if (it == g_continuously_pending.end()) {
- g_continuously_pending[fde->id] =
- SpinCheck{.fde = fde, .timestamp = now, .cycle = cycle};
- } else {
- it->second.cycle = cycle;
- }
- }
-
- for (auto it = g_continuously_pending.begin(); it != g_continuously_pending.end();) {
- if (it->second.cycle != cycle) {
- it = g_continuously_pending.erase(it);
- } else {
- // Use an absurdly long window, since all we really care about is
- // getting a bugreport eventually.
- if (now - it->second.timestamp > 300s) {
- LOG(FATAL_WITHOUT_ABORT)
- << "detected spin in fdevent: " << dump_fde(it->second.fde);
-#if defined(__linux__)
- int fd = it->second.fde->fd.get();
- std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
- std::string path;
- if (!android::base::Readlink(fd_path, &path)) {
- PLOG(FATAL_WITHOUT_ABORT) << "readlink of fd " << fd << " failed";
- }
- LOG(FATAL_WITHOUT_ABORT) << "fd " << fd << " = " << path;
-#endif
- abort();
- }
- ++it;
- }
- }
-}
-
void fdevent_context_poll::Loop() {
main_thread_id_ = android::base::GetThreadId();
- uint64_t cycle = 0;
while (true) {
if (terminate_loop_) {
break;
}
D("--- --- waiting for events");
+ std::vector<adb_pollfd> pollfds;
+ for (const auto& [fd, fde] : this->installed_fdevents_) {
+ adb_pollfd pfd;
+ pfd.fd = fd;
+ pfd.events = 0;
+ if (fde->state & FDE_READ) {
+ pfd.events |= POLLIN;
+ }
+ if (fde->state & FDE_WRITE) {
+ pfd.events |= POLLOUT;
+ }
+ if (fde->state & FDE_ERROR) {
+ pfd.events |= POLLERR;
+ }
+#if defined(__linux__)
+ pfd.events |= POLLRDHUP;
+#endif
+ pfd.revents = 0;
+ pollfds.push_back(pfd);
+ }
+ CHECK_GT(pollfds.size(), 0u);
+ D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
- fdevent_process(this);
-
- fdevent_check_spin(this, cycle++);
-
- while (!pending_list_.empty()) {
- fdevent* fde = pending_list_.front();
- pending_list_.pop_front();
- fdevent_call_fdfunc(fde);
+ std::optional<std::chrono::milliseconds> timeout = CalculatePollDuration();
+ int timeout_ms;
+ if (!timeout) {
+ timeout_ms = -1;
+ } else {
+ timeout_ms = timeout->count();
}
- this->FlushRunQueue();
+ int ret = adb_poll(pollfds.data(), pollfds.size(), timeout_ms);
+ if (ret == -1) {
+ PLOG(ERROR) << "poll(), ret = " << ret;
+ return;
+ }
+
+ auto post_poll = std::chrono::steady_clock::now();
+ std::vector<fdevent_event> poll_events;
+
+ for (const auto& pollfd : pollfds) {
+ unsigned events = 0;
+ if (pollfd.revents & POLLIN) {
+ events |= FDE_READ;
+ }
+ if (pollfd.revents & POLLOUT) {
+ events |= FDE_WRITE;
+ }
+ if (pollfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
+ // We fake a read, as the rest of the code assumes that errors will
+ // be detected at that point.
+ events |= FDE_READ | FDE_ERROR;
+ }
+#if defined(__linux__)
+ if (pollfd.revents & POLLRDHUP) {
+ events |= FDE_READ | FDE_ERROR;
+ }
+#endif
+
+ auto it = this->installed_fdevents_.find(pollfd.fd);
+ CHECK(it != this->installed_fdevents_.end());
+ fdevent* fde = it->second;
+
+ if (events == 0) {
+ if (fde->timeout) {
+ auto deadline = fde->last_active + *fde->timeout;
+ if (deadline < post_poll) {
+ events |= FDE_TIMEOUT;
+ }
+ }
+ }
+
+ if (events != 0) {
+ D("%s got events %x", dump_fde(fde).c_str(), events);
+ poll_events.push_back({fde, events});
+ fde->last_active = post_poll;
+ }
+ }
+ this->HandleEvents(std::move(poll_events));
}
main_thread_id_.reset();
@@ -351,7 +195,7 @@
size_t fdevent_context_poll::InstalledCount() {
// We always have an installed fde for interrupt.
- return poll_node_map_.size() - 1;
+ return this->installed_fdevents_.size() - 1;
}
void fdevent_context_poll::Interrupt() {
diff --git a/adb/fdevent/fdevent_poll.h b/adb/fdevent/fdevent_poll.h
index db08301..98abab2 100644
--- a/adb/fdevent/fdevent_poll.h
+++ b/adb/fdevent/fdevent_poll.h
@@ -44,13 +44,10 @@
}
};
-struct fdevent_context_poll : public fdevent_context {
+struct fdevent_context_poll final : public fdevent_context {
fdevent_context_poll();
virtual ~fdevent_context_poll();
- virtual void Register(fdevent* fde) final;
- virtual void Unregister(fdevent* fde) final;
-
virtual void Set(fdevent* fde, unsigned events) final;
virtual void Loop() final;
@@ -61,11 +58,6 @@
virtual void Interrupt() final;
public:
- // All operations to fdevent should happen only in the main thread.
- // That's why we don't need a lock for fdevent.
- std::unordered_map<int, PollNode> poll_node_map_;
- std::list<fdevent*> pending_list_;
-
unique_fd interrupt_fd_;
fdevent* interrupt_fde_ = nullptr;
};
diff --git a/adb/fdevent/fdevent_test.cpp b/adb/fdevent/fdevent_test.cpp
index 682f061..e06b3b3 100644
--- a/adb/fdevent/fdevent_test.cpp
+++ b/adb/fdevent/fdevent_test.cpp
@@ -118,8 +118,8 @@
TEST_F(FdeventTest, smoke) {
for (bool use_new_callback : {true, false}) {
fdevent_reset();
- const size_t PIPE_COUNT = 10;
- const size_t MESSAGE_LOOP_COUNT = 100;
+ const size_t PIPE_COUNT = 512;
+ const size_t MESSAGE_LOOP_COUNT = 10;
const std::string MESSAGE = "fdevent_test";
int fd_pair1[2];
int fd_pair2[2];
@@ -172,44 +172,6 @@
}
}
-struct InvalidFdArg {
- fdevent* fde;
- unsigned expected_events;
- size_t* happened_event_count;
-};
-
-static void InvalidFdEventCallback(int, unsigned events, void* userdata) {
- InvalidFdArg* arg = reinterpret_cast<InvalidFdArg*>(userdata);
- ASSERT_EQ(arg->expected_events, events);
- fdevent_destroy(arg->fde);
- if (++*(arg->happened_event_count) == 2) {
- fdevent_terminate_loop();
- }
-}
-
-static void InvalidFdThreadFunc() {
- const int INVALID_READ_FD = std::numeric_limits<int>::max() - 1;
- size_t happened_event_count = 0;
- InvalidFdArg read_arg;
- read_arg.expected_events = FDE_READ | FDE_ERROR;
- read_arg.happened_event_count = &happened_event_count;
- read_arg.fde = fdevent_create(INVALID_READ_FD, InvalidFdEventCallback, &read_arg);
- fdevent_add(read_arg.fde, FDE_READ);
-
- const int INVALID_WRITE_FD = std::numeric_limits<int>::max();
- InvalidFdArg write_arg;
- write_arg.expected_events = FDE_READ | FDE_ERROR;
- write_arg.happened_event_count = &happened_event_count;
- write_arg.fde = fdevent_create(INVALID_WRITE_FD, InvalidFdEventCallback, &write_arg);
- fdevent_add(write_arg.fde, FDE_WRITE);
- fdevent_loop();
-}
-
-TEST_F(FdeventTest, invalid_fd) {
- std::thread thread(InvalidFdThreadFunc);
- thread.join();
-}
-
TEST_F(FdeventTest, run_on_main_thread) {
std::vector<int> vec;
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index b08a13b..987f994 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -33,6 +33,7 @@
// Include this before open/close/unlink are defined as macros below.
#include <android-base/errors.h>
#include <android-base/macros.h>
+#include <android-base/off64_t.h>
#include <android-base/unique_fd.h>
#include <android-base/utf8.h>
diff --git a/base/include/android-base/endian.h b/base/include/android-base/endian.h
index 10efaa3..2d0f614 100644
--- a/base/include/android-base/endian.h
+++ b/base/include/android-base/endian.h
@@ -18,6 +18,9 @@
/* A cross-platform equivalent of bionic's <sys/endian.h>. */
+/* For __BIONIC__ and __GLIBC__ */
+#include <sys/cdefs.h>
+
#if defined(__BIONIC__)
#include <sys/endian.h>
@@ -38,6 +41,9 @@
#define betoh16(x) be16toh(x)
#define betoh32(x) be32toh(x)
#define betoh64(x) be64toh(x)
+#define letoh16(x) le16toh(x)
+#define letoh32(x) le32toh(x)
+#define letoh64(x) le64toh(x)
#else
@@ -45,10 +51,8 @@
/* macOS has some of the basics. */
#include <sys/_endian.h>
#else
-/* Windows really has nothing. */
-#define LITTLE_ENDIAN __LITTLE_ENDIAN
-#define BIG_ENDIAN __BIG_ENDIAN
-#define BYTE_ORDER __BYTE_ORDER
+/* Windows has even less. */
+#include <sys/param.h>
#define htons(x) __builtin_bswap16(x)
#define htonl(x) __builtin_bswap32(x)
#define ntohs(x) __builtin_bswap16(x)
diff --git a/base/include/android-base/mapped_file.h b/base/include/android-base/mapped_file.h
index 2ab49ab..6a19f1b 100644
--- a/base/include/android-base/mapped_file.h
+++ b/base/include/android-base/mapped_file.h
@@ -28,15 +28,17 @@
#include <windows.h>
#define PROT_READ 1
#define PROT_WRITE 2
+using os_handle = HANDLE;
#else
#include <sys/mman.h>
+using os_handle = int;
#endif
namespace android {
namespace base {
/**
- * A region of a file mapped into memory, also known as MmapFile.
+ * A region of a file mapped into memory (for grepping: also known as MmapFile or file mapping).
*/
class MappedFile {
public:
@@ -49,16 +51,33 @@
int prot);
/**
+ * Same thing, but using the raw OS file handle instead of a CRT wrapper.
+ */
+ static MappedFile FromOsHandle(os_handle h, off64_t offset, size_t length, int prot);
+
+ /**
* Removes the mapping.
*/
~MappedFile();
- char* data() { return base_ + offset_; }
- size_t size() { return size_; }
+ /**
+ * Not copyable but movable.
+ */
+ MappedFile(MappedFile&& other);
+ MappedFile& operator=(MappedFile&& other);
+
+ char* data() const { return base_ + offset_; }
+ size_t size() const { return size_; }
+
+ bool isValid() const { return base_ != nullptr; }
+
+ explicit operator bool() const { return isValid(); }
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(MappedFile);
+ void Close();
+
char* base_;
size_t size_;
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
index f60de56..862b73b 100644
--- a/base/mapped_file.cpp
+++ b/base/mapped_file.cpp
@@ -16,13 +16,15 @@
#include "android-base/mapped_file.h"
-#include <errno.h>
+#include <utility>
-#include "android-base/unique_fd.h"
+#include <errno.h>
namespace android {
namespace base {
+static constexpr char kEmptyBuffer[] = {'0'};
+
static off64_t InitPageSize() {
#if defined(_WIN32)
SYSTEM_INFO si;
@@ -35,51 +37,86 @@
std::unique_ptr<MappedFile> MappedFile::FromFd(borrowed_fd fd, off64_t offset, size_t length,
int prot) {
- static off64_t page_size = InitPageSize();
+#if defined(_WIN32)
+ auto file =
+ FromOsHandle(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), offset, length, prot);
+#else
+ auto file = FromOsHandle(fd.get(), offset, length, prot);
+#endif
+ return file ? std::make_unique<MappedFile>(std::move(file)) : std::unique_ptr<MappedFile>{};
+}
+
+MappedFile MappedFile::FromOsHandle(os_handle h, off64_t offset, size_t length, int prot) {
+ static const off64_t page_size = InitPageSize();
size_t slop = offset % page_size;
off64_t file_offset = offset - slop;
off64_t file_length = length + slop;
#if defined(_WIN32)
- HANDLE handle =
- CreateFileMapping(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), nullptr,
- (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
+ HANDLE handle = CreateFileMappingW(
+ h, nullptr, (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
if (handle == nullptr) {
// http://b/119818070 "app crashes when reading asset of zero length".
// Return a MappedFile that's only valid for reading the size.
- if (length == 0) {
- return std::unique_ptr<MappedFile>(new MappedFile{nullptr, 0, 0, nullptr});
+ if (length == 0 && ::GetLastError() == ERROR_FILE_INVALID) {
+ return MappedFile{const_cast<char*>(kEmptyBuffer), 0, 0, nullptr};
}
- return nullptr;
+ return MappedFile(nullptr, 0, 0, nullptr);
}
void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
file_offset, file_length);
if (base == nullptr) {
CloseHandle(handle);
- return nullptr;
+ return MappedFile(nullptr, 0, 0, nullptr);
}
- return std::unique_ptr<MappedFile>(
- new MappedFile{static_cast<char*>(base), length, slop, handle});
+ return MappedFile{static_cast<char*>(base), length, slop, handle};
#else
- void* base = mmap(nullptr, file_length, prot, MAP_SHARED, fd.get(), file_offset);
+ void* base = mmap(nullptr, file_length, prot, MAP_SHARED, h, file_offset);
if (base == MAP_FAILED) {
// http://b/119818070 "app crashes when reading asset of zero length".
// mmap fails with EINVAL for a zero length region.
if (errno == EINVAL && length == 0) {
- return std::unique_ptr<MappedFile>(new MappedFile{nullptr, 0, 0});
+ return MappedFile{const_cast<char*>(kEmptyBuffer), 0, 0};
}
- return nullptr;
+ return MappedFile(nullptr, 0, 0);
}
- return std::unique_ptr<MappedFile>(new MappedFile{static_cast<char*>(base), length, slop});
+ return MappedFile{static_cast<char*>(base), length, slop};
#endif
}
+MappedFile::MappedFile(MappedFile&& other)
+ : base_(std::exchange(other.base_, nullptr)),
+ size_(std::exchange(other.size_, 0)),
+ offset_(std::exchange(other.offset_, 0))
+#ifdef _WIN32
+ ,
+ handle_(std::exchange(other.handle_, nullptr))
+#endif
+{
+}
+
+MappedFile& MappedFile::operator=(MappedFile&& other) {
+ Close();
+ base_ = std::exchange(other.base_, nullptr);
+ size_ = std::exchange(other.size_, 0);
+ offset_ = std::exchange(other.offset_, 0);
+#ifdef _WIN32
+ handle_ = std::exchange(other.handle_, nullptr);
+#endif
+ return *this;
+}
+
MappedFile::~MappedFile() {
+ Close();
+}
+
+void MappedFile::Close() {
#if defined(_WIN32)
- if (base_ != nullptr) UnmapViewOfFile(base_);
+ if (base_ != nullptr && size_ != 0) UnmapViewOfFile(base_);
if (handle_ != nullptr) CloseHandle(handle_);
+ handle_ = nullptr;
#else
- if (base_ != nullptr) munmap(base_, size_ + offset_);
+ if (base_ != nullptr && size_ != 0) munmap(base_, size_ + offset_);
#endif
base_ = nullptr;
diff --git a/base/mapped_file_test.cpp b/base/mapped_file_test.cpp
index cfde73c..3629108 100644
--- a/base/mapped_file_test.cpp
+++ b/base/mapped_file_test.cpp
@@ -44,5 +44,8 @@
ASSERT_TRUE(tf.fd != -1);
auto m = android::base::MappedFile::FromFd(tf.fd, 4096, 0, PROT_READ);
- ASSERT_EQ(0u, m->size());
+ ASSERT_NE(nullptr, m);
+ EXPECT_TRUE((bool)*m);
+ EXPECT_EQ(0u, m->size());
+ EXPECT_NE(nullptr, m->data());
}
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index e01e39b..b3f2d5f 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -56,8 +56,16 @@
if (!path) {
return false;
}
+
+ CreateLogicalPartitionParams params = {
+ .block_device = *path,
+ .metadata_slot = slot_number,
+ .partition_name = partition_name,
+ .force_writable = true,
+ .timeout_ms = 5s,
+ };
std::string dm_path;
- if (!CreateLogicalPartition(path->c_str(), slot_number, partition_name, true, 5s, &dm_path)) {
+ if (!CreateLogicalPartition(params, &dm_path)) {
LOG(ERROR) << "Could not map partition: " << partition_name;
return false;
}
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 04ba0bf..1b85b47 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -52,29 +52,36 @@
using DmTargetZero = android::dm::DmTargetZero;
using DmTargetLinear = android::dm::DmTargetLinear;
-static bool GetPhysicalPartitionDevicePath(const LpMetadata& metadata,
+static bool GetPhysicalPartitionDevicePath(const IPartitionOpener& opener,
+ const LpMetadata& metadata,
const LpMetadataBlockDevice& block_device,
- const std::string& super_device,
- std::string* result) {
- // Note: device-mapper will not accept symlinks, so we must use realpath
- // here.
- std::string name = GetBlockDevicePartitionName(block_device);
- std::string path = "/dev/block/by-name/" + name;
+ const std::string& super_device, std::string* result) {
// If the super device is the source of this block device's metadata,
// make sure we use the correct super device (and not just "super",
// which might not exist.)
+ std::string name = GetBlockDevicePartitionName(block_device);
+ std::string dev_string = opener.GetDeviceString(name);
if (GetMetadataSuperBlockDevice(metadata) == &block_device) {
- path = super_device;
+ dev_string = opener.GetDeviceString(super_device);
}
- if (!android::base::Realpath(path, result)) {
- PERROR << "realpath: " << path;
- return false;
+
+ // Note: device-mapper will not accept symlinks, so we must use realpath
+ // here. If the device string is a major:minor sequence, we don't need to
+ // to call Realpath (it would not work anyway).
+ if (android::base::StartsWith(dev_string, "/")) {
+ if (!android::base::Realpath(dev_string, result)) {
+ PERROR << "realpath: " << dev_string;
+ return false;
+ }
+ } else {
+ *result = dev_string;
}
return true;
}
-static bool CreateDmTable(const LpMetadata& metadata, const LpMetadataPartition& partition,
- const std::string& super_device, DmTable* table) {
+static bool CreateDmTable(const IPartitionOpener& opener, const LpMetadata& metadata,
+ const LpMetadataPartition& partition, const std::string& super_device,
+ DmTable* table) {
uint64_t sector = 0;
for (size_t i = 0; i < partition.num_extents; i++) {
const auto& extent = metadata.extents[partition.first_extent_index + i];
@@ -85,12 +92,13 @@
break;
case LP_TARGET_TYPE_LINEAR: {
const auto& block_device = metadata.block_devices[extent.target_source];
- std::string path;
- if (!GetPhysicalPartitionDevicePath(metadata, block_device, super_device, &path)) {
+ std::string dev_string;
+ if (!GetPhysicalPartitionDevicePath(opener, metadata, block_device, super_device,
+ &dev_string)) {
LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
return false;
}
- target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, path,
+ target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, dev_string,
extent.target_data);
break;
}
@@ -109,26 +117,6 @@
return true;
}
-static bool CreateLogicalPartition(const LpMetadata& metadata, const LpMetadataPartition& partition,
- bool force_writable, const std::chrono::milliseconds& timeout_ms,
- const std::string& super_device, std::string* path) {
- DeviceMapper& dm = DeviceMapper::Instance();
-
- DmTable table;
- if (!CreateDmTable(metadata, partition, super_device, &table)) {
- return false;
- }
- if (force_writable) {
- table.set_readonly(false);
- }
- std::string name = GetPartitionName(partition);
- if (!dm.CreateDevice(name, table, path, timeout_ms)) {
- return false;
- }
- LINFO << "Created logical partition " << name << " on device " << *path;
- return true;
-}
-
bool CreateLogicalPartitions(const std::string& block_device) {
uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
auto metadata = ReadMetadata(block_device.c_str(), slot);
@@ -145,13 +133,20 @@
}
bool CreateLogicalPartitions(const LpMetadata& metadata, const std::string& super_device) {
+ CreateLogicalPartitionParams params = {
+ .block_device = super_device,
+ .metadata = &metadata,
+ };
for (const auto& partition : metadata.partitions) {
if (!partition.num_extents) {
LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition);
continue;
}
- std::string path;
- if (!CreateLogicalPartition(metadata, partition, false, {}, super_device, &path)) {
+
+ params.partition = &partition;
+
+ std::string ignore_path;
+ if (!CreateLogicalPartition(params, &ignore_path)) {
LERROR << "Could not create logical partition: " << GetPartitionName(partition);
return false;
}
@@ -159,29 +154,62 @@
return true;
}
-bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
- const std::string& partition_name, bool force_writable,
- const std::chrono::milliseconds& timeout_ms, std::string* path) {
- for (const auto& partition : metadata.partitions) {
- if (GetPartitionName(partition) == partition_name) {
- return CreateLogicalPartition(metadata, partition, force_writable, timeout_ms,
- block_device, path);
+bool CreateLogicalPartition(const CreateLogicalPartitionParams& params, std::string* path) {
+ const LpMetadata* metadata = params.metadata;
+
+ // Read metadata if needed.
+ std::unique_ptr<LpMetadata> local_metadata;
+ if (!metadata) {
+ if (!params.metadata_slot) {
+ LOG(ERROR) << "Either metadata or a metadata slot must be specified.";
+ return false;
+ }
+ auto slot = *params.metadata_slot;
+ if (local_metadata = ReadMetadata(params.block_device, slot); !local_metadata) {
+ LOG(ERROR) << "Could not read partition table for: " << params.block_device;
+ return false;
+ }
+ metadata = local_metadata.get();
+ }
+
+ // Find the partition by name if needed.
+ const LpMetadataPartition* partition = params.partition;
+ if (!partition) {
+ for (const auto& iter : metadata->partitions) {
+ if (GetPartitionName(iter) == params.partition_name) {
+ partition = &iter;
+ break;
+ }
+ }
+ if (!partition) {
+ LERROR << "Could not find any partition with name: " << params.partition_name;
+ return false;
}
}
- LERROR << "Could not find any partition with name: " << partition_name;
- return false;
-}
-bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
- const std::string& partition_name, bool force_writable,
- const std::chrono::milliseconds& timeout_ms, std::string* path) {
- auto metadata = ReadMetadata(block_device.c_str(), metadata_slot);
- if (!metadata) {
- LOG(ERROR) << "Could not read partition table.";
- return true;
+ PartitionOpener default_opener;
+ const IPartitionOpener* opener =
+ params.partition_opener ? params.partition_opener : &default_opener;
+
+ DmTable table;
+ if (!CreateDmTable(*opener, *metadata, *partition, params.block_device, &table)) {
+ return false;
}
- return CreateLogicalPartition(block_device, *metadata.get(), partition_name, force_writable,
- timeout_ms, path);
+ if (params.force_writable) {
+ table.set_readonly(false);
+ }
+
+ std::string device_name = params.device_name;
+ if (device_name.empty()) {
+ device_name = GetPartitionName(*partition);
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.CreateDevice(device_name, table, path, params.timeout_ms)) {
+ return false;
+ }
+ LINFO << "Created logical partition " << device_name << " on device " << *path;
+ return true;
}
bool UnmapDevice(const std::string& name) {
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 4ee7db9..d7ea81d 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -133,7 +133,7 @@
return ret | !rmdir(test_directory.c_str());
}
-// At less than 1% free space return value of false,
+// At less than 1% or 8MB of free space return value of false,
// means we will try to wrap with overlayfs.
bool fs_mgr_filesystem_has_space(const std::string& mount_point) {
// If we have access issues to find out space remaining, return true
@@ -145,9 +145,11 @@
return true;
}
- static constexpr int kPercentThreshold = 1; // 1%
+ static constexpr int kPercentThreshold = 1; // 1%
+ static constexpr unsigned long kSizeThreshold = 8 * 1024 * 1024; // 8MB
- return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100));
+ return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100)) &&
+ (vst.f_bfree * vst.f_bsize) >= kSizeThreshold;
}
const auto kPhysicalDevice = "/dev/block/by-name/"s;
@@ -959,9 +961,16 @@
}
if (changed || partition_create) {
- if (!CreateLogicalPartition(super_device, slot_number, partition_name, true, 10s,
- scratch_device))
+ CreateLogicalPartitionParams params = {
+ .block_device = super_device,
+ .metadata_slot = slot_number,
+ .partition_name = partition_name,
+ .force_writable = true,
+ .timeout_ms = 10s,
+ };
+ if (!CreateLogicalPartition(params, scratch_device)) {
return false;
+ }
if (change) *change = true;
}
@@ -1182,11 +1191,15 @@
if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
auto scratch_device = fs_mgr_overlayfs_scratch_device();
if (scratch_device.empty()) {
- auto slot_number = fs_mgr_overlayfs_slot_number();
- auto super_device = fs_mgr_overlayfs_super_device(slot_number);
- const auto partition_name = android::base::Basename(kScratchMountPoint);
- CreateLogicalPartition(super_device, slot_number, partition_name, true, 10s,
- &scratch_device);
+ auto metadata_slot = fs_mgr_overlayfs_slot_number();
+ CreateLogicalPartitionParams params = {
+ .block_device = fs_mgr_overlayfs_super_device(metadata_slot),
+ .metadata_slot = metadata_slot,
+ .partition_name = android::base::Basename(kScratchMountPoint),
+ .force_writable = true,
+ .timeout_ms = 10s,
+ };
+ CreateLogicalPartition(params, &scratch_device);
}
mount_scratch = fs_mgr_overlayfs_mount_scratch(scratch_device,
fs_mgr_overlayfs_scratch_mount_type());
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index a1dc2dc..8e2fdbb 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -29,6 +29,7 @@
#include <chrono>
#include <memory>
+#include <optional>
#include <string>
#include <vector>
@@ -49,22 +50,36 @@
// method for ReadMetadata and CreateLogicalPartitions.
bool CreateLogicalPartitions(const std::string& block_device);
-// Create a block device for a single logical partition, given metadata and
-// the partition name. On success, a path to the partition's block device is
-// returned. If |force_writable| is true, the "readonly" flag will be ignored
-// so the partition can be flashed.
-//
-// If |timeout_ms| is non-zero, then CreateLogicalPartition will block for the
-// given amount of time until the path returned in |path| is available.
-bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
- const std::string& partition_name, bool force_writable,
- const std::chrono::milliseconds& timeout_ms, std::string* path);
+struct CreateLogicalPartitionParams {
+ // Block device of the super partition.
+ std::string block_device;
-// Same as above, but with a given metadata object. Care should be taken that
-// the metadata represents a valid partition layout.
-bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
- const std::string& partition_name, bool force_writable,
- const std::chrono::milliseconds& timeout_ms, std::string* path);
+ // If |metadata| is null, the slot will be read using |metadata_slot|.
+ const LpMetadata* metadata = nullptr;
+ std::optional<uint32_t> metadata_slot;
+
+ // If |partition| is not set, it will be found via |partition_name|.
+ const LpMetadataPartition* partition = nullptr;
+ std::string partition_name;
+
+ // Force the device to be read-write even if it was specified as readonly
+ // in the metadata.
+ bool force_writable = false;
+
+ // If |timeout_ms| is non-zero, then CreateLogicalPartition will block for
+ // the given amount of time until the path returned in |path| is available.
+ std::chrono::milliseconds timeout_ms = {};
+
+ // If this is non-empty, it will override the device mapper name (by
+ // default the partition name will be used).
+ std::string device_name;
+
+ // If non-null, this will use the specified IPartitionOpener rather than
+ // the default one.
+ const IPartitionOpener* partition_opener = nullptr;
+};
+
+bool CreateLogicalPartition(const CreateLogicalPartitionParams& params, std::string* path);
// Destroy the block device for a logical partition, by name. If |timeout_ms|
// is non-zero, then this will block until the device path has been unlinked.
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index a4e0d76..e7a3ff2 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -79,6 +79,13 @@
return true;
}
+bool DeviceMapper::DeleteDeviceIfExists(const std::string& name) {
+ if (GetState(name) == DmDeviceState::INVALID) {
+ return true;
+ }
+ return DeleteDevice(name);
+}
+
bool DeviceMapper::DeleteDevice(const std::string& name) {
struct dm_ioctl io;
InitIo(&io, name);
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index cf306f3..e25ce7f 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -89,6 +89,7 @@
// Removes a device mapper device with the given name.
// Returns 'true' on success, false otherwise.
bool DeleteDevice(const std::string& name);
+ bool DeleteDeviceIfExists(const std::string& name);
// Fetches and returns the complete state of the underlying device mapper
// device with given name.
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 777743c..77c9c28 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -140,7 +140,7 @@
}
if (opener) {
for (size_t i = 0; i < builder->block_devices_.size(); i++) {
- std::string partition_name = GetBlockDevicePartitionName(builder->block_devices_[i]);
+ std::string partition_name = builder->GetBlockDevicePartitionName(i);
BlockDeviceInfo device_info;
if (opener->GetInfo(partition_name, &device_info)) {
builder->UpdateBlockDeviceInfo(i, device_info);
@@ -164,13 +164,20 @@
// name and system properties.
// See comments for UpdateMetadataForOtherSuper.
auto super_device = GetMetadataSuperBlockDevice(*metadata.get());
- if (GetBlockDevicePartitionName(*super_device) != "super" &&
+ if (android::fs_mgr::GetBlockDevicePartitionName(*super_device) != "super" &&
IsRetrofitDynamicPartitionsDevice()) {
if (!UpdateMetadataForOtherSuper(metadata.get(), source_slot_number, target_slot_number)) {
return nullptr;
}
}
+ if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false)) {
+ if (!UpdateMetadataForInPlaceSnapshot(metadata.get(), source_slot_number,
+ target_slot_number)) {
+ return nullptr;
+ }
+ }
+
return New(*metadata.get(), &opener);
}
@@ -192,7 +199,8 @@
// Translate block devices.
auto source_block_devices = std::move(metadata->block_devices);
for (const auto& source_block_device : source_block_devices) {
- std::string partition_name = GetBlockDevicePartitionName(source_block_device);
+ std::string partition_name =
+ android::fs_mgr::GetBlockDevicePartitionName(source_block_device);
std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
if (slot_suffix.empty() || slot_suffix != source_slot_suffix) {
// This should never happen. It means that the source metadata
@@ -375,7 +383,7 @@
block_devices_.emplace_back(out);
}
}
- if (GetBlockDevicePartitionName(block_devices_[0]) != super_partition) {
+ if (GetBlockDevicePartitionName(0) != super_partition) {
LERROR << "No super partition was specified.";
return false;
}
@@ -458,7 +466,7 @@
return nullptr;
}
-PartitionGroup* MetadataBuilder::FindGroup(const std::string& group_name) {
+PartitionGroup* MetadataBuilder::FindGroup(std::string_view group_name) {
for (const auto& group : groups_) {
if (group->name() == group_name) {
return group.get();
@@ -582,8 +590,7 @@
CHECK_NE(sectors_per_block, 0);
CHECK(sectors_needed % sectors_per_block == 0);
- if (IsABDevice() && !IsRetrofitMetadata() &&
- GetPartitionSlotSuffix(partition->name()) == "_b") {
+ if (IsABDevice() && ShouldHalveSuper() && GetPartitionSlotSuffix(partition->name()) == "_b") {
// Allocate "a" partitions top-down and "b" partitions bottom-up, to
// minimize fragmentation during OTA.
free_regions = PrioritizeSecondHalfOfSuper(free_regions);
@@ -849,7 +856,7 @@
bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name,
uint32_t* index) const {
for (size_t i = 0; i < block_devices_.size(); i++) {
- if (GetBlockDevicePartitionName(block_devices_[i]) == partition_name) {
+ if (GetBlockDevicePartitionName(i) == partition_name) {
*index = i;
return true;
}
@@ -974,7 +981,8 @@
// Note: we don't compare alignment, since it's a performance thing and
// won't affect whether old extents continue to work.
return first.first_logical_sector == second.first_logical_sector && first.size == second.size &&
- GetBlockDevicePartitionName(first) == GetBlockDevicePartitionName(second);
+ android::fs_mgr::GetBlockDevicePartitionName(first) ==
+ android::fs_mgr::GetBlockDevicePartitionName(second);
}
bool MetadataBuilder::ImportPartitions(const LpMetadata& metadata,
@@ -1056,8 +1064,9 @@
false);
}
-bool MetadataBuilder::IsRetrofitMetadata() const {
- return GetBlockDevicePartitionName(block_devices_[0]) != LP_METADATA_DEFAULT_PARTITION_NAME;
+bool MetadataBuilder::ShouldHalveSuper() const {
+ return GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
+ !IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false);
}
bool MetadataBuilder::AddLinearExtent(Partition* partition, const std::string& block_device,
@@ -1083,7 +1092,7 @@
return partitions;
}
-bool MetadataBuilder::ChangePartitionGroup(Partition* partition, const std::string& group_name) {
+bool MetadataBuilder::ChangePartitionGroup(Partition* partition, std::string_view group_name) {
if (!FindGroup(group_name)) {
LERROR << "Partition cannot change to unknown group: " << group_name;
return false;
@@ -1121,5 +1130,11 @@
return true;
}
+std::string MetadataBuilder::GetBlockDevicePartitionName(uint64_t index) const {
+ return index < block_devices_.size()
+ ? android::fs_mgr::GetBlockDevicePartitionName(block_devices_[index])
+ : "";
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 6d27873..b3c13e6 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -35,7 +35,7 @@
IPropertyFetcher::OverrideForTesting(std::make_unique<NiceMock<MockPropertyFetcher>>());
}
-MockPropertyFetcher* GetMockedInstance() {
+MockPropertyFetcher* GetMockedPropertyFetcher() {
return static_cast<MockPropertyFetcher*>(IPropertyFetcher::GetInstance());
}
@@ -789,7 +789,7 @@
// A and B slots should be allocated from separate halves of the partition,
// to mitigate allocating too many extents. (b/120433288)
- ON_CALL(*GetMockedInstance(), GetProperty("ro.boot.slot_suffix", _))
+ ON_CALL(*GetMockedPropertyFetcher(), GetProperty("ro.boot.slot_suffix", _))
.WillByDefault(Return("_a"));
auto builder = MetadataBuilder::New(device_info, 65536, 2);
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 3b229bd..c3f6e91 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -128,7 +128,7 @@
private:
void ShrinkTo(uint64_t aligned_size);
- void set_group_name(const std::string& group_name) { group_name_ = group_name; }
+ void set_group_name(std::string_view group_name) { group_name_ = group_name; }
std::string name_;
std::string group_name_;
@@ -227,7 +227,7 @@
Partition* FindPartition(const std::string& name);
// Find a group by name. If no group is found, nullptr is returned.
- PartitionGroup* FindGroup(const std::string& name);
+ PartitionGroup* FindGroup(std::string_view name);
// Add a predetermined extent to a partition.
bool AddLinearExtent(Partition* partition, const std::string& block_device,
@@ -252,7 +252,7 @@
// the metadata is exported, to avoid errors during potential group and
// size shuffling operations. This will return false if the new group does
// not exist.
- bool ChangePartitionGroup(Partition* partition, const std::string& group_name);
+ bool ChangePartitionGroup(Partition* partition, std::string_view group_name);
// Changes the size of a partition group. Size constraints will not be
// checked until metadata is exported, to avoid errors during group
@@ -287,6 +287,9 @@
// Return true if a block device is found, else false.
bool HasBlockDevice(const std::string& partition_name) const;
+ // Return the name of the block device at |index|.
+ std::string GetBlockDevicePartitionName(uint64_t index) const;
+
private:
MetadataBuilder();
MetadataBuilder(const MetadataBuilder&) = delete;
@@ -314,8 +317,8 @@
// Return true if the device is retrofitting dynamic partitions.
static bool IsRetrofitDynamicPartitionsDevice();
- // Return true if "this" metadata represents a metadata on a retrofit device.
- bool IsRetrofitMetadata() const;
+ // Return true if _b partitions should be prioritized at the second half of the device.
+ bool ShouldHalveSuper() const;
bool ValidatePartitionGroups() const;
diff --git a/fs_mgr/liblp/include/liblp/partition_opener.h b/fs_mgr/liblp/include/liblp/partition_opener.h
index e506bd5..7c9100b 100644
--- a/fs_mgr/liblp/include/liblp/partition_opener.h
+++ b/fs_mgr/liblp/include/liblp/partition_opener.h
@@ -62,6 +62,11 @@
// Return block device information about the given named physical partition.
// The name can be an absolute path if the full path is already known.
virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const = 0;
+
+ // Return a path that can be used to pass the block device to device-mapper.
+ // This must either result in an absolute path, or a major:minor device
+ // sequence.
+ virtual std::string GetDeviceString(const std::string& partition_name) const = 0;
};
// Helper class to implement IPartitionOpener. If |partition_name| is not an
@@ -71,6 +76,7 @@
virtual android::base::unique_fd Open(const std::string& partition_name,
int flags) const override;
virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override;
+ virtual std::string GetDeviceString(const std::string& partition_name) const override;
};
} // namespace fs_mgr
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 2990863..3dace25 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -668,7 +668,7 @@
}
TEST(liblp, UpdateRetrofit) {
- ON_CALL(*GetMockedInstance(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
+ ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
.WillByDefault(Return(true));
unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
@@ -700,7 +700,7 @@
}
TEST(liblp, UpdateNonRetrofit) {
- ON_CALL(*GetMockedInstance(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
+ ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
.WillByDefault(Return(false));
unique_fd fd = CreateFlashedDisk();
diff --git a/fs_mgr/liblp/mock_property_fetcher.h b/fs_mgr/liblp/mock_property_fetcher.h
index eb91de2..0c28710 100644
--- a/fs_mgr/liblp/mock_property_fetcher.h
+++ b/fs_mgr/liblp/mock_property_fetcher.h
@@ -44,4 +44,4 @@
} // namespace fs_mgr
} // namespace android
-android::fs_mgr::MockPropertyFetcher* GetMockedInstance();
+android::fs_mgr::MockPropertyFetcher* GetMockedPropertyFetcher();
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
index cc4a882..f1e8fc2 100644
--- a/fs_mgr/liblp/partition_opener.cpp
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -100,5 +100,9 @@
return GetBlockDeviceInfo(path, info);
}
+std::string PartitionOpener::GetDeviceString(const std::string& partition_name) const {
+ return GetPartitionAbsolutePath(partition_name);
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 338b525..17f05aa 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -24,6 +24,10 @@
#include <sys/ioctl.h>
#endif
+#include <map>
+#include <string>
+#include <vector>
+
#include <android-base/file.h>
#include <ext4_utils/ext4_utils.h>
#include <openssl/sha.h>
@@ -182,6 +186,14 @@
return true;
}
+bool UpdatePartitionName(LpMetadataPartition* partition, const std::string& name) {
+ if (name.size() > sizeof(partition->name)) {
+ return false;
+ }
+ strncpy(partition->name, name.c_str(), sizeof(partition->name));
+ return true;
+}
+
bool SetBlockReadonly(int fd, bool readonly) {
#if defined(__linux__)
int val = readonly;
@@ -207,5 +219,70 @@
return base::unique_fd(open(path, flags));
}
+bool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot_number,
+ uint32_t target_slot_number) {
+ std::string source_slot_suffix = SlotSuffixForSlotNumber(source_slot_number);
+ std::string target_slot_suffix = SlotSuffixForSlotNumber(target_slot_number);
+
+ // There can be leftover groups with target suffix on retrofit devices.
+ // They are useless now, so delete.
+ std::vector<LpMetadataPartitionGroup*> new_group_ptrs;
+ for (auto& group : metadata->groups) {
+ std::string group_name = GetPartitionGroupName(group);
+ std::string slot_suffix = GetPartitionSlotSuffix(group_name);
+ // Don't add groups with target slot suffix.
+ if (slot_suffix == target_slot_suffix) continue;
+ // Replace source slot suffix with target slot suffix.
+ if (slot_suffix == source_slot_suffix) {
+ std::string new_name = group_name.substr(0, group_name.size() - slot_suffix.size()) +
+ target_slot_suffix;
+ if (!UpdatePartitionGroupName(&group, new_name)) {
+ LERROR << "Group name too long: " << new_name;
+ return false;
+ }
+ }
+ new_group_ptrs.push_back(&group);
+ }
+
+ std::vector<LpMetadataPartition*> new_partition_ptrs;
+ for (auto& partition : metadata->partitions) {
+ std::string partition_name = GetPartitionName(partition);
+ std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
+ // Don't add partitions with target slot suffix.
+ if (slot_suffix == target_slot_suffix) continue;
+ // Replace source slot suffix with target slot suffix.
+ if (slot_suffix == source_slot_suffix) {
+ std::string new_name =
+ partition_name.substr(0, partition_name.size() - slot_suffix.size()) +
+ target_slot_suffix;
+ if (!UpdatePartitionName(&partition, new_name)) {
+ LERROR << "Partition name too long: " << new_name;
+ return false;
+ }
+ }
+ // Update group index.
+ auto it = std::find(new_group_ptrs.begin(), new_group_ptrs.end(),
+ &metadata->groups[partition.group_index]);
+ if (it == new_group_ptrs.end()) {
+ LWARN << "Removing partition " << partition_name << " from group "
+ << GetPartitionGroupName(metadata->groups[partition.group_index])
+ << "; this partition should not belong to this group!";
+ continue; // not adding to new_partition_ptrs
+ }
+ partition.group_index = std::distance(new_group_ptrs.begin(), it);
+ new_partition_ptrs.push_back(&partition);
+ }
+
+ std::vector<LpMetadataPartition> new_partitions;
+ for (auto* p : new_partition_ptrs) new_partitions.emplace_back(std::move(*p));
+ metadata->partitions = std::move(new_partitions);
+
+ std::vector<LpMetadataPartitionGroup> new_groups;
+ for (auto* g : new_group_ptrs) new_groups.emplace_back(std::move(*g));
+ metadata->groups = std::move(new_groups);
+
+ return true;
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 96f1717..25ab66b 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -89,12 +89,17 @@
// Update names from C++ strings.
bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name);
bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name);
+bool UpdatePartitionName(LpMetadataPartition* partition, const std::string& name);
// Call BLKROSET ioctl on fd so that fd is readonly / read-writable.
bool SetBlockReadonly(int fd, bool readonly);
::android::base::unique_fd GetControlFileOrOpen(const char* path, int flags);
+// For Virtual A/B updates, modify |metadata| so that it can be written to |target_slot_number|.
+bool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot_number,
+ uint32_t target_slot_number);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index 15f7fff..cac3989 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -15,6 +15,7 @@
*/
#include <gtest/gtest.h>
+#include <liblp/builder.h>
#include <liblp/liblp.h>
#include "utility.h"
@@ -75,3 +76,81 @@
EXPECT_EQ(GetPartitionSlotSuffix("system_a"), "_a");
EXPECT_EQ(GetPartitionSlotSuffix("system_b"), "_b");
}
+
+namespace android {
+namespace fs_mgr {
+// Equality comparison for testing. In reality, equality of device_index doesn't
+// necessary mean equality of the block device.
+bool operator==(const LinearExtent& l, const LinearExtent& r) {
+ return l.device_index() == r.device_index() && l.physical_sector() == r.physical_sector() &&
+ l.end_sector() == r.end_sector();
+}
+} // namespace fs_mgr
+} // namespace android
+
+static std::vector<LinearExtent> GetPartitionExtents(Partition* p) {
+ std::vector<LinearExtent> extents;
+ for (auto&& extent : p->extents()) {
+ auto linear_extent = extent->AsLinearExtent();
+ if (!linear_extent) return {};
+ extents.push_back(*linear_extent);
+ }
+ return extents;
+}
+
+TEST(liblp, UpdateMetadataForInPlaceSnapshot) {
+ using std::unique_ptr;
+
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("group_a", 256 * 1024));
+ Partition* system_a = builder->AddPartition("system_a", "group_a", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system_a, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(system_a, 40 * 1024));
+ Partition* vendor_a = builder->AddPartition("vendor_a", "group_a", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(vendor_a, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(vendor_a, 20 * 1024));
+
+ ASSERT_TRUE(builder->AddGroup("group_b", 258 * 1024));
+ Partition* system_b = builder->AddPartition("system_b", "group_b", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system_b, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(system_b, 36 * 1024));
+ Partition* vendor_b = builder->AddPartition("vendor_b", "group_b", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(vendor_b, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(vendor_b, 32 * 1024));
+
+ auto system_a_extents = GetPartitionExtents(system_a);
+ ASSERT_FALSE(system_a_extents.empty());
+
+ auto vendor_a_extents = GetPartitionExtents(vendor_a);
+ ASSERT_FALSE(vendor_a_extents.empty());
+
+ auto metadata = builder->Export();
+ ASSERT_NE(nullptr, metadata);
+
+ ASSERT_TRUE(UpdateMetadataForInPlaceSnapshot(metadata.get(), 0, 1));
+
+ auto new_builder = MetadataBuilder::New(*metadata);
+ ASSERT_NE(nullptr, new_builder);
+
+ EXPECT_EQ(nullptr, new_builder->FindGroup("group_a"));
+ EXPECT_EQ(nullptr, new_builder->FindPartition("system_a"));
+ EXPECT_EQ(nullptr, new_builder->FindPartition("vendor_a"));
+
+ auto group_b = new_builder->FindGroup("group_b");
+ ASSERT_NE(nullptr, group_b);
+ ASSERT_EQ(256 * 1024, group_b->maximum_size());
+
+ auto new_system_b = new_builder->FindPartition("system_b");
+ ASSERT_NE(nullptr, new_system_b);
+ EXPECT_EQ(40 * 1024, new_system_b->size());
+ auto new_system_b_extents = GetPartitionExtents(new_system_b);
+ EXPECT_EQ(system_a_extents, new_system_b_extents);
+
+ auto new_vendor_b = new_builder->FindPartition("vendor_b");
+ ASSERT_NE(nullptr, new_vendor_b);
+ EXPECT_EQ(20 * 1024, new_vendor_b->size());
+ auto new_vendor_b_extents = GetPartitionExtents(new_vendor_b);
+ EXPECT_EQ(vendor_a_extents, new_vendor_b_extents);
+}
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 746987e..128a4f9 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -32,9 +32,14 @@
whole_static_libs: [
"libext2_uuid",
"libext4_utils",
- "libfiemap",
"libfstab",
],
+ header_libs: [
+ "libfiemap_headers",
+ ],
+ export_header_lib_headers: [
+ "libfiemap_headers",
+ ],
export_include_dirs: ["include"],
}
@@ -49,7 +54,7 @@
name: "libsnapshot",
defaults: ["libsnapshot_defaults"],
srcs: [":libsnapshot_sources"],
- static_libs: [
+ whole_static_libs: [
"libfiemap_binder",
],
}
@@ -59,6 +64,9 @@
defaults: ["libsnapshot_defaults"],
srcs: [":libsnapshot_sources"],
recovery_available: true,
+ whole_static_libs: [
+ "libfiemap_passthrough",
+ ],
}
cc_test {
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 4fb6808..1630ee5 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -219,9 +219,12 @@
bool WriteUpdateState(LockedFile* file, UpdateState state);
std::string GetStateFilePath() const;
+ enum class SnapshotState : int { Created, Merging, MergeCompleted };
+ static std::string to_string(SnapshotState state);
+
// This state is persisted per-snapshot in /metadata/ota/snapshots/.
struct SnapshotStatus {
- std::string state;
+ SnapshotState state;
uint64_t device_size;
uint64_t snapshot_size;
// These are non-zero when merging.
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 75a1f26..fef5c06 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -165,7 +165,7 @@
// actual backing image. This is harmless, since it'll get removed when
// CancelUpdate is called.
SnapshotStatus status = {
- .state = "created",
+ .state = SnapshotState::Created,
.device_size = device_size,
.snapshot_size = snapshot_size,
};
@@ -190,7 +190,7 @@
if (!ReadSnapshotStatus(lock, name, &status)) {
return false;
}
- if (status.state == "merge-completed") {
+ if (status.state == SnapshotState::MergeCompleted) {
LOG(ERROR) << "Should not create a snapshot device for " << name
<< " after merging has completed.";
return false;
@@ -313,7 +313,8 @@
// There may be an extra device, since the kernel doesn't let us have a
// snapshot and linear target in the same table.
auto dm_name = GetSnapshotDeviceName(name, status);
- if (name != dm_name && !dm.DeleteDevice(dm_name)) {
+ if (name != dm_name && dm.GetState(dm_name) != DmDeviceState::INVALID &&
+ !dm.DeleteDevice(dm_name)) {
LOG(ERROR) << "Could not delete inner snapshot device: " << dm_name;
return false;
}
@@ -422,8 +423,8 @@
if (!ReadSnapshotStatus(lock, name, &status)) {
return false;
}
- if (status.state != "created") {
- LOG(WARNING) << "Snapshot " << name << " has unexpected state: " << status.state;
+ if (status.state != SnapshotState::Created) {
+ LOG(WARNING) << "Snapshot " << name << " has unexpected state: " << to_string(status.state);
}
// After this, we return true because we technically did switch to a merge
@@ -433,7 +434,7 @@
return false;
}
- status.state = "merging";
+ status.state = SnapshotState::Merging;
DmTargetSnapshot::Status dm_status;
if (!QuerySnapshotStatus(dm_name, nullptr, &dm_status)) {
@@ -657,7 +658,7 @@
// rebooted after this check, the device will still be a snapshot-merge
// target. If the have rebooted, the device will now be a linear target,
// and we can try cleanup again.
- if (snapshot_status.state == "merge-complete" && !IsSnapshotDevice(dm_name)) {
+ if (snapshot_status.state == SnapshotState::MergeCompleted && !IsSnapshotDevice(dm_name)) {
// NB: It's okay if this fails now, we gave cleanup our best effort.
OnSnapshotMergeComplete(lock, name, snapshot_status);
return UpdateState::MergeCompleted;
@@ -678,7 +679,7 @@
// These two values are equal when merging is complete.
if (status.sectors_allocated != status.metadata_sectors) {
- if (snapshot_status.state == "merge-complete") {
+ if (snapshot_status.state == SnapshotState::MergeCompleted) {
LOG(ERROR) << "Snapshot " << name << " is merging after being marked merge-complete.";
return UpdateState::MergeFailed;
}
@@ -693,7 +694,7 @@
// This makes it simpler to reason about the next reboot: no matter what
// part of cleanup failed, first-stage init won't try to create another
// snapshot device for this partition.
- snapshot_status.state = "merge-complete";
+ snapshot_status.state = SnapshotState::MergeCompleted;
if (!WriteSnapshotStatus(lock, name, snapshot_status)) {
return UpdateState::MergeFailed;
}
@@ -1068,7 +1069,16 @@
return false;
}
- status->state = pieces[0];
+ if (pieces[0] == "created") {
+ status->state = SnapshotState::Created;
+ } else if (pieces[0] == "merging") {
+ status->state = SnapshotState::Merging;
+ } else if (pieces[0] == "merge-completed") {
+ status->state = SnapshotState::MergeCompleted;
+ } else {
+ LOG(ERROR) << "Unrecognized state " << pieces[0] << " for snapshot: " << name;
+ }
+
if (!android::base::ParseUint(pieces[1], &status->device_size)) {
LOG(ERROR) << "Invalid device size in status line for: " << path;
return false;
@@ -1088,6 +1098,20 @@
return true;
}
+std::string SnapshotManager::to_string(SnapshotState state) {
+ switch (state) {
+ case SnapshotState::Created:
+ return "created";
+ case SnapshotState::Merging:
+ return "merging";
+ case SnapshotState::MergeCompleted:
+ return "merge-completed";
+ default:
+ LOG(ERROR) << "Unknown snapshot state: " << (int)state;
+ return "unknown";
+ }
+}
+
bool SnapshotManager::WriteSnapshotStatus(LockedFile* lock, const std::string& name,
const SnapshotStatus& status) {
// The caller must take an exclusive lock to modify snapshots.
@@ -1102,7 +1126,7 @@
}
std::vector<std::string> pieces = {
- status.state,
+ to_string(status.state),
std::to_string(status.device_size),
std::to_string(status.snapshot_size),
std::to_string(status.sectors_allocated),
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 34ea331..438ec2f 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -36,6 +36,7 @@
using android::base::unique_fd;
using android::dm::DeviceMapper;
using android::dm::DmDeviceState;
+using android::fiemap::IImageManager;
using namespace std::chrono_literals;
using namespace std::string_literals;
@@ -48,9 +49,11 @@
void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
private:
- std::string slot_suffix_;
+ std::string slot_suffix_ = "_a";
};
+// These are not reset between each test because it's expensive to reconnect
+// to gsid each time.
std::unique_ptr<SnapshotManager> sm;
TestDeviceInfo* test_device = nullptr;
@@ -60,16 +63,14 @@
protected:
void SetUp() override {
+ ASSERT_TRUE(sm->EnsureImageManager());
+ image_manager_ = sm->image_manager();
+
test_device->set_slot_suffix("_a");
- if (sm->GetUpdateState() != UpdateState::None) {
- CleanupTestArtifacts();
- }
- ASSERT_TRUE(sm->BeginUpdate());
- ASSERT_TRUE(sm->EnsureImageManager());
+ CleanupTestArtifacts();
- image_manager_ = sm->image_manager();
- ASSERT_NE(image_manager_, nullptr);
+ ASSERT_TRUE(sm->BeginUpdate());
}
void TearDown() override {
@@ -81,20 +82,25 @@
void CleanupTestArtifacts() {
// Normally cancelling inside a merge is not allowed. Since these
// are tests, we don't care, destroy everything that might exist.
+ // Note we hardcode this list because of an annoying quirk: when
+ // completing a merge, the snapshot stops existing, so we can't
+ // get an accurate list to remove.
+ lock_ = nullptr;
+
std::vector<std::string> snapshots = {"test-snapshot"};
for (const auto& snapshot : snapshots) {
DeleteSnapshotDevice(snapshot);
- temp_images_.emplace_back(snapshot + "-cow");
+ DeleteBackingImage(image_manager_, snapshot + "-cow");
auto status_file = sm->GetSnapshotStatusFilePath(snapshot);
android::base::RemoveFileIfExists(status_file);
}
- // Remove all images.
- temp_images_.emplace_back("test-snapshot-cow");
- for (const auto& temp_image : temp_images_) {
- image_manager_->UnmapImageDevice(temp_image);
- image_manager_->DeleteBackingImage(temp_image);
+ // Remove all images. We hardcode the list of names since this can run
+ // before the test (when cleaning up from a crash).
+ std::vector<std::string> temp_partitions = {"base-device"};
+ for (const auto& partition : temp_partitions) {
+ DeleteBackingImage(image_manager_, partition);
}
if (sm->GetUpdateState() != UpdateState::None) {
@@ -112,23 +118,29 @@
if (!image_manager_->CreateBackingImage(name, size, false)) {
return false;
}
- temp_images_.emplace_back(name);
return image_manager_->MapImageDevice(name, 10s, path);
}
- bool DeleteSnapshotDevice(const std::string& snapshot) {
+ void DeleteSnapshotDevice(const std::string& snapshot) {
if (dm_.GetState(snapshot) != DmDeviceState::INVALID) {
- if (!dm_.DeleteDevice(snapshot)) return false;
+ ASSERT_TRUE(dm_.DeleteDevice(snapshot));
}
if (dm_.GetState(snapshot + "-inner") != DmDeviceState::INVALID) {
- if (!dm_.DeleteDevice(snapshot + "-inner")) return false;
+ ASSERT_TRUE(dm_.DeleteDevice(snapshot + "-inner"));
}
- return true;
+ }
+
+ void DeleteBackingImage(IImageManager* manager, const std::string& name) {
+ if (manager->IsImageMapped(name)) {
+ ASSERT_TRUE(manager->UnmapImageDevice(name));
+ }
+ if (manager->BackingImageExists(name)) {
+ ASSERT_TRUE(manager->DeleteBackingImage(name));
+ }
}
DeviceMapper& dm_;
std::unique_ptr<SnapshotManager::LockedFile> lock_;
- std::vector<std::string> temp_images_;
android::fiemap::IImageManager* image_manager_ = nullptr;
};
@@ -148,7 +160,7 @@
{
SnapshotManager::SnapshotStatus status;
ASSERT_TRUE(sm->ReadSnapshotStatus(lock_.get(), "test-snapshot", &status));
- ASSERT_EQ(status.state, "created");
+ ASSERT_EQ(status.state, SnapshotManager::SnapshotState::Created);
ASSERT_EQ(status.device_size, kDeviceSize);
ASSERT_EQ(status.snapshot_size, kDeviceSize);
}
@@ -279,7 +291,7 @@
ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeNeedsReboot);
// Forcefully delete the snapshot device, so it looks like we just rebooted.
- ASSERT_TRUE(DeleteSnapshotDevice("test-snapshot"));
+ DeleteSnapshotDevice("test-snapshot");
// Map snapshot should fail now, because we're in a merge-complete state.
ASSERT_TRUE(AcquireLock());
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 397d8e5..ed6d0e3 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -709,8 +709,9 @@
EXPECT_EQ "${lval}" "${rval}" ${*}
return
fi
- EXPECT_EQ "${lval}" "${rval}" ||
+ if ! EXPECT_EQ "${lval}" "${rval}"; then
die "${@}"
+ fi
}
[ "USAGE: check_ne <lval> <rval> [--warning [message]]
@@ -724,8 +725,9 @@
EXPECT_NE "${lval}" "${rval}" ${*}
return
fi
- EXPECT_NE "${lval}" "${rval}" ||
+ if ! EXPECT_NE "${lval}" "${rval}"; then
die "${@}"
+ fi
}
[ "USAGE: skip_administrative_mounts [data] < /proc/mounts
@@ -847,6 +849,8 @@
# Do something.
+# Collect characteristics of the device and report.
+
D=`get_property ro.serialno`
[ -n "${D}" ] || D=`get_property ro.boot.serialno`
[ -z "${D}" -o -n "${ANDROID_SERIAL}" ] || ANDROID_SERIAL=${D}
@@ -869,22 +873,42 @@
[ -z "${ACTIVE_SLOT}" ] ||
echo "${BLUE}[ INFO ]${NORMAL} active slot is ${ACTIVE_SLOT}" >&2
+# Acquire list of system partitions
+
+PARTITIONS=`adb_su cat /vendor/etc/fstab* |
+ skip_administrative_mounts |
+ sed -n "s@^\([^ ${TAB}/][^ ${TAB}/]*\)[ ${TAB}].*[, ${TAB}]ro[, ${TAB}].*@\1@p" |
+ sort -u |
+ tr '\n' ' '`
+PARTITIONS="${PARTITIONS:-system vendor}"
+# KISS (we do not support sub-mounts for system partitions currently)
+MOUNTS="`for i in ${PARTITIONS}; do
+ echo /${i}
+ done |
+ tr '\n' ' '`"
+echo "${BLUE}[ INFO ]${NORMAL} System Partitions list: ${PARTITIONS}" >&2
+
# Report existing partition sizes
-adb_sh ls -l /dev/block/by-name/ </dev/null 2>/dev/null |
+adb_sh ls -l /dev/block/by-name/ /dev/block/mapper/ </dev/null 2>/dev/null |
sed -n 's@.* \([^ ]*\) -> /dev/block/\([^ ]*\)$@\1 \2@p' |
while read name device; do
- case ${name} in
- system_[ab] | system | vendor_[ab] | vendor | super | cache)
- case ${device} in
- sd*)
- device=${device%%[0-9]*}/${device}
- ;;
- esac
- size=`adb_su cat /sys/block/${device}/size 2>/dev/null </dev/null` &&
- size=`expr ${size} / 2` &&
- echo "${BLUE}[ INFO ]${NORMAL} partition ${name} device ${device} size ${size}K" >&2
+ [ super = ${name} -o cache = ${name} ] ||
+ (
+ for i in ${PARTITIONS}; do
+ [ ${i} = ${name} -o ${i} = ${name%_[ab]} ] && exit
+ done
+ exit 1
+ ) ||
+ continue
+
+ case ${device} in
+ sd*)
+ device=${device%%[0-9]*}/${device}
;;
esac
+ size=`adb_su cat /sys/block/${device}/size 2>/dev/null </dev/null` &&
+ size=`expr ${size} / 2` &&
+ echo "${BLUE}[ INFO ]${NORMAL} partition ${name} device ${device} size ${size}K" >&2
done
# If reboot too soon after fresh flash, could trip device update failure logic
@@ -1107,7 +1131,7 @@
# Feed log with selinux denials as baseline before overlays
adb_unroot
-adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
+adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null
adb_root
D=`adb remount 2>&1`
@@ -1199,21 +1223,19 @@
# Check something.
-echo "${GREEN}[ RUN ]${NORMAL} push content to /system and /vendor" >&2
+echo "${GREEN}[ RUN ]${NORMAL} push content to ${MOUNTS}" >&2
A="Hello World! $(date)"
-echo "${A}" | adb_sh cat - ">/system/hello"
+for i in ${MOUNTS}; do
+ echo "${A}" | adb_sh cat - ">${i}/hello"
+ B="`adb_cat ${i}/hello`" ||
+ die "${i#/} hello"
+ check_eq "${A}" "${B}" ${i} before reboot
+done
echo "${A}" | adb_sh cat - ">/system/priv-app/hello"
-echo "${A}" | adb_sh cat - ">/vendor/hello"
-B="`adb_cat /system/hello`" ||
- die "system hello"
-check_eq "${A}" "${B}" /system before reboot
B="`adb_cat /system/priv-app/hello`" ||
die "system priv-app hello"
check_eq "${A}" "${B}" /system/priv-app before reboot
-B="`adb_cat /vendor/hello`" ||
- die "vendor hello"
-check_eq "${A}" "${B}" /vendor before reboot
SYSTEM_DEVT=`adb_sh stat --format=%D /system/hello </dev/null`
VENDOR_DEVT=`adb_sh stat --format=%D /vendor/hello </dev/null`
SYSTEM_INO=`adb_sh stat --format=%i /system/hello </dev/null`
@@ -1288,24 +1310,23 @@
echo "${GREEN}[ OK ]${NORMAL} /vendor content correct MAC after reboot" >&2
# Feed unprivileged log with selinux denials as a result of overlays
wait_for_screen
- adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
+ adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null
fi
-B="`adb_cat /system/hello`"
-check_eq "${A}" "${B}" /system after reboot
# If overlayfs has a nested security problem, this will fail.
B="`adb_ls /system/`" ||
- dir "adb ls /system"
+ die "adb ls /system"
[ X"${B}" != X"${B#*priv-app}" ] ||
- dir "adb ls /system/priv-app"
+ die "adb ls /system/priv-app"
B="`adb_cat /system/priv-app/hello`"
check_eq "${A}" "${B}" /system/priv-app after reboot
-echo "${GREEN}[ OK ]${NORMAL} /system content remains after reboot" >&2
# Only root can read vendor if sepolicy permissions are as expected.
adb_root ||
die "adb root"
-B="`adb_cat /vendor/hello`"
-check_eq "${A}" "${B}" vendor after reboot
-echo "${GREEN}[ OK ]${NORMAL} /vendor content remains after reboot" >&2
+for i in ${MOUNTS}; do
+ B="`adb_cat ${i}/hello`"
+ check_eq "${A}" "${B}" ${i#/} after reboot
+ echo "${GREEN}[ OK ]${NORMAL} ${i} content remains after reboot" >&2
+done
check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" system devt after reboot
check_eq "${VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/hello </dev/null`" vendor devt after reboot
@@ -1316,7 +1337,7 @@
check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" devt for su after reboot
# Feed log with selinux denials as a result of overlays
-adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
+adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null
# Check if the updated libc.so is persistent after reboot.
adb_root &&
@@ -1421,9 +1442,9 @@
B="`adb_cat /system/hello`"
check_eq "${A}" "${B}" system after flash vendor
B="`adb_ls /system/`" ||
- dir "adb ls /system"
+ die "adb ls /system"
[ X"${B}" != X"${B#*priv-app}" ] ||
- dir "adb ls /system/priv-app"
+ die "adb ls /system/priv-app"
B="`adb_cat /system/priv-app/hello`"
check_eq "${A}" "${B}" system/priv-app after flash vendor
adb_root ||
@@ -1476,6 +1497,9 @@
check_eq "cat: /system/priv-app/hello: No such file or directory" "${B}" after rm
B="`adb_cat /vendor/hello`"
check_eq "cat: /vendor/hello: No such file or directory" "${B}" after rm
+for i in ${MOUNTS}; do
+ adb_sh rm ${i}/hello </dev/null 2>/dev/null || true
+done
if ${is_bootloader_fastboot} && [ -n "${scratch_partition}" ]; then
diff --git a/init/Android.bp b/init/Android.bp
index 66a3172..1e38bf0 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -270,11 +270,13 @@
shared_libs: [
"libcutils",
"libhidl-gen-utils",
- "libjsoncpp",
"liblog",
"libprocessgroup",
"libprotobuf-cpp-lite",
],
+ header_libs: [
+ "libjsoncpp_headers",
+ ],
srcs: [
"action.cpp",
"action_manager.cpp",
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
index 7f5768c..9a33b55 100644
--- a/libnativeloader/library_namespaces.cpp
+++ b/libnativeloader/library_namespaces.cpp
@@ -124,7 +124,7 @@
// we might as well end up loading them from /system/lib or /product/lib
// For now we rely on CTS test to catch things like this but
// it should probably be addressed in the future.
- for (const auto& soname : android::base::Split(default_public_libraries(), ":")) {
+ for (const auto& soname : android::base::Split(preloadable_public_libraries(), ":")) {
LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
"Error preloading public library %s: %s", soname.c_str(), dlerror());
}
diff --git a/libnativeloader/library_namespaces.h b/libnativeloader/library_namespaces.h
index 84cabde..7b3efff 100644
--- a/libnativeloader/library_namespaces.h
+++ b/libnativeloader/library_namespaces.h
@@ -38,7 +38,7 @@
// object for a given ClassLoader.
class LibraryNamespaces {
public:
- LibraryNamespaces() : initialized_(false) {}
+ LibraryNamespaces() : initialized_(false), app_main_namespace_(nullptr) {}
LibraryNamespaces(LibraryNamespaces&&) = default;
LibraryNamespaces(const LibraryNamespaces&) = delete;
diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp
index a641109..75255b6 100644
--- a/libnativeloader/native_loader_test.cpp
+++ b/libnativeloader/native_loader_test.cpp
@@ -29,6 +29,7 @@
#include "public_libraries.h"
using namespace ::testing;
+using namespace ::android::nativeloader::internal;
namespace android {
namespace nativeloader {
@@ -289,7 +290,7 @@
void SetExpectations() {
std::vector<std::string> default_public_libs =
- android::base::Split(default_public_libraries(), ":");
+ android::base::Split(preloadable_public_libraries(), ":");
for (auto l : default_public_libs) {
EXPECT_CALL(*mock, dlopen(StrEq(l.c_str()), RTLD_NOW | RTLD_NODELETE))
.WillOnce(Return(any_nonnull));
@@ -576,5 +577,87 @@
INSTANTIATE_TEST_SUITE_P(NativeLoaderTests_Create, NativeLoaderTest_Create, testing::Bool());
+const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
+ [](const struct ConfigEntry&) -> Result<bool> { return true; };
+
+TEST(NativeLoaderConfigParser, NamesAndComments) {
+ const char file_content[] = R"(
+######
+
+libA.so
+#libB.so
+
+
+ libC.so
+libD.so
+ #### libE.so
+)";
+ const std::vector<std::string> expected_result = {"libA.so", "libC.so", "libD.so"};
+ Result<std::vector<std::string>> result = ParseConfig(file_content, always_true);
+ ASSERT_TRUE(result) << result.error().message();
+ ASSERT_EQ(expected_result, *result);
+}
+
+TEST(NativeLoaderConfigParser, WithBitness) {
+ const char file_content[] = R"(
+libA.so 32
+libB.so 64
+libC.so
+)";
+#if defined(__LP64__)
+ const std::vector<std::string> expected_result = {"libB.so", "libC.so"};
+#else
+ const std::vector<std::string> expected_result = {"libA.so", "libC.so"};
+#endif
+ Result<std::vector<std::string>> result = ParseConfig(file_content, always_true);
+ ASSERT_TRUE(result) << result.error().message();
+ ASSERT_EQ(expected_result, *result);
+}
+
+TEST(NativeLoaderConfigParser, WithNoPreload) {
+ const char file_content[] = R"(
+libA.so nopreload
+libB.so nopreload
+libC.so
+)";
+
+ const std::vector<std::string> expected_result = {"libC.so"};
+ Result<std::vector<std::string>> result =
+ ParseConfig(file_content,
+ [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; });
+ ASSERT_TRUE(result) << result.error().message();
+ ASSERT_EQ(expected_result, *result);
+}
+
+TEST(NativeLoaderConfigParser, WithNoPreloadAndBitness) {
+ const char file_content[] = R"(
+libA.so nopreload 32
+libB.so 64 nopreload
+libC.so 32
+libD.so 64
+libE.so nopreload
+)";
+
+#if defined(__LP64__)
+ const std::vector<std::string> expected_result = {"libD.so"};
+#else
+ const std::vector<std::string> expected_result = {"libC.so"};
+#endif
+ Result<std::vector<std::string>> result =
+ ParseConfig(file_content,
+ [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; });
+ ASSERT_TRUE(result) << result.error().message();
+ ASSERT_EQ(expected_result, *result);
+}
+
+TEST(NativeLoaderConfigParser, RejectMalformed) {
+ ASSERT_FALSE(ParseConfig("libA.so 32 64", always_true));
+ ASSERT_FALSE(ParseConfig("libA.so 32 32", always_true));
+ ASSERT_FALSE(ParseConfig("libA.so 32 nopreload 64", always_true));
+ ASSERT_FALSE(ParseConfig("32 libA.so nopreload", always_true));
+ ASSERT_FALSE(ParseConfig("nopreload libA.so 32", always_true));
+ ASSERT_FALSE(ParseConfig("libA.so nopreload # comment", always_true));
+}
+
} // namespace nativeloader
} // namespace android
diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp
index 6cee668..3694360 100644
--- a/libnativeloader/public_libraries.cpp
+++ b/libnativeloader/public_libraries.cpp
@@ -34,7 +34,8 @@
namespace android::nativeloader {
-using namespace std::string_literals;
+using namespace internal;
+using namespace ::std::string_literals;
using android::base::ErrnoError;
using android::base::Errorf;
using android::base::Result;
@@ -95,53 +96,21 @@
file_name->insert(insert_pos, vndk_version_str());
}
-const std::function<Result<void>(const std::string&)> always_true =
- [](const std::string&) -> Result<void> { return {}; };
+const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
+ [](const struct ConfigEntry&) -> Result<bool> { return true; };
Result<std::vector<std::string>> ReadConfig(
const std::string& configFile,
- const std::function<Result<void>(const std::string& /* soname */)>& check_soname) {
- // Read list of public native libraries from the config file.
+ const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) {
std::string file_content;
if (!base::ReadFileToString(configFile, &file_content)) {
return ErrnoError();
}
-
- std::vector<std::string> lines = base::Split(file_content, "\n");
-
- std::vector<std::string> sonames;
- for (auto& line : lines) {
- auto trimmed_line = base::Trim(line);
- if (trimmed_line[0] == '#' || trimmed_line.empty()) {
- continue;
- }
- size_t space_pos = trimmed_line.rfind(' ');
- if (space_pos != std::string::npos) {
- std::string type = trimmed_line.substr(space_pos + 1);
- if (type != "32" && type != "64") {
- return Errorf("Malformed line: {}", line);
- }
-#if defined(__LP64__)
- // Skip 32 bit public library.
- if (type == "32") {
- continue;
- }
-#else
- // Skip 64 bit public library.
- if (type == "64") {
- continue;
- }
-#endif
- trimmed_line.resize(space_pos);
- }
-
- auto ret = check_soname(trimmed_line);
- if (!ret) {
- return ret.error();
- }
- sonames.push_back(trimmed_line);
+ Result<std::vector<std::string>> result = ParseConfig(file_content, filter_fn);
+ if (!result) {
+ return Errorf("Cannot parse {}: {}", configFile, result.error().message());
}
- return sonames;
+ return result;
}
void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
@@ -165,13 +134,13 @@
config_file_path.c_str());
auto ret = ReadConfig(
- config_file_path, [&company_name](const std::string& soname) -> Result<void> {
- if (android::base::StartsWith(soname, "lib") &&
- android::base::EndsWith(soname, "." + company_name + ".so")) {
- return {};
+ config_file_path, [&company_name](const struct ConfigEntry& entry) -> Result<bool> {
+ if (android::base::StartsWith(entry.soname, "lib") &&
+ android::base::EndsWith(entry.soname, "." + company_name + ".so")) {
+ return true;
} else {
- return Errorf("Library name \"{}\" does not end with the company name {}.", soname,
- company_name);
+ return Errorf("Library name \"{}\" does not end with the company name {}.",
+ entry.soname, company_name);
}
});
if (ret) {
@@ -185,9 +154,16 @@
}
}
-static std::string InitDefaultPublicLibraries() {
+static std::string InitDefaultPublicLibraries(bool for_preload) {
std::string config_file = root_dir() + kDefaultPublicLibrariesFile;
- auto sonames = ReadConfig(config_file, always_true);
+ auto sonames =
+ ReadConfig(config_file, [&for_preload](const struct ConfigEntry& entry) -> Result<bool> {
+ if (for_preload) {
+ return !entry.nopreload;
+ } else {
+ return true;
+ }
+ });
if (!sonames) {
LOG_ALWAYS_FATAL("Error reading public native library list from \"%s\": %s",
config_file.c_str(), sonames.error().message().c_str());
@@ -290,8 +266,13 @@
} // namespace
+const std::string& preloadable_public_libraries() {
+ static std::string list = InitDefaultPublicLibraries(/*for_preload*/ true);
+ return list;
+}
+
const std::string& default_public_libraries() {
- static std::string list = InitDefaultPublicLibraries();
+ static std::string list = InitDefaultPublicLibraries(/*for_preload*/ false);
return list;
}
@@ -325,4 +306,61 @@
return list;
}
+namespace internal {
+// Exported for testing
+Result<std::vector<std::string>> ParseConfig(
+ const std::string& file_content,
+ const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) {
+ std::vector<std::string> lines = base::Split(file_content, "\n");
+
+ std::vector<std::string> sonames;
+ for (auto& line : lines) {
+ auto trimmed_line = base::Trim(line);
+ if (trimmed_line[0] == '#' || trimmed_line.empty()) {
+ continue;
+ }
+
+ std::vector<std::string> tokens = android::base::Split(trimmed_line, " ");
+ if (tokens.size() < 1 || tokens.size() > 3) {
+ return Errorf("Malformed line \"{}\"", line);
+ }
+ struct ConfigEntry entry = {.soname = "", .nopreload = false, .bitness = ALL};
+ size_t i = tokens.size();
+ while (i-- > 0) {
+ if (tokens[i] == "nopreload") {
+ entry.nopreload = true;
+ } else if (tokens[i] == "32" || tokens[i] == "64") {
+ if (entry.bitness != ALL) {
+ return Errorf("Malformed line \"{}\": bitness can be specified only once", line);
+ }
+ entry.bitness = tokens[i] == "32" ? ONLY_32 : ONLY_64;
+ } else {
+ if (i != 0) {
+ return Errorf("Malformed line \"{}\"", line);
+ }
+ entry.soname = tokens[i];
+ }
+ }
+
+ // skip 32-bit lib on 64-bit process and vice versa
+#if defined(__LP64__)
+ if (entry.bitness == ONLY_32) continue;
+#else
+ if (entry.bitness == ONLY_64) continue;
+#endif
+
+ Result<bool> ret = filter_fn(entry);
+ if (!ret) {
+ return ret.error();
+ }
+ if (*ret) {
+ // filter_fn has returned true.
+ sonames.push_back(entry.soname);
+ }
+ }
+ return sonames;
+}
+
+} // namespace internal
+
} // namespace android::nativeloader
diff --git a/libnativeloader/public_libraries.h b/libnativeloader/public_libraries.h
index 9bb3366..2de4172 100644
--- a/libnativeloader/public_libraries.h
+++ b/libnativeloader/public_libraries.h
@@ -15,13 +15,19 @@
*/
#pragma once
+#include <algorithm>
#include <string>
+#include <android-base/result.h>
+
namespace android::nativeloader {
+using android::base::Result;
+
// These provide the list of libraries that are available to the namespace for apps.
// Not all of the libraries are available to apps. Depending on the context,
// e.g., if it is a vendor app or not, different set of libraries are made available.
+const std::string& preloadable_public_libraries();
const std::string& default_public_libraries();
const std::string& runtime_public_libraries();
const std::string& vendor_public_libraries();
@@ -30,4 +36,21 @@
const std::string& llndk_libraries();
const std::string& vndksp_libraries();
-}; // namespace android::nativeloader
+// These are exported for testing
+namespace internal {
+
+enum Bitness { ALL = 0, ONLY_32, ONLY_64 };
+
+struct ConfigEntry {
+ std::string soname;
+ bool nopreload;
+ Bitness bitness;
+};
+
+Result<std::vector<std::string>> ParseConfig(
+ const std::string& file_content,
+ const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn);
+
+} // namespace internal
+
+} // namespace android::nativeloader
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index 5b8179f..c5c4960 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -17,6 +17,7 @@
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
+#include <algorithm>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
@@ -48,13 +49,6 @@
#define off64_t off_t
#endif
-#define min(a, b) \
- ({ \
- typeof(a) _a = (a); \
- typeof(b) _b = (b); \
- (_a < _b) ? _a : _b; \
- })
-
#define SPARSE_HEADER_MAJOR_VER 1
#define SPARSE_HEADER_MINOR_VER 0
#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
@@ -231,7 +225,7 @@
struct output_file_gz* outgz = to_output_file_gz(out);
while (len > 0) {
- ret = gzwrite(outgz->gz_fd, data, min(len, (unsigned int)INT_MAX));
+ ret = gzwrite(outgz->gz_fd, data, std::min<unsigned int>(len, (unsigned int)INT_MAX));
if (ret == 0) {
error("gzwrite %s", gzerror(outgz->gz_fd, nullptr));
return -1;
@@ -268,7 +262,7 @@
int ret;
while (off > 0) {
- to_write = min(off, (int64_t)INT_MAX);
+ to_write = std::min(off, (int64_t)INT_MAX);
ret = outc->write(outc->priv, nullptr, to_write);
if (ret < 0) {
return ret;
@@ -470,7 +464,7 @@
}
while (len) {
- write_len = min(len, out->block_size);
+ write_len = std::min(len, out->block_size);
ret = out->ops->write(out, out->fill_buf, write_len);
if (ret < 0) {
return ret;
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 46e9920..a639592 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -633,15 +633,15 @@
namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.links = default
-namespace.neuralnetworks.link.default.shared_libs = libc.so
-namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
-namespace.neuralnetworks.link.default.shared_libs += libdl.so
-namespace.neuralnetworks.link.default.shared_libs += liblog.so
-namespace.neuralnetworks.link.default.shared_libs += libm.so
-namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
-namespace.neuralnetworks.link.default.shared_libs += libsync.so
-namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
+namespace.neuralnetworks.links = system
+namespace.neuralnetworks.link.system.shared_libs = libc.so
+namespace.neuralnetworks.link.system.shared_libs += libcgrouprc.so
+namespace.neuralnetworks.link.system.shared_libs += libdl.so
+namespace.neuralnetworks.link.system.shared_libs += liblog.so
+namespace.neuralnetworks.link.system.shared_libs += libm.so
+namespace.neuralnetworks.link.system.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.system.shared_libs += libsync.so
+namespace.neuralnetworks.link.system.shared_libs += libvndksupport.so
###############################################################################
# Namespace config for native tests that need access to both system and vendor