Merge "trusty: Convert Trusty driver tests to python_test"
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index edcea44..a541f6e 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -391,6 +391,9 @@
apex_available: [
"com.android.runtime",
],
+
+ // Required for tests.
+ required: ["crash_dump.policy"],
}
cc_binary {
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 2cf5b18..85adbea 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -88,7 +88,7 @@
struct sigaction old_sigaction; \
struct sigaction new_sigaction = {}; \
new_sigaction.sa_handler = [](int) {}; \
- if (sigaction(SIGALRM, &new_sigaction, &new_sigaction) != 0) { \
+ if (sigaction(SIGALRM, &new_sigaction, &old_sigaction) != 0) { \
err(1, "sigaction failed"); \
} \
alarm(seconds); \
@@ -1409,6 +1409,16 @@
return true;
}
+extern "C" void foo() {
+ LOG(INFO) << "foo";
+ std::this_thread::sleep_for(1s);
+}
+
+extern "C" void bar() {
+ LOG(INFO) << "bar";
+ std::this_thread::sleep_for(1s);
+}
+
TEST_F(CrasherTest, seccomp_tombstone) {
int intercept_result;
unique_fd output_fd;
@@ -1416,6 +1426,11 @@
static const auto dump_type = kDebuggerdTombstone;
StartProcess(
[]() {
+ std::thread a(foo);
+ std::thread b(bar);
+
+ std::this_thread::sleep_for(100ms);
+
raise_debugger_signal(dump_type);
_exit(0);
},
@@ -1430,16 +1445,8 @@
std::string result;
ConsumeFd(std::move(output_fd), &result);
ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
-}
-
-extern "C" void foo() {
- LOG(INFO) << "foo";
- std::this_thread::sleep_for(1s);
-}
-
-extern "C" void bar() {
- LOG(INFO) << "bar";
- std::this_thread::sleep_for(1s);
+ ASSERT_BACKTRACE_FRAME(result, "foo");
+ ASSERT_BACKTRACE_FRAME(result, "bar");
}
TEST_F(CrasherTest, seccomp_backtrace) {
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index baf994f..4c1f9eb 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -98,32 +98,6 @@
__linker_disable_fallback_allocator();
}
-static void iterate_siblings(bool (*callback)(pid_t, int), int output_fd) {
- pid_t current_tid = gettid();
- char buf[BUFSIZ];
- snprintf(buf, sizeof(buf), "/proc/%d/task", current_tid);
- DIR* dir = opendir(buf);
-
- if (!dir) {
- async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno));
- return;
- }
-
- struct dirent* ent;
- while ((ent = readdir(dir))) {
- char* end;
- long tid = strtol(ent->d_name, &end, 10);
- if (end == ent->d_name || *end != '\0') {
- continue;
- }
-
- if (tid != current_tid) {
- callback(tid, output_fd);
- }
- }
- closedir(dir);
-}
-
static bool forward_output(int src_fd, int dst_fd, pid_t expected_tid) {
// Make sure the thread actually got the signal.
struct pollfd pfd = {
@@ -216,21 +190,21 @@
}
// Only allow one thread to perform a trace at a time.
- static pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER;
- int ret = pthread_mutex_trylock(&trace_mutex);
- if (ret != 0) {
- async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s",
- strerror(ret));
+ static std::mutex trace_mutex;
+ if (!trace_mutex.try_lock()) {
+ async_safe_format_log(ANDROID_LOG_INFO, "libc", "trace lock failed");
return;
}
+ std::lock_guard<std::mutex> scoped_lock(trace_mutex, std::adopt_lock);
+
// Fetch output fd from tombstoned.
unique_fd tombstone_socket, output_fd;
if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, nullptr,
kDebuggerdNativeBacktrace)) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"missing crash_dump_fallback() in selinux policy?");
- goto exit;
+ return;
}
dump_backtrace_header(output_fd.get());
@@ -239,15 +213,15 @@
debuggerd_fallback_trace(output_fd.get(), ucontext);
// Send a signal to all of our siblings, asking them to dump their stack.
- iterate_siblings(
- [](pid_t tid, int output_fd) {
+ pid_t current_tid = gettid();
+ if (!iterate_tids(current_tid, [&output_fd](pid_t tid) {
// Use a pipe, to be able to detect situations where the thread gracefully exits before
// receiving our signal.
unique_fd pipe_read, pipe_write;
if (!Pipe(&pipe_read, &pipe_write)) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
strerror(errno));
- return false;
+ return;
}
uint64_t expected = pack_thread_fd(-1, -1);
@@ -257,7 +231,7 @@
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"thread %d is already outputting to fd %d?", tid, fd);
close(sent_fd);
- return false;
+ return;
}
siginfo_t siginfo = {};
@@ -269,10 +243,10 @@
if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, BIONIC_SIGNAL_DEBUGGER, &siginfo) != 0) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
tid, strerror(errno));
- return false;
+ return;
}
- bool success = forward_output(pipe_read.get(), output_fd, tid);
+ bool success = forward_output(pipe_read.get(), output_fd.get(), tid);
if (!success) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"timeout expired while waiting for thread %d to dump", tid);
@@ -288,15 +262,14 @@
}
}
- return true;
- },
- output_fd.get());
+ return;
+ })) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open /proc/%d/task: %s",
+ current_tid, strerror(errno));
+ }
dump_backtrace_footer(output_fd.get());
tombstoned_notify_completion(tombstone_socket.get());
-
-exit:
- pthread_mutex_unlock(&trace_mutex);
}
static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) {
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index f21a203..14caaf6 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -23,6 +23,7 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/prctl.h>
#include <sys/types.h>
#include <unistd.h>
@@ -73,22 +74,40 @@
std::map<pid_t, ThreadInfo> threads;
threads[tid] = ThreadInfo{
- .registers = std::move(regs),
- .uid = uid,
- .tid = tid,
- .thread_name = std::move(thread_name),
- .pid = pid,
- .command_line = std::move(command_line),
- .selinux_label = std::move(selinux_label),
- .siginfo = siginfo,
+ .registers = std::move(regs), .uid = uid, .tid = tid, .thread_name = std::move(thread_name),
+ .pid = pid, .command_line = std::move(command_line), .selinux_label = std::move(selinux_label),
+ .siginfo = siginfo,
+#if defined(__aarch64__)
+ // Only supported on aarch64 for now.
+ .tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
+ .pac_enabled_keys = prctl(PR_PAC_GET_ENABLED_KEYS, 0, 0, 0, 0),
+#endif
};
+ if (pid == tid) {
+ const ThreadInfo& thread = threads[pid];
+ if (!iterate_tids(pid, [&threads, &thread](pid_t tid) {
+ threads[tid] = ThreadInfo{
+ .uid = thread.uid,
+ .tid = tid,
+ .pid = thread.pid,
+ .command_line = thread.command_line,
+ .thread_name = get_thread_name(tid),
+ .tagged_addr_ctrl = thread.tagged_addr_ctrl,
+ .pac_enabled_keys = thread.pac_enabled_keys,
+ };
+ })) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to open /proc/%d/task: %s", pid,
+ strerror(errno));
+ }
+ }
unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch());
auto process_memory =
unwindstack::Memory::CreateProcessMemoryCached(getpid());
unwinder.SetProcessMemory(process_memory);
if (!unwinder.Init()) {
- async_safe_fatal("failed to init unwinder object");
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to init unwinder object");
+ return;
}
ProcessInfo process_info;
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index b7d5bc4..3e31bb7 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -48,6 +48,7 @@
#include <android/log.h>
#include <bionic/macros.h>
+#include <bionic/reserved_signals.h>
#include <log/log.h>
#include <log/log_read.h>
#include <log/logprint.h>
@@ -346,6 +347,93 @@
f->set_build_id(frame.map_info->GetPrintableBuildID());
}
+static void dump_registers(unwindstack::Unwinder* unwinder,
+ const std::unique_ptr<unwindstack::Regs>& regs, Thread& thread,
+ bool memory_dump) {
+ if (regs == nullptr) {
+ return;
+ }
+
+ unwindstack::Maps* maps = unwinder->GetMaps();
+ unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
+
+ regs->IterateRegisters([&thread, memory_dump, maps, memory](const char* name, uint64_t value) {
+ Register r;
+ r.set_name(name);
+ r.set_u64(value);
+ *thread.add_registers() = r;
+
+ if (memory_dump) {
+ MemoryDump dump;
+
+ dump.set_register_name(name);
+ std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(untag_address(value));
+ if (map_info) {
+ dump.set_mapping_name(map_info->name());
+ }
+
+ constexpr size_t kNumBytesAroundRegister = 256;
+ constexpr size_t kNumTagsAroundRegister = kNumBytesAroundRegister / kTagGranuleSize;
+ char buf[kNumBytesAroundRegister];
+ uint8_t tags[kNumTagsAroundRegister];
+ ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory);
+ if (bytes == -1) {
+ return;
+ }
+ dump.set_begin_address(value);
+ dump.set_memory(buf, bytes);
+
+ bool has_tags = false;
+#if defined(__aarch64__)
+ for (size_t i = 0; i < kNumTagsAroundRegister; ++i) {
+ if (tags[i] != 0) {
+ has_tags = true;
+ }
+ }
+#endif // defined(__aarch64__)
+
+ if (has_tags) {
+ dump.mutable_arm_mte_metadata()->set_memory_tags(tags, kNumTagsAroundRegister);
+ }
+
+ *thread.add_memory_dump() = std::move(dump);
+ }
+ });
+}
+
+static void log_unwinder_error(unwindstack::Unwinder* unwinder) {
+ if (unwinder->LastErrorCode() == unwindstack::ERROR_NONE) {
+ return;
+ }
+
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error code: %s",
+ unwinder->LastErrorCodeString());
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error address: 0x%" PRIx64,
+ unwinder->LastErrorAddress());
+}
+
+static void dump_thread_backtrace(unwindstack::Unwinder* unwinder, Thread& thread) {
+ if (unwinder->NumFrames() == 0) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
+ log_unwinder_error(unwinder);
+ return;
+ }
+
+ if (unwinder->elf_from_memory_not_file()) {
+ auto backtrace_note = thread.mutable_backtrace_note();
+ *backtrace_note->Add() =
+ "Function names and BuildId information is missing for some frames due";
+ *backtrace_note->Add() = "to unreadable libraries. For unwinds of apps, only shared libraries";
+ *backtrace_note->Add() = "found under the lib/ directory are readable.";
+ *backtrace_note->Add() = "On this device, run setenforce 0 to make the libraries readable.";
+ }
+ unwinder->SetDisplayBuildID(true);
+ for (const auto& frame : unwinder->frames()) {
+ BacktraceFrame* f = thread.add_current_backtrace();
+ fill_in_backtrace_frame(f, frame);
+ }
+}
+
static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
const ThreadInfo& thread_info, bool memory_dump = false) {
Thread thread;
@@ -355,97 +443,32 @@
thread.set_tagged_addr_ctrl(thread_info.tagged_addr_ctrl);
thread.set_pac_enabled_keys(thread_info.pac_enabled_keys);
- unwindstack::Maps* maps = unwinder->GetMaps();
- unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
-
- thread_info.registers->IterateRegisters(
- [&thread, memory_dump, maps, memory](const char* name, uint64_t value) {
- Register r;
- r.set_name(name);
- r.set_u64(value);
- *thread.add_registers() = r;
-
- if (memory_dump) {
- MemoryDump dump;
-
- dump.set_register_name(name);
- std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(untag_address(value));
- if (map_info) {
- dump.set_mapping_name(map_info->name());
- }
-
- constexpr size_t kNumBytesAroundRegister = 256;
- constexpr size_t kNumTagsAroundRegister = kNumBytesAroundRegister / kTagGranuleSize;
- char buf[kNumBytesAroundRegister];
- uint8_t tags[kNumTagsAroundRegister];
- size_t start_offset = 0;
- ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory);
- if (bytes == -1) {
- return;
- }
- dump.set_begin_address(value);
-
- if (start_offset + bytes > sizeof(buf)) {
- async_safe_fatal("dump_memory overflowed? start offset = %zu, bytes read = %zd",
- start_offset, bytes);
- }
-
- dump.set_memory(buf, bytes);
-
- bool has_tags = false;
-#if defined(__aarch64__)
- for (size_t i = 0; i < kNumTagsAroundRegister; ++i) {
- if (tags[i] != 0) {
- has_tags = true;
- }
- }
-#endif // defined(__aarch64__)
-
- if (has_tags) {
- dump.mutable_arm_mte_metadata()->set_memory_tags(tags, kNumTagsAroundRegister);
- }
-
- *thread.add_memory_dump() = std::move(dump);
- }
- });
-
- std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
- unwinder->SetRegs(regs_copy.get());
- unwinder->Unwind();
- if (unwinder->NumFrames() == 0) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
- if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error code: %s",
- unwinder->LastErrorCodeString());
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error address: 0x%" PRIx64,
- unwinder->LastErrorAddress());
+ if (thread_info.pid == getpid() && thread_info.pid != thread_info.tid) {
+ // Fallback path for non-main thread, doing unwind from running process.
+ unwindstack::ThreadUnwinder thread_unwinder(kMaxFrames, unwinder->GetMaps());
+ if (!thread_unwinder.Init()) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Unable to initialize ThreadUnwinder object.");
+ log_unwinder_error(&thread_unwinder);
+ return;
}
+
+ std::unique_ptr<unwindstack::Regs> initial_regs;
+ thread_unwinder.UnwindWithSignal(BIONIC_SIGNAL_BACKTRACE, thread_info.tid, &initial_regs);
+ dump_registers(&thread_unwinder, initial_regs, thread, memory_dump);
+ dump_thread_backtrace(&thread_unwinder, thread);
} else {
- if (unwinder->elf_from_memory_not_file()) {
- auto backtrace_note = thread.mutable_backtrace_note();
- *backtrace_note->Add() =
- "Function names and BuildId information is missing for some frames due";
- *backtrace_note->Add() =
- "to unreadable libraries. For unwinds of apps, only shared libraries";
- *backtrace_note->Add() = "found under the lib/ directory are readable.";
- *backtrace_note->Add() = "On this device, run setenforce 0 to make the libraries readable.";
- }
- unwinder->SetDisplayBuildID(true);
- for (const auto& frame : unwinder->frames()) {
- BacktraceFrame* f = thread.add_current_backtrace();
- fill_in_backtrace_frame(f, frame);
- }
+ dump_registers(unwinder, thread_info.registers, thread, memory_dump);
+ std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
+ unwinder->SetRegs(regs_copy.get());
+ unwinder->Unwind();
+ dump_thread_backtrace(unwinder, thread);
}
auto& threads = *tombstone->mutable_threads();
threads[thread_info.tid] = thread;
}
-static void dump_main_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
- const ThreadInfo& thread_info) {
- dump_thread(tombstone, unwinder, thread_info, true);
-}
-
static void dump_mappings(Tombstone* tombstone, unwindstack::Unwinder* unwinder) {
unwindstack::Maps* maps = unwinder->GetMaps();
std::shared_ptr<unwindstack::Memory> process_memory = unwinder->GetProcessMemory();
@@ -663,7 +686,8 @@
dump_abort_message(&result, unwinder, process_info);
- dump_main_thread(&result, unwinder, main_thread);
+ // Dump the main thread, but save the memory around the registers.
+ dump_thread(&result, unwinder, main_thread, /* memory_dump */ true);
for (const auto& [tid, thread_info] : threads) {
if (tid != target_thread) {
diff --git a/debuggerd/seccomp_policy/crash_dump.arm64.policy b/debuggerd/seccomp_policy/crash_dump.arm64.policy
index 858a338..4e8fdf9 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy
@@ -25,7 +25,7 @@
rt_sigprocmask: 1
rt_sigaction: 1
rt_tgsigqueueinfo: 1
-prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS || arg0 == PR_GET_TAGGED_ADDR_CTRL || arg0 == PR_PAC_GET_ENABLED_KEYS
madvise: 1
mprotect: arg2 in 0x1|0x2
munmap: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index 152697c..4eb996e 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -37,7 +37,7 @@
#define PR_SET_VMA 0x53564d41
#if defined(__aarch64__)
// PR_PAC_RESET_KEYS happens on aarch64 in pthread_create path.
-prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA || arg0 == PR_PAC_RESET_KEYS
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA || arg0 == PR_PAC_RESET_KEYS || arg0 == PR_GET_TAGGED_ADDR_CTRL || arg0 == PR_PAC_GET_ENABLED_KEYS
#else
prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA
#endif
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index ce0fd30..5c6abc9 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -18,6 +18,7 @@
#include <time.h>
+#include <functional>
#include <string>
#include <utility>
@@ -74,3 +75,24 @@
n = strftime(s, sz, "%z", &tm), s += n, sz -= n;
return buf;
}
+
+bool iterate_tids(pid_t pid, std::function<void(pid_t)> callback) {
+ char buf[BUFSIZ];
+ snprintf(buf, sizeof(buf), "/proc/%d/task", pid);
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(buf), closedir);
+ if (dir == nullptr) {
+ return false;
+ }
+
+ struct dirent* entry;
+ while ((entry = readdir(dir.get())) != nullptr) {
+ pid_t tid = atoi(entry->d_name);
+ if (tid == 0) {
+ continue;
+ }
+ if (pid != tid) {
+ callback(tid);
+ }
+ }
+ return true;
+}
diff --git a/debuggerd/util.h b/debuggerd/util.h
index ec2862a..4375870 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -16,6 +16,7 @@
#pragma once
+#include <functional>
#include <string>
#include <vector>
@@ -27,3 +28,4 @@
std::string get_thread_name(pid_t tid);
std::string get_timestamp();
+bool iterate_tids(pid_t, std::function<void(pid_t)>);
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 44dc81f..22f8363 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -186,6 +186,11 @@
return result;
}
+static void RemoveScratchPartition() {
+ AutoMountMetadata mount_metadata;
+ android::fs_mgr::TeardownAllOverlayForMountPoint();
+}
+
bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe) {
std::vector<char> data = std::move(device->download_data());
if (data.empty()) {
@@ -218,7 +223,7 @@
if (!FlashPartitionTable(super_name, *new_metadata.get())) {
return device->WriteFail("Unable to flash new partition table");
}
- android::fs_mgr::TeardownAllOverlayForMountPoint();
+ RemoveScratchPartition();
sync();
return device->WriteOkay("Successfully flashed partition table");
}
@@ -262,7 +267,7 @@
if (!UpdateAllPartitionMetadata(device, super_name, *new_metadata.get())) {
return device->WriteFail("Unable to write new partition table");
}
- android::fs_mgr::TeardownAllOverlayForMountPoint();
+ RemoveScratchPartition();
sync();
return device->WriteOkay("Successfully updated partition table");
}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index fde1dab..39d86f9 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1084,7 +1084,11 @@
// Rewrite vbmeta if that's what we're flashing and modification has been requested.
if (g_disable_verity || g_disable_verification) {
- if (partition == "vbmeta" || partition == "vbmeta_a" || partition == "vbmeta_b") {
+ // The vbmeta partition might have additional prefix if running in virtual machine
+ // e.g., guest_vbmeta_a.
+ if (android::base::EndsWith(partition, "vbmeta") ||
+ android::base::EndsWith(partition, "vbmeta_a") ||
+ android::base::EndsWith(partition, "vbmeta_b")) {
rewrite_vbmeta_buffer(buf, false /* vbmeta_in_boot */);
} else if (!has_vbmeta_partition() &&
(partition == "boot" || partition == "boot_a" || partition == "boot_b")) {
diff --git a/fastboot/fuzzer/Android.bp b/fastboot/fuzzer/Android.bp
index fcd3bd6..1b59e4a 100644
--- a/fastboot/fuzzer/Android.bp
+++ b/fastboot/fuzzer/Android.bp
@@ -15,6 +15,11 @@
*
*/
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_fuzz {
name: "fastboot_fuzzer",
host_supported: true,
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 8ce961b..960173a 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -2256,7 +2256,8 @@
#if ALLOW_ADBD_DISABLE_VERITY == 0
// Allowlist the mount point if user build.
static const std::vector<const std::string> kAllowedPaths = {
- "/odm", "/odm_dlkm", "/oem", "/product", "/system_ext", "/vendor", "/vendor_dlkm",
+ "/odm", "/odm_dlkm", "/oem", "/product",
+ "/system_dlkm", "/system_ext", "/vendor", "/vendor_dlkm",
};
static const std::vector<const std::string> kAllowedPrefixes = {
"/mnt/product/",
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 0ca1946..143e980 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -657,7 +657,12 @@
if (partition_ext4 == fstab->end()) {
auto new_entry = *GetEntryForMountPoint(fstab, mount_point);
new_entry.fs_type = "ext4";
- fstab->emplace_back(new_entry);
+ auto it = std::find_if(fstab->rbegin(), fstab->rend(),
+ [&mount_point](const auto& entry) {
+ return entry.mount_point == mount_point;
+ });
+ auto end_of_mount_point_group = fstab->begin() + std::distance(it, fstab->rend());
+ fstab->insert(end_of_mount_point_group, new_entry);
}
}
}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 2da5b0f..996fa5e 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -33,6 +33,7 @@
#include <algorithm>
#include <memory>
+#include <optional>
#include <string>
#include <vector>
@@ -1121,6 +1122,23 @@
return true;
}
+static inline uint64_t GetIdealDataScratchSize() {
+ BlockDeviceInfo super_info;
+ PartitionOpener opener;
+ if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &super_info)) {
+ LERROR << "could not get block device info for super";
+ return 0;
+ }
+
+ struct statvfs s;
+ if (statvfs("/data", &s) < 0) {
+ PERROR << "could not statfs /data";
+ return 0;
+ }
+
+ return std::min(super_info.size, (uint64_t(s.f_frsize) * s.f_bfree) / 2);
+}
+
static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists, bool* change) {
*partition_exists = false;
if (change) *change = false;
@@ -1136,13 +1154,6 @@
return true;
}
- BlockDeviceInfo info;
- PartitionOpener opener;
- if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &info)) {
- LERROR << "could not get block device info for super";
- return false;
- }
-
if (change) *change = true;
// Note: calling RemoveDisabledImages here ensures that we do not race with
@@ -1152,10 +1163,11 @@
return false;
}
if (!images->BackingImageExists(partition_name)) {
- static constexpr uint64_t kMinimumSize = 64_MiB;
- static constexpr uint64_t kMaximumSize = 2_GiB;
+ uint64_t size = GetIdealDataScratchSize();
+ if (!size) {
+ size = 2_GiB;
+ }
- uint64_t size = std::clamp(info.size / 2, kMinimumSize, kMaximumSize);
auto flags = IImageManager::CREATE_IMAGE_DEFAULT;
if (!images->CreateBackingImage(partition_name, size, flags)) {
@@ -1396,18 +1408,35 @@
return ret;
}
+struct MapInfo {
+ // If set, partition is owned by ImageManager.
+ std::unique_ptr<IImageManager> images;
+ // If set, and images is null, this is a DAP partition.
+ std::string name;
+ // If set, and images and name are empty, this is a non-dynamic partition.
+ std::string device;
+
+ MapInfo() = default;
+ MapInfo(MapInfo&&) = default;
+ ~MapInfo() {
+ if (images) {
+ images->UnmapImageDevice(name);
+ } else if (!name.empty()) {
+ DestroyLogicalPartition(name);
+ }
+ }
+};
+
// Note: This function never returns the DSU scratch device in recovery or fastbootd,
// because the DSU scratch is created in the first-stage-mount, which is not run in recovery.
-static bool EnsureScratchMapped(std::string* device, bool* mapped) {
- *mapped = false;
- *device = GetBootScratchDevice();
- if (!device->empty()) {
- return true;
+static std::optional<MapInfo> EnsureScratchMapped() {
+ MapInfo info;
+ info.device = GetBootScratchDevice();
+ if (!info.device.empty()) {
+ return {std::move(info)};
}
-
if (!fs_mgr_in_recovery()) {
- errno = EINVAL;
- return false;
+ return {};
}
auto partition_name = android::base::Basename(kScratchMountPoint);
@@ -1417,11 +1446,15 @@
// would otherwise always be mapped.
auto images = IImageManager::Open("remount", 10s);
if (images && images->BackingImageExists(partition_name)) {
- if (!images->MapImageDevice(partition_name, 10s, device)) {
- return false;
+ if (images->IsImageDisabled(partition_name)) {
+ return {};
}
- *mapped = true;
- return true;
+ if (!images->MapImageDevice(partition_name, 10s, &info.device)) {
+ return {};
+ }
+ info.name = partition_name;
+ info.images = std::move(images);
+ return {std::move(info)};
}
// Avoid uart spam by first checking for a scratch partition.
@@ -1429,12 +1462,12 @@
auto super_device = fs_mgr_overlayfs_super_device(metadata_slot);
auto metadata = ReadCurrentMetadata(super_device);
if (!metadata) {
- return false;
+ return {};
}
auto partition = FindPartition(*metadata.get(), partition_name);
if (!partition) {
- return false;
+ return {};
}
CreateLogicalPartitionParams params = {
@@ -1444,11 +1477,11 @@
.force_writable = true,
.timeout_ms = 10s,
};
- if (!CreateLogicalPartition(params, device)) {
- return false;
+ if (!CreateLogicalPartition(params, &info.device)) {
+ return {};
}
- *mapped = true;
- return true;
+ info.name = partition_name;
+ return {std::move(info)};
}
// This should only be reachable in recovery, where DSU scratch is not
@@ -1602,26 +1635,35 @@
fs_mgr_overlayfs_teardown_one(overlay_mount_point, teardown_dir, ignore_change);
}
- // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
- bool mapped = false;
- std::string scratch_device;
- if (EnsureScratchMapped(&scratch_device, &mapped)) {
+ if (mount_point.empty()) {
+ // Throw away the entire partition.
+ auto partition_name = android::base::Basename(kScratchMountPoint);
+ auto images = IImageManager::Open("remount", 10s);
+ if (images && images->BackingImageExists(partition_name)) {
+ if (images->DisableImage(partition_name)) {
+ LOG(INFO) << "Disabled scratch partition for: " << kScratchMountPoint;
+ } else {
+ LOG(ERROR) << "Unable to disable scratch partition for " << kScratchMountPoint;
+ }
+ }
+ }
+
+ if (auto info = EnsureScratchMapped(); info.has_value()) {
+ // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
fs_mgr_overlayfs_umount_scratch();
- if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
+ if (fs_mgr_overlayfs_mount_scratch(info->device, fs_mgr_overlayfs_scratch_mount_type())) {
bool should_destroy_scratch = false;
fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change,
&should_destroy_scratch);
+ fs_mgr_overlayfs_umount_scratch();
if (should_destroy_scratch) {
fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, nullptr);
}
- fs_mgr_overlayfs_umount_scratch();
- }
- if (mapped) {
- DestroyLogicalPartition(android::base::Basename(kScratchMountPoint));
}
}
// Teardown DSU overlay if present.
+ std::string scratch_device;
if (MapDsuScratchDevice(&scratch_device)) {
fs_mgr_overlayfs_umount_scratch();
if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
diff --git a/fs_mgr/fuzz/Android.bp b/fs_mgr/fuzz/Android.bp
index f0afd28..b2b9be8 100644
--- a/fs_mgr/fuzz/Android.bp
+++ b/fs_mgr/fuzz/Android.bp
@@ -14,6 +14,11 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_fuzz {
name: "libfstab_fuzzer",
srcs: [
diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp
index 31a57a8..003e6ed 100644
--- a/fs_mgr/libfiemap/binder.cpp
+++ b/fs_mgr/libfiemap/binder.cpp
@@ -66,6 +66,7 @@
bool RemoveDisabledImages() override;
bool GetMappedImageDevice(const std::string& name, std::string* device) override;
bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;
+ bool IsImageDisabled(const std::string& name) override;
std::vector<std::string> GetAllBackingImages() override;
@@ -219,6 +220,17 @@
return !device->empty();
}
+bool ImageManagerBinder::IsImageDisabled(const std::string& name) {
+ bool retval;
+ auto status = manager_->isImageDisabled(name, &retval);
+ if (!status.isOk()) {
+ LOG(ERROR) << __PRETTY_FUNCTION__
+ << " binder returned: " << status.exceptionMessage().string();
+ return false;
+ }
+ return retval;
+}
+
bool ImageManagerBinder::MapAllImages(const std::function<bool(std::set<std::string>)>&) {
LOG(ERROR) << __PRETTY_FUNCTION__ << " not available over binder";
return false;
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index 2c14c8a..c416f4d 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -79,7 +79,7 @@
partition_opener_ = std::make_unique<android::fs_mgr::PartitionOpener>();
// Allow overriding whether ImageManager thinks it's in recovery, for testing.
-#ifdef __ANDROID_RECOVERY__
+#ifdef __ANDROID_RAMDISK__
device_info_.is_recovery = {true};
#else
if (!device_info_.is_recovery.has_value()) {
@@ -523,7 +523,7 @@
auto image_header = GetImageHeaderPath(name);
-#if !defined __ANDROID_RECOVERY__
+#ifndef __ANDROID_RAMDISK__
// If there is a device-mapper node wrapping the block device, then we're
// able to create another node around it; the dm layer does not carry the
// exclusion lock down the stack when a mount occurs.
@@ -744,6 +744,134 @@
return CreateLogicalPartitions(*metadata.get(), data_partition_name);
}
+std::ostream& operator<<(std::ostream& os, android::fs_mgr::Extent* extent) {
+ if (auto e = extent->AsLinearExtent()) {
+ return os << "<begin:" << e->physical_sector() << ", end:" << e->end_sector()
+ << ", device:" << e->device_index() << ">";
+ }
+ return os << "<unknown>";
+}
+
+static bool CompareExtent(android::fs_mgr::Extent* a, android::fs_mgr::Extent* b) {
+ if (auto linear_a = a->AsLinearExtent()) {
+ auto linear_b = b->AsLinearExtent();
+ if (!linear_b) {
+ return false;
+ }
+ return linear_a->physical_sector() == linear_b->physical_sector() &&
+ linear_a->num_sectors() == linear_b->num_sectors() &&
+ linear_a->device_index() == linear_b->device_index();
+ }
+ return false;
+}
+
+static bool CompareExtents(android::fs_mgr::Partition* oldp, android::fs_mgr::Partition* newp) {
+ const auto& old_extents = oldp->extents();
+ const auto& new_extents = newp->extents();
+
+ auto old_iter = old_extents.begin();
+ auto new_iter = new_extents.begin();
+ while (true) {
+ if (old_iter == old_extents.end()) {
+ if (new_iter == new_extents.end()) {
+ break;
+ }
+ LOG(ERROR) << "Unexpected extent added: " << (*new_iter);
+ return false;
+ }
+ if (new_iter == new_extents.end()) {
+ LOG(ERROR) << "Unexpected extent removed: " << (*old_iter);
+ return false;
+ }
+
+ if (!CompareExtent(old_iter->get(), new_iter->get())) {
+ LOG(ERROR) << "Extents do not match: " << old_iter->get() << ", " << new_iter->get();
+ return false;
+ }
+
+ old_iter++;
+ new_iter++;
+ }
+ return true;
+}
+
+bool ImageManager::ValidateImageMaps() {
+ if (!MetadataExists(metadata_dir_)) {
+ LOG(INFO) << "ImageManager skipping verification; no images for " << metadata_dir_;
+ return true;
+ }
+
+ auto metadata = OpenMetadata(metadata_dir_);
+ if (!metadata) {
+ LOG(ERROR) << "ImageManager skipping verification; failed to open " << metadata_dir_;
+ return true;
+ }
+
+ for (const auto& partition : metadata->partitions) {
+ auto name = GetPartitionName(partition);
+ auto image_path = GetImageHeaderPath(name);
+ auto fiemap = SplitFiemap::Open(image_path);
+ if (fiemap == nullptr) {
+ LOG(ERROR) << "SplitFiemap::Open(\"" << image_path << "\") failed";
+ return false;
+ }
+ if (!fiemap->HasPinnedExtents()) {
+ LOG(ERROR) << "Image doesn't have pinned extents: " << image_path;
+ return false;
+ }
+
+ android::fs_mgr::PartitionOpener opener;
+ auto builder = android::fs_mgr::MetadataBuilder::New(*metadata.get(), &opener);
+ if (!builder) {
+ LOG(ERROR) << "Could not create metadata builder: " << image_path;
+ return false;
+ }
+
+ auto new_p = builder->AddPartition("_temp_for_verify", 0);
+ if (!new_p) {
+ LOG(ERROR) << "Could not add temporary partition: " << image_path;
+ return false;
+ }
+
+ auto partition_size = android::fs_mgr::GetPartitionSize(*metadata.get(), partition);
+ if (!FillPartitionExtents(builder.get(), new_p, fiemap.get(), partition_size)) {
+ LOG(ERROR) << "Could not fill partition extents: " << image_path;
+ return false;
+ }
+
+ auto old_p = builder->FindPartition(name);
+ if (!old_p) {
+ LOG(ERROR) << "Could not find metadata for " << image_path;
+ return false;
+ }
+
+ if (!CompareExtents(old_p, new_p)) {
+ LOG(ERROR) << "Metadata for " << image_path << " does not match fiemap";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ImageManager::IsImageDisabled(const std::string& name) {
+ if (!MetadataExists(metadata_dir_)) {
+ return true;
+ }
+
+ auto metadata = OpenMetadata(metadata_dir_);
+ if (!metadata) {
+ return false;
+ }
+
+ auto partition = FindPartition(*metadata.get(), name);
+ if (!partition) {
+ return false;
+ }
+
+ return !!(partition->attributes & LP_PARTITION_ATTR_DISABLED);
+}
+
std::unique_ptr<MappedDevice> MappedDevice::Open(IImageManager* manager,
const std::chrono::milliseconds& timeout_ms,
const std::string& name) {
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 6d09751..7472949 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -119,6 +119,7 @@
ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));
ASSERT_TRUE(manager_->BackingImageExists(base_name_));
ASSERT_TRUE(manager_->DisableImage(base_name_));
+ ASSERT_TRUE(manager_->IsImageDisabled(base_name_));
ASSERT_TRUE(manager_->RemoveDisabledImages());
ASSERT_TRUE(!manager_->BackingImageExists(base_name_));
}
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 3c87000..00dd661 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -131,6 +131,9 @@
virtual bool RemoveAllImages() = 0;
virtual bool UnmapImageIfExists(const std::string& name);
+
+ // Returns whether DisableImage() was called.
+ virtual bool IsImageDisabled(const std::string& name) = 0;
};
class ImageManager final : public IImageManager {
@@ -162,6 +165,7 @@
bool RemoveDisabledImages() override;
bool GetMappedImageDevice(const std::string& name, std::string* device) override;
bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;
+ bool IsImageDisabled(const std::string& name) override;
std::vector<std::string> GetAllBackingImages();
@@ -174,6 +178,9 @@
// Writes |bytes| zeros at the beginning of the passed image
FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes);
+ // Validate that all images still have the same block map.
+ bool ValidateImageMaps();
+
private:
ImageManager(const std::string& metadata_dir, const std::string& data_dir,
const DeviceInfo& device_info);
diff --git a/fs_mgr/libfiemap/metadata.h b/fs_mgr/libfiemap/metadata.h
index 4eb3ad5..30b2c61 100644
--- a/fs_mgr/libfiemap/metadata.h
+++ b/fs_mgr/libfiemap/metadata.h
@@ -20,6 +20,7 @@
#include <string>
#include <libfiemap/split_fiemap_writer.h>
+#include <liblp/builder.h>
#include <liblp/liblp.h>
namespace android {
@@ -34,5 +35,9 @@
bool RemoveImageMetadata(const std::string& metadata_dir, const std::string& partition_name);
bool RemoveAllMetadata(const std::string& dir);
+bool FillPartitionExtents(android::fs_mgr::MetadataBuilder* builder,
+ android::fs_mgr::Partition* partition, android::fiemap::SplitFiemap* file,
+ uint64_t partition_size);
+
} // namespace fiemap
} // namespace android
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 8b269cd..6db8f13 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -275,6 +275,24 @@
],
}
+cc_test {
+ name: "vts_ota_config_test",
+ srcs: [
+ "vts_ota_config_test.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+ test_suites: [
+ "vts",
+ ],
+ test_options: {
+ min_shipping_api_level: 33,
+ },
+ auto_gen_config: true,
+ require_root: true,
+}
+
cc_binary {
name: "snapshotctl",
srcs: [
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 532f66d..5daa84d 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -197,6 +197,9 @@
// user-space snapshots
bool userspace_snapshots = 9;
+
+ // io_uring support
+ bool io_uring_enabled = 10;
}
// Next: 10
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 120f95b..38b47d5 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -809,6 +809,9 @@
// userspace snapshots.
bool UpdateUsesUserSnapshots(LockedFile* lock);
+ // Check if io_uring API's need to be used
+ bool UpdateUsesIouring(LockedFile* lock);
+
// Wrapper around libdm, with diagnostics.
bool DeleteDeviceIfExists(const std::string& name,
const std::chrono::milliseconds& timeout_ms = {});
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index f3de2b4..797d627 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -1685,6 +1685,9 @@
if (UpdateUsesUserSnapshots(lock.get()) && transition == InitTransition::SELINUX_DETACH) {
snapuserd_argv->emplace_back("-user_snapshot");
+ if (UpdateUsesIouring(lock.get())) {
+ snapuserd_argv->emplace_back("-io_uring");
+ }
}
size_t num_cows = 0;
@@ -2062,6 +2065,11 @@
return update_status.compression_enabled();
}
+bool SnapshotManager::UpdateUsesIouring(LockedFile* lock) {
+ SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+ return update_status.io_uring_enabled();
+}
+
bool SnapshotManager::UpdateUsesUserSnapshots() {
// This and the following function is constantly
// invoked during snapshot merge. We want to avoid
@@ -2877,6 +2885,7 @@
status.set_source_build_fingerprint(old_status.source_build_fingerprint());
status.set_merge_phase(old_status.merge_phase());
status.set_userspace_snapshots(old_status.userspace_snapshots());
+ status.set_io_uring_enabled(old_status.io_uring_enabled());
}
return WriteSnapshotUpdateStatus(lock, status);
}
@@ -3200,6 +3209,7 @@
status.set_userspace_snapshots(IsUserspaceSnapshotsEnabled());
if (IsUserspaceSnapshotsEnabled()) {
is_snapshot_userspace_ = true;
+ status.set_io_uring_enabled(IsIouringEnabled());
LOG(INFO) << "User-space snapshots enabled";
} else {
is_snapshot_userspace_ = false;
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index c1a5af7..a648384 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -199,6 +199,7 @@
bool UnmapImageIfExists(const std::string& name) override {
return impl_->UnmapImageIfExists(name);
}
+ bool IsImageDisabled(const std::string& name) override { return impl_->IsImageDisabled(name); }
private:
std::unique_ptr<android::fiemap::IImageManager> impl_;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index d76558b..04d228d 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -46,8 +46,6 @@
#include "partition_cow_creator.h"
#include "utility.h"
-#include <android-base/properties.h>
-
// Mock classes are not used. Header included to ensure mocked class definition aligns with the
// class itself.
#include <libsnapshot/mock_device_info.h>
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
index a082742..0b88567 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -28,6 +28,7 @@
DEFINE_bool(socket_handoff, false,
"If true, perform a socket hand-off with an existing snapuserd instance, then exit.");
DEFINE_bool(user_snapshot, false, "If true, user-space snapshots are used");
+DEFINE_bool(io_uring, false, "If true, io_uring feature is enabled");
namespace android {
namespace snapshot {
@@ -51,7 +52,12 @@
// is applied will check for the property. This is ok as the system
// properties are valid at this point. We can't do this during first
// stage init and hence use the command line flags to get the information.
- if (!IsDmSnapshotTestingEnabled() && (FLAGS_user_snapshot || IsUserspaceSnapshotsEnabled())) {
+ bool user_snapshots = FLAGS_user_snapshot;
+ if (!user_snapshots) {
+ user_snapshots = (!IsDmSnapshotTestingEnabled() && IsUserspaceSnapshotsEnabled());
+ }
+
+ if (user_snapshots) {
LOG(INFO) << "Starting daemon for user-space snapshots.....";
return StartServerForUserspaceSnapshots(arg_start, argc, argv);
} else {
@@ -75,6 +81,11 @@
MaskAllSignalsExceptIntAndTerm();
+ user_server_.SetServerRunning();
+ if (FLAGS_io_uring) {
+ user_server_.SetIouringEnabled();
+ }
+
if (FLAGS_socket_handoff) {
return user_server_.RunForSocketHandoff();
}
@@ -165,7 +176,10 @@
}
void Daemon::Interrupt() {
- if (IsUserspaceSnapshotsEnabled()) {
+ // TODO: We cannot access system property during first stage init.
+ // Until we remove the dm-snapshot code, we will have this check
+ // and verify it through a temp variable.
+ if (user_server_.IsServerRunning()) {
user_server_.Interrupt();
} else {
server_.Interrupt();
@@ -173,7 +187,7 @@
}
void Daemon::ReceivedSocketSignal() {
- if (IsUserspaceSnapshotsEnabled()) {
+ if (user_server_.IsServerRunning()) {
user_server_.ReceivedSocketSignal();
} else {
server_.ReceivedSocketSignal();
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
index 5109d82..b988c7b 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -39,7 +39,21 @@
}
bool SnapshotHandler::InitializeWorkers() {
- for (int i = 0; i < kNumWorkerThreads; i++) {
+ int num_worker_threads = kNumWorkerThreads;
+
+ // We will need multiple worker threads only during
+ // device boot after OTA. For all other purposes,
+ // one thread is sufficient. We don't want to consume
+ // unnecessary memory especially during OTA install phase
+ // when daemon will be up during entire post install phase.
+ //
+ // During boot up, we need multiple threads primarily for
+ // update-verification.
+ if (is_socket_present_) {
+ num_worker_threads = 1;
+ }
+
+ for (int i = 0; i < num_worker_threads; i++) {
std::unique_ptr<Worker> wt =
std::make_unique<Worker>(cow_device_, backing_store_device_, control_device_,
misc_name_, base_path_merge_, GetSharedPtr());
@@ -677,6 +691,16 @@
return false;
}
+ // During selinux init transition, libsnapshot will propagate the
+ // status of io_uring enablement. As properties are not initialized,
+ // we cannot query system property.
+ //
+ // TODO: b/219642530: Intermittent I/O failures observed
+ if (is_io_uring_enabled_) {
+ return false;
+ }
+
+ // Finally check the system property
return android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
index b0f2d65..cc82985 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -319,6 +319,7 @@
void SetMergedBlockCountForNextCommit(int x) { total_ra_blocks_merged_ = x; }
int GetTotalBlocksToMerge() { return total_ra_blocks_merged_; }
void SetSocketPresent(bool socket) { is_socket_present_ = socket; }
+ void SetIouringEnabled(bool io_uring_enabled) { is_io_uring_enabled_ = io_uring_enabled; }
bool MergeInitiated() { return merge_initiated_; }
double GetMergePercentage() { return merge_completion_percentage_; }
@@ -396,6 +397,7 @@
bool merge_initiated_ = false;
bool attached_ = false;
bool is_socket_present_;
+ bool is_io_uring_enabled_ = false;
bool scratch_space_ = false;
std::unique_ptr<struct io_uring> ring_;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
index d4d4efe..ffb982a 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
@@ -461,6 +461,14 @@
return false;
}
+ {
+ // TODO: b/219642530 - Disable io_uring for merge
+ // until we figure out the cause of intermittent
+ // IO failures.
+ merge_async_ = false;
+ return true;
+ }
+
ring_ = std::make_unique<struct io_uring>();
int ret = io_uring_queue_init(queue_depth_, ring_.get(), 0);
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
index a79e3e1..82b2b25 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -293,7 +293,6 @@
void UserSnapshotServer::RunThread(std::shared_ptr<UserSnapshotDmUserHandler> handler) {
LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
- handler->snapuserd()->SetSocketPresent(is_socket_present_);
if (!handler->snapuserd()->Start()) {
LOG(ERROR) << " Failed to launch all worker threads";
}
@@ -471,6 +470,9 @@
return nullptr;
}
+ snapuserd->SetSocketPresent(is_socket_present_);
+ snapuserd->SetIouringEnabled(io_uring_enabled_);
+
if (!snapuserd->InitializeWorkers()) {
LOG(ERROR) << "Failed to initialize workers";
return nullptr;
@@ -599,8 +601,13 @@
return false;
}
- // We must re-initialize property service access, since we launched before
- // second-stage init.
+ // This initialization of system property is important. When daemon is
+ // launched post selinux transition (before init second stage),
+ // bionic libc initializes system property as part of __libc_init_common();
+ // however that initialization fails silently given that fact that we don't
+ // have /dev/__properties__ setup which is created at init second stage.
+ //
+ // At this point, we have the handlers setup and is safe to setup property.
__system_properties_init();
if (!android::base::WaitForProperty("snapuserd.proxy_ready", "true")) {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
index c645456..34e7941 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -84,6 +84,8 @@
std::vector<struct pollfd> watched_fds_;
bool is_socket_present_ = false;
int num_partitions_merge_complete_ = 0;
+ bool is_server_running_ = false;
+ bool io_uring_enabled_ = false;
std::mutex lock_;
@@ -136,6 +138,10 @@
void SetTerminating() { terminating_ = true; }
void ReceivedSocketSignal() { received_socket_signal_ = true; }
+ void SetServerRunning() { is_server_running_ = true; }
+ bool IsServerRunning() { return is_server_running_; }
+ void SetIouringEnabled() { io_uring_enabled_ = true; }
+ bool IsIouringEnabled() { return io_uring_enabled_; }
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 89d6145..f01bec9 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -192,6 +192,10 @@
return android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
}
+bool IsIouringEnabled() {
+ return android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
+}
+
std::string GetOtherPartitionName(const std::string& name) {
auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);
CHECK(suffix == "_a" || suffix == "_b");
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index a032b68..0ef3234 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -135,6 +135,8 @@
bool IsDmSnapshotTestingEnabled();
+bool IsIouringEnabled();
+
// Swap the suffix of a partition name.
std::string GetOtherPartitionName(const std::string& name);
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/vts_ota_config_test.cpp b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
new file mode 100644
index 0000000..afc2d81
--- /dev/null
+++ b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
@@ -0,0 +1,23 @@
+//
+// Copyright (C) 2022 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 <android-base/properties.h>
+#include <gtest/gtest.h>
+
+TEST(VAB, Enabled) {
+ ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.enabled", false));
+ ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false));
+}
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 335da9e..7e3924a 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -88,3 +88,32 @@
test_suites: ["general-tests"],
}
+
+cc_test {
+ name: "vts_fs_test",
+ test_suites: [
+ "vts",
+ "device-tests",
+ ],
+ test_options: {
+ min_shipping_api_level: 31,
+ },
+ require_root: true,
+ auto_gen_config: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ srcs: [
+ "vts_fs_test.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libfs_mgr",
+ ],
+ static_libs: [
+ "libfstab",
+ "libgmock",
+ "libgtest",
+ ],
+}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index eccb902..1dbee75 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -1104,3 +1104,76 @@
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
EXPECT_EQ(0, entry->readahead_size_kb);
}
+
+TEST(fs_mgr, TransformFstabForDsu) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+system /system erofs ro wait,logical,first_stage_mount
+system /system ext4 ro wait,logical,first_stage_mount
+vendor /vendor ext4 ro wait,logical,first_stage_mount
+data /data f2fs noatime wait
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
+ ASSERT_EQ(4U, fstab.size());
+
+ auto entry = fstab.begin();
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/vendor", entry->mount_point);
+ EXPECT_EQ("vendor", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/data", entry->mount_point);
+ EXPECT_EQ("userdata_gsi", entry->blk_device);
+ entry++;
+}
+
+TEST(fs_mgr, TransformFstabForDsu_synthesisExt4Entry) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+system /system erofs ro wait,logical,first_stage_mount
+vendor /vendor ext4 ro wait,logical,first_stage_mount
+data /data f2fs noatime wait
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
+ ASSERT_EQ(4U, fstab.size());
+
+ auto entry = fstab.begin();
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("erofs", entry->fs_type);
+ entry++;
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("ext4", entry->fs_type);
+ entry++;
+
+ EXPECT_EQ("/vendor", entry->mount_point);
+ EXPECT_EQ("vendor", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/data", entry->mount_point);
+ EXPECT_EQ("userdata_gsi", entry->blk_device);
+ entry++;
+}
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
new file mode 100644
index 0000000..415e67e
--- /dev/null
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -0,0 +1,111 @@
+// 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 <sys/mount.h>
+#include <sys/utsname.h>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <fstab/fstab.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <libdm/dm.h>
+
+static int GetVsrLevel() {
+ return android::base::GetIntProperty("ro.vendor.api_level", -1);
+}
+
+TEST(fs, ErofsSupported) {
+ // S and higher for this test.
+ if (GetVsrLevel() < __ANDROID_API_S__) {
+ GTEST_SKIP();
+ }
+
+ struct utsname uts;
+ ASSERT_EQ(uname(&uts), 0);
+
+ unsigned int major, minor;
+ ASSERT_EQ(sscanf(uts.release, "%u.%u", &major, &minor), 2);
+
+ // EROFS support only required in 5.10+
+ if (major < 5 || (major == 5 && minor < 10)) {
+ GTEST_SKIP();
+ }
+
+ std::string fs;
+ ASSERT_TRUE(android::base::ReadFileToString("/proc/filesystems", &fs));
+ EXPECT_THAT(fs, ::testing::HasSubstr("\terofs\n"));
+}
+
+TEST(fs, PartitionTypes) {
+ android::fs_mgr::Fstab fstab;
+ ASSERT_TRUE(android::fs_mgr::ReadFstabFromFile("/proc/mounts", &fstab));
+
+ auto& dm = android::dm::DeviceMapper::Instance();
+
+ std::string super_bdev, userdata_bdev;
+ ASSERT_TRUE(android::base::Readlink("/dev/block/by-name/super", &super_bdev));
+ ASSERT_TRUE(android::base::Readlink("/dev/block/by-name/userdata", &userdata_bdev));
+
+ int vsr_level = GetVsrLevel();
+
+ for (const auto& entry : fstab) {
+ std::string parent_bdev = entry.blk_device;
+ while (true) {
+ auto basename = android::base::Basename(parent_bdev);
+ if (!android::base::StartsWith(basename, "dm-")) {
+ break;
+ }
+
+ auto parent = dm.GetParentBlockDeviceByPath(parent_bdev);
+ if (!parent || *parent == parent_bdev) {
+ break;
+ }
+ parent_bdev = *parent;
+ }
+
+ if (parent_bdev == userdata_bdev ||
+ android::base::StartsWith(parent_bdev, "/dev/block/loop")) {
+ if (entry.flags & MS_RDONLY) {
+ // APEXes should not be F2FS.
+ EXPECT_NE(entry.fs_type, "f2fs");
+ }
+ continue;
+ }
+
+ if (vsr_level < __ANDROID_API_T__) {
+ continue;
+ }
+ if (vsr_level == __ANDROID_API_T__ && parent_bdev != super_bdev) {
+ // Only check for dynamic partitions at this VSR level.
+ continue;
+ }
+
+ if (entry.flags & MS_RDONLY) {
+ EXPECT_EQ(entry.fs_type, "erofs") << entry.mount_point;
+ } else {
+ EXPECT_NE(entry.fs_type, "ext4") << entry.mount_point;
+ }
+ }
+}
+
+TEST(fs, NoDtFstab) {
+ if (GetVsrLevel() <= __ANDROID_API_S__) {
+ GTEST_SKIP();
+ }
+
+ android::fs_mgr::Fstab fstab;
+ EXPECT_FALSE(android::fs_mgr::ReadFstabFromDt(&fstab, false));
+}
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 377acb7..5890f9a 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -92,6 +92,7 @@
mBatteryDevicePresent(false),
mBatteryFixedCapacity(0),
mBatteryFixedTemperature(0),
+ mChargerDockOnline(false),
mHealthInfo(std::make_unique<HealthInfo_2_1>()) {
initHealthInfo(mHealthInfo.get());
}
@@ -196,6 +197,7 @@
{"USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC},
{"USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB},
{"Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS},
+ {"Dock", ANDROID_POWER_SUPPLY_TYPE_DOCK},
{NULL, 0},
};
std::string buf;
@@ -319,9 +321,21 @@
case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
props.chargerWirelessOnline = true;
break;
+ case ANDROID_POWER_SUPPLY_TYPE_DOCK:
+ mChargerDockOnline = true;
+ break;
default:
- KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
- mChargerNames[i].string());
+ path.clear();
+ path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH,
+ mChargerNames[i].string());
+ if (access(path.string(), R_OK) == 0) {
+ mChargerDockOnline = true;
+ KLOG_INFO(LOG_TAG, "%s: online\n",
+ mChargerNames[i].string());
+ } else {
+ KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
+ mChargerNames[i].string());
+ }
}
path.clear();
path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
@@ -391,8 +405,8 @@
bool BatteryMonitor::isChargerOnline() {
const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
- return props.chargerAcOnline | props.chargerUsbOnline |
- props.chargerWirelessOnline;
+ return props.chargerAcOnline | props.chargerUsbOnline | props.chargerWirelessOnline |
+ mChargerDockOnline;
}
int BatteryMonitor::getChargeStatus() {
@@ -477,10 +491,10 @@
char vs[128];
const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
- snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d voltage_max: %d\n",
- props.chargerAcOnline, props.chargerUsbOnline,
- props.chargerWirelessOnline, props.maxChargingCurrent,
- props.maxChargingVoltage);
+ snprintf(vs, sizeof(vs),
+ "ac: %d usb: %d wireless: %d dock: %d current_max: %d voltage_max: %d\n",
+ props.chargerAcOnline, props.chargerUsbOnline, props.chargerWirelessOnline,
+ mChargerDockOnline, props.maxChargingCurrent, props.maxChargingVoltage);
write(fd, vs, strlen(vs));
snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
props.batteryStatus, props.batteryHealth, props.batteryPresent);
@@ -554,6 +568,7 @@
case ANDROID_POWER_SUPPLY_TYPE_AC:
case ANDROID_POWER_SUPPLY_TYPE_USB:
case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
+ case ANDROID_POWER_SUPPLY_TYPE_DOCK:
path.clear();
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path.string(), R_OK) == 0)
@@ -691,6 +706,17 @@
case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
break;
}
+
+ // Look for "is_dock" file
+ path.clear();
+ path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path.string(), R_OK) == 0) {
+ path.clear();
+ path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path.string(), R_OK) == 0)
+ mChargerNames.add(String8(name));
+
+ }
}
}
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index 4484fa6..3e73fcd 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -94,9 +94,18 @@
gr_flip();
}
-void HealthdDraw::blank_screen(bool blank) {
+void HealthdDraw::blank_screen(bool blank, int drm) {
if (!graphics_available) return;
- gr_fb_blank(blank);
+ gr_fb_blank(blank, drm);
+}
+
+/* support screen rotation for foldable phone */
+void HealthdDraw::rotate_screen(int drm) {
+ if (!graphics_available) return;
+ if (drm == 0)
+ gr_rotate(GRRotation::RIGHT /* landscape mode */);
+ else
+ gr_rotate(GRRotation::NONE /* Portrait mode */);
}
void HealthdDraw::clear_screen(void) {
@@ -139,6 +148,8 @@
void HealthdDraw::determine_xy(const animation::text_field& field,
const int length, int* x, int* y) {
*x = field.pos_x;
+ screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);
+ screen_height_ = gr_fb_height();
int str_len_px = length * field.font->char_width;
if (field.pos_x == CENTER_VAL) {
diff --git a/healthd/healthd_draw.h b/healthd/healthd_draw.h
index 0b48ce8..3d4abbd 100644
--- a/healthd/healthd_draw.h
+++ b/healthd/healthd_draw.h
@@ -31,8 +31,12 @@
// Redraws screen.
void redraw_screen(const animation* batt_anim, GRSurface* surf_unknown);
+ // According to the index of Direct Rendering Manager,
// Blanks screen if true, unblanks if false.
- virtual void blank_screen(bool blank);
+ virtual void blank_screen(bool blank, int drm);
+
+ // Rotate screen.
+ virtual void rotate_screen(int drm);
static std::unique_ptr<HealthdDraw> Create(animation *anim);
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 0f9779c..9fe85d4 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -327,7 +327,7 @@
#if !defined(__ANDROID_VNDK__)
if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
- healthd_draw_->blank_screen(true);
+ healthd_draw_->blank_screen(true, static_cast<int>(drm_));
screen_blanked_ = true;
}
#endif
@@ -337,7 +337,7 @@
if (batt_anim_.num_cycles > 0 && batt_anim_.cur_cycle == batt_anim_.num_cycles) {
reset_animation(&batt_anim_);
next_screen_transition_ = -1;
- healthd_draw_->blank_screen(true);
+ healthd_draw_->blank_screen(true, static_cast<int>(drm_));
screen_blanked_ = true;
LOGV("[%" PRId64 "] animation done\n", now);
if (configuration_->ChargerIsOnline()) {
@@ -348,8 +348,17 @@
disp_time = batt_anim_.frames[batt_anim_.cur_frame].disp_time;
+ /* turn off all screen */
+ if (screen_switch_ == SCREEN_SWITCH_ENABLE) {
+ healthd_draw_->blank_screen(true, 0 /* drm */);
+ healthd_draw_->blank_screen(true, 1 /* drm */);
+ healthd_draw_->rotate_screen(static_cast<int>(drm_));
+ screen_blanked_ = true;
+ screen_switch_ = SCREEN_SWITCH_DISABLE;
+ }
+
if (screen_blanked_) {
- healthd_draw_->blank_screen(false);
+ healthd_draw_->blank_screen(false, static_cast<int>(drm_));
screen_blanked_ = false;
}
@@ -452,7 +461,26 @@
return 0;
}
+int Charger::SetSwCallback(int code, int value) {
+ if (code > SW_MAX) return -1;
+ if (code == SW_LID) {
+ if ((screen_switch_ == SCREEN_SWITCH_DEFAULT) || ((value != 0) && (drm_ == DRM_INNER)) ||
+ ((value == 0) && (drm_ == DRM_OUTER))) {
+ screen_switch_ = SCREEN_SWITCH_ENABLE;
+ drm_ = (value != 0) ? DRM_OUTER : DRM_INNER;
+ keys_[code].pending = true;
+ }
+ }
+
+ return 0;
+}
+
void Charger::UpdateInputState(input_event* ev) {
+ if (ev->type == EV_SW && ev->code == SW_LID) {
+ SetSwCallback(ev->code, ev->value);
+ return;
+ }
+
if (ev->type != EV_KEY) return;
SetKeyCallback(ev->code, ev->value);
}
@@ -511,10 +539,26 @@
key->pending = false;
}
+void Charger::ProcessHallSensor(int code) {
+ key_state* key = &keys_[code];
+
+ if (code == SW_LID) {
+ if (key->pending) {
+ reset_animation(&batt_anim_);
+ kick_animation(&batt_anim_);
+ RequestDisableSuspend();
+ }
+ }
+
+ key->pending = false;
+}
+
void Charger::HandleInputState(int64_t now) {
ProcessKey(KEY_POWER, now);
if (next_key_check_ != -1 && now > next_key_check_) next_key_check_ = -1;
+
+ ProcessHallSensor(SW_LID);
}
void Charger::HandlePowerSupplyState(int64_t now) {
@@ -743,9 +787,14 @@
batt_anim_.frames[i].surface = scale_frames[i];
}
}
+ drm_ = DRM_INNER;
+ screen_switch_ = SCREEN_SWITCH_DEFAULT;
ev_sync_key_state(std::bind(&Charger::SetKeyCallback, this, std::placeholders::_1,
std::placeholders::_2));
+ (void)ev_sync_sw_state(
+ std::bind(&Charger::SetSwCallback, this, std::placeholders::_1, std::placeholders::_2));
+
next_screen_transition_ = -1;
next_key_check_ = -1;
next_pwr_check_ = -1;
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 3cda727..89c2e25 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -48,7 +48,8 @@
ANDROID_POWER_SUPPLY_TYPE_AC,
ANDROID_POWER_SUPPLY_TYPE_USB,
ANDROID_POWER_SUPPLY_TYPE_WIRELESS,
- ANDROID_POWER_SUPPLY_TYPE_BATTERY
+ ANDROID_POWER_SUPPLY_TYPE_BATTERY,
+ ANDROID_POWER_SUPPLY_TYPE_DOCK
};
BatteryMonitor();
@@ -75,6 +76,8 @@
bool mBatteryDevicePresent;
int mBatteryFixedCapacity;
int mBatteryFixedTemperature;
+ // TODO(b/214126090): to migrate to AIDL HealthInfo
+ bool mChargerDockOnline;
std::unique_ptr<android::hardware::health::V2_1::HealthInfo> mHealthInfo;
int readFromFile(const String8& path, std::string* buf);
diff --git a/healthd/include_charger/charger/healthd_mode_charger.h b/healthd/include_charger/charger/healthd_mode_charger.h
index 216e5ad..28e1fb5 100644
--- a/healthd/include_charger/charger/healthd_mode_charger.h
+++ b/healthd/include_charger/charger/healthd_mode_charger.h
@@ -44,6 +44,17 @@
aidl::android::hardware::health::BatteryStatus battery_status;
};
+enum DirectRenderManager {
+ DRM_INNER,
+ DRM_OUTER,
+};
+
+enum SrceenSwitch {
+ SCREEN_SWITCH_DEFAULT,
+ SCREEN_SWITCH_DISABLE,
+ SCREEN_SWITCH_ENABLE,
+};
+
// Configuration interface for charger. This includes:
// - HalHealthLoop APIs that interests charger.
// - configuration values that used to be provided by sysprops
@@ -85,9 +96,11 @@
void InitDefaultAnimationFrames();
void UpdateScreenState(int64_t now);
int SetKeyCallback(int code, int value);
+ int SetSwCallback(int code, int value);
void UpdateInputState(input_event* ev);
void SetNextKeyCheck(key_state* key, int64_t timeout);
void ProcessKey(int code, int64_t now);
+ void ProcessHallSensor(int code);
void HandleInputState(int64_t now);
void HandlePowerSupplyState(int64_t now);
int InputCallback(int fd, unsigned int epevents);
@@ -102,6 +115,9 @@
int64_t next_pwr_check_ = 0;
int64_t wait_batt_level_timestamp_ = 0;
+ DirectRenderManager drm_;
+ SrceenSwitch screen_switch_;
+
key_state keys_[KEY_MAX + 1] = {};
animation batt_anim_;
diff --git a/init/Android.bp b/init/Android.bp
index 66427dc..c39d163 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -162,12 +162,15 @@
"libavb",
"libc++fs",
"libcgrouprc_format",
+ "libfsverity_init",
"liblmkd_utils",
+ "libmini_keyctl_static",
"libmodprobe",
"libprocinfo",
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
+ "libsigningutils",
"libsnapshot_cow",
"libsnapshot_init",
"libxml2",
@@ -178,6 +181,7 @@
"libbacktrace",
"libbase",
"libbootloader_message",
+ "libcrypto",
"libcutils",
"libdl",
"libext4_utils",
@@ -192,6 +196,7 @@
"libprocessgroup_setup",
"libselinux",
"libutils",
+ "libziparchive",
],
bootstrap: true,
visibility: [":__subpackages__"],
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index c7b7b0c..3cd0252 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -254,6 +254,9 @@
// stage init
CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"))
+
+ // First stage init stores Mainline sepolicy here.
+ CHECKCALL(mkdir("/dev/selinux", 0744));
#undef CHECKCALL
SetStdioToDevNull(argv);
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 70e26ec..9f7c215 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1035,8 +1035,8 @@
constexpr auto VENDOR_API_LEVEL_PROP = "ro.vendor.api_level";
// Api level properties of the board. The order of the properties must be kept.
- std::vector<std::string> BOARD_API_LEVEL_PROPS = {
- "ro.board.api_level", "ro.board.first_api_level", "ro.vendor.build.version.sdk"};
+ std::vector<std::string> BOARD_API_LEVEL_PROPS = {"ro.board.api_level",
+ "ro.board.first_api_level"};
// Api level properties of the device. The order of the properties must be kept.
std::vector<std::string> DEVICE_API_LEVEL_PROPS = {"ro.product.first_api_level",
"ro.build.version.sdk"};
@@ -1107,6 +1107,7 @@
LoadPropertiesFromSecondStageRes(&properties);
load_properties_from_file("/system/build.prop", nullptr, &properties);
load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
+ load_properties_from_file("/system_dlkm/etc/build.prop", nullptr, &properties);
// TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
// all updated.
// if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
@@ -1171,6 +1172,9 @@
// Don't check for failure here, since we don't always have all of these partitions.
// E.g. In case of recovery, the vendor partition will not have mounted and we
// still need the system / platform properties to function.
+ if (access("/dev/selinux/apex_property_contexts", R_OK) != -1) {
+ LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
+ }
if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
LoadPropertyInfoFromFile("/system_ext/etc/selinux/system_ext_property_contexts",
&property_infos);
@@ -1194,6 +1198,7 @@
LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos);
LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
+ LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
}
auto serialized_contexts = std::string();
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 28cd012..c89c5ab 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -26,26 +26,29 @@
// The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy
// file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.
-// The split policy is for supporting treble devices. It splits the SEPolicy across files on
-// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'vendor'
-// portion of the policy). This is necessary to allow the system image to be updated independently
-// of the vendor image, while maintaining contributions from both partitions in the SEPolicy. This
-// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be
-// identical to the system image shipped on a vendor's device.
+// The split policy is for supporting treble devices and updateable apexes. It splits the SEPolicy
+// across files on /system/etc/selinux (the 'plat' portion of the policy), /vendor/etc/selinux
+// (the 'vendor' portion of the policy), /system_ext/etc/selinux, /product/etc/selinux,
+// /odm/etc/selinux, and /dev/selinux (the apex portion of policy). This is necessary to allow
+// images to be updated independently of the vendor image, while maintaining contributions from
+// multiple partitions in the SEPolicy. This is especially important for VTS testing, where the
+// SEPolicy on the Google System Image may not be identical to the system image shipped on a
+// vendor's device.
// The split SEPolicy is loaded as described below:
// 1) There is a precompiled SEPolicy located at either /vendor/etc/selinux/precompiled_sepolicy or
// /odm/etc/selinux/precompiled_sepolicy if odm parition is present. Stored along with this file
-// are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext and /product that
-// were used to compile this precompiled policy. The system partition contains a similar sha256
-// of the parts of the SEPolicy that it currently contains. Symmetrically, system_ext and
-// product paritition contain sha256 hashes of their SEPolicy. The init loads this
+// are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext, /product, and apex
+// that were used to compile this precompiled policy. The system partition contains a similar
+// sha256 of the parts of the SEPolicy that it currently contains. Symmetrically, system_ext,
+// product, and apex contain sha256 hashes of their SEPolicy. Init loads this
// precompiled_sepolicy directly if and only if the hashes along with the precompiled SEPolicy on
-// /vendor or /odm match the hashes for system, system_ext and product SEPolicy, respectively.
-// 2) If these hashes do not match, then either /system or /system_ext or /product (or some of them)
-// have been updated out of sync with /vendor (or /odm if it is present) and the init needs to
-// compile the SEPolicy. /system contains the SEPolicy compiler, secilc, and it is used by the
-// OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
+// /vendor or /odm match the hashes for system, system_ext, product, and apex SEPolicy,
+// respectively.
+// 2) If these hashes do not match, then either /system or /system_ext /product, or apex (or some of
+// them) have been updated out of sync with /vendor (or /odm if it is present) and the init needs
+// to compile the SEPolicy. /system contains the SEPolicy compiler, secilc, and it is used by
+// the OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
// That function contains even more documentation with the specific implementation details of how
// the SEPolicy is compiled if needed.
@@ -58,19 +61,25 @@
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <fstream>
+#include <CertUtils.h>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/result.h>
+#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <fs_avb/fs_avb.h>
#include <fs_mgr.h>
+#include <fsverity_init.h>
#include <libgsi/libgsi.h>
#include <libsnapshot/snapshot.h>
+#include <mini_keyctl_utils.h>
#include <selinux/android.h>
+#include <ziparchive/zip_archive.h>
#include "block_dev_initializer.h"
#include "debug_ramdisk.h"
@@ -247,6 +256,7 @@
precompiled_sepolicy + ".system_ext_sepolicy_and_mapping.sha256"},
{"/product/etc/selinux/product_sepolicy_and_mapping.sha256",
precompiled_sepolicy + ".product_sepolicy_and_mapping.sha256"},
+ {"/dev/selinux/apex_sepolicy.sha256", precompiled_sepolicy + ".apex_sepolicy.sha256"},
};
for (const auto& [actual_id_path, precompiled_id_path] : sepolicy_hashes) {
@@ -325,7 +335,7 @@
// * vendor -- policy needed due to logic contained in the vendor image,
// * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
// with newer versions of platform policy.
- // * (optional) policy needed due to logic on product, system_ext, or odm images.
+ // * (optional) policy needed due to logic on product, system_ext, odm, or apex.
// secilc is invoked to compile the above three policy files into a single monolithic policy
// file. This file is then loaded into the kernel.
@@ -421,6 +431,12 @@
if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
odm_policy_cil_file.clear();
}
+
+ // apex_sepolicy.cil is default but optional.
+ std::string apex_policy_cil_file("/dev/selinux/apex_sepolicy.cil");
+ if (access(apex_policy_cil_file.c_str(), F_OK) == -1) {
+ apex_policy_cil_file.clear();
+ }
const std::string version_as_string = std::to_string(SEPOLICY_VERSION);
// clang-format off
@@ -463,6 +479,9 @@
if (!odm_policy_cil_file.empty()) {
compile_args.push_back(odm_policy_cil_file.c_str());
}
+ if (!apex_policy_cil_file.empty()) {
+ compile_args.push_back(apex_policy_cil_file.c_str());
+ }
compile_args.push_back(nullptr);
if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
@@ -489,6 +508,197 @@
return true;
}
+constexpr const char* kSigningCertRelease =
+ "/system/etc/selinux/com.android.sepolicy.cert-release.der";
+constexpr const char* kFsVerityProcPath = "/proc/sys/fs/verity";
+const std::string kSepolicyApexMetadataDir = "/metadata/sepolicy/";
+const std::string kSepolicyApexSystemDir = "/system/etc/selinux/apex/";
+const std::string kSepolicyZip = "SEPolicy.zip";
+const std::string kSepolicySignature = "SEPolicy.zip.sig";
+
+const std::string kTmpfsDir = "/dev/selinux/";
+
+// Files that are deleted after policy is compiled/loaded.
+const std::vector<std::string> kApexSepolicyTmp{"apex_sepolicy.cil", "apex_sepolicy.sha256"};
+// Files that need to persist because they are used by userspace processes.
+const std::vector<std::string> kApexSepolicy{"apex_file_contexts", "apex_property_contexts",
+ "apex_service_contexts", "apex_seapp_contexts",
+ "apex_test"};
+
+Result<void> PutFileInTmpfs(ZipArchiveHandle archive, const std::string& fileName) {
+ ZipEntry entry;
+ std::string dstPath = kTmpfsDir + fileName;
+
+ int ret = FindEntry(archive, fileName, &entry);
+ if (ret != 0) {
+ // All files are optional. If a file doesn't exist, return without error.
+ return {};
+ }
+
+ unique_fd fd(TEMP_FAILURE_RETRY(
+ open(dstPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR)));
+ if (fd == -1) {
+ return Error() << "Failed to open " << dstPath;
+ }
+
+ ret = ExtractEntryToFile(archive, &entry, fd);
+ if (ret != 0) {
+ return Error() << "Failed to extract entry \"" << fileName << "\" ("
+ << entry.uncompressed_length << " bytes) to \"" << dstPath
+ << "\": " << ErrorCodeString(ret);
+ }
+
+ return {};
+}
+
+Result<void> GetPolicyFromApex(const std::string& dir) {
+ LOG(INFO) << "Loading APEX Sepolicy from " << dir + kSepolicyZip;
+ unique_fd fd(open((dir + kSepolicyZip).c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
+ if (fd < 0) {
+ return ErrnoError() << "Failed to open package " << dir + kSepolicyZip;
+ }
+
+ ZipArchiveHandle handle;
+ int ret = OpenArchiveFd(fd.get(), (dir + kSepolicyZip).c_str(), &handle,
+ /*assume_ownership=*/false);
+ if (ret < 0) {
+ return Error() << "Failed to open package " << dir + kSepolicyZip << ": "
+ << ErrorCodeString(ret);
+ }
+
+ auto handle_guard = android::base::make_scope_guard([&handle] { CloseArchive(handle); });
+
+ for (const auto& file : kApexSepolicy) {
+ auto extract = PutFileInTmpfs(handle, file);
+ if (!extract.ok()) {
+ return extract.error();
+ }
+ }
+ for (const auto& file : kApexSepolicyTmp) {
+ auto extract = PutFileInTmpfs(handle, file);
+ if (!extract.ok()) {
+ return extract.error();
+ }
+ }
+ return {};
+}
+
+Result<void> LoadSepolicyApexCerts() {
+ key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
+ if (keyring_id < 0) {
+ return Error() << "Failed to find .fs-verity keyring id";
+ }
+
+ // TODO(b/199914227) the release key should always exist. Once it's checked in, start
+ // throwing an error here if it doesn't exist.
+ if (access(kSigningCertRelease, F_OK) == 0) {
+ LoadKeyFromFile(keyring_id, "fsv_sepolicy_apex_release", kSigningCertRelease);
+ }
+ return {};
+}
+
+Result<void> SepolicyFsVerityCheck() {
+ return Error() << "TODO implementent support for fsverity SEPolicy.";
+}
+
+Result<void> SepolicyCheckSignature(const std::string& dir) {
+ std::string signature;
+ if (!android::base::ReadFileToString(dir + kSepolicySignature, &signature)) {
+ return ErrnoError() << "Failed to read " << kSepolicySignature;
+ }
+
+ std::fstream sepolicyZip(dir + kSepolicyZip, std::ios::in | std::ios::binary);
+ if (!sepolicyZip) {
+ return Error() << "Failed to open " << kSepolicyZip;
+ }
+ sepolicyZip.seekg(0);
+ std::string sepolicyStr((std::istreambuf_iterator<char>(sepolicyZip)),
+ std::istreambuf_iterator<char>());
+
+ auto releaseKey = extractPublicKeyFromX509(kSigningCertRelease);
+ if (!releaseKey.ok()) {
+ return releaseKey.error();
+ }
+
+ return verifySignature(sepolicyStr, signature, *releaseKey);
+}
+
+Result<void> SepolicyVerify(const std::string& dir, bool supportsFsVerity) {
+ if (supportsFsVerity) {
+ auto fsVerityCheck = SepolicyFsVerityCheck();
+ if (fsVerityCheck.ok()) {
+ return fsVerityCheck;
+ }
+ // TODO(b/199914227) If the device supports fsverity, but we fail here, we should fail to
+ // boot and not carry on. For now, fallback to a signature checkuntil the fsverity
+ // logic is implemented.
+ LOG(INFO) << "Falling back to standard signature check. " << fsVerityCheck.error();
+ }
+
+ auto sepolicySignature = SepolicyCheckSignature(dir);
+ if (!sepolicySignature.ok()) {
+ return Error() << "Apex SEPolicy failed signature check";
+ }
+ return {};
+}
+
+void CleanupApexSepolicy() {
+ for (const auto& file : kApexSepolicyTmp) {
+ std::string path = kTmpfsDir + file;
+ unlink(path.c_str());
+ }
+}
+
+// Updatable sepolicy is shipped within an zip within an APEX. Because
+// it needs to be available before Apexes are mounted, apexd copies
+// the zip from the APEX and stores it in /metadata/sepolicy. If there is
+// no updatable sepolicy in /metadata/sepolicy, then the updatable policy is
+// loaded from /system/etc/selinux/apex. Init performs the following
+// steps on boot:
+//
+// 1. Validates the zip by checking its signature against a public key that is
+// stored in /system/etc/selinux.
+// 2. Extracts files from zip and stores them in /dev/selinux.
+// 3. Checks if the apex_sepolicy.sha256 matches the sha256 of precompiled_sepolicy.
+// if so, the precompiled sepolicy is used. Otherwise, an on-device compile of the policy
+// is used. This is the same flow as on-device compilation of policy for Treble.
+// 4. Cleans up files in /dev/selinux which are no longer needed.
+// 5. Restorecons the remaining files in /dev/selinux.
+// 6. Sets selinux into enforcing mode and continues normal booting.
+//
+void PrepareApexSepolicy() {
+ bool supportsFsVerity = access(kFsVerityProcPath, F_OK) == 0;
+ if (supportsFsVerity) {
+ auto loadSepolicyApexCerts = LoadSepolicyApexCerts();
+ if (!loadSepolicyApexCerts.ok()) {
+ // TODO(b/199914227) If the device supports fsverity, but we fail here, we should fail
+ // to boot and not carry on. For now, fallback to a signature checkuntil the fsverity
+ // logic is implemented.
+ LOG(INFO) << loadSepolicyApexCerts.error();
+ }
+ }
+ // If apex sepolicy zip exists in /metadata/sepolicy, use that, otherwise use version on
+ // /system.
+ auto dir = (access((kSepolicyApexMetadataDir + kSepolicyZip).c_str(), F_OK) == 0)
+ ? kSepolicyApexMetadataDir
+ : kSepolicyApexSystemDir;
+
+ auto sepolicyVerify = SepolicyVerify(dir, supportsFsVerity);
+ if (!sepolicyVerify.ok()) {
+ LOG(INFO) << "Error: " << sepolicyVerify.error();
+ // If signature verification fails, fall back to version on /system.
+ // This file doesn't need to be verified because it lives on the system partition which
+ // is signed and protected by verified boot.
+ dir = kSepolicyApexSystemDir;
+ }
+
+ auto apex = GetPolicyFromApex(dir);
+ if (!apex.ok()) {
+ // TODO(b/199914227) Make failure fatal. For now continue booting with non-apex sepolicy.
+ LOG(ERROR) << apex.error();
+ }
+}
+
void ReadPolicy(std::string* policy) {
PolicyFile policy_file;
@@ -740,9 +950,12 @@
LOG(INFO) << "Opening SELinux policy";
+ PrepareApexSepolicy();
+
// Read the policy before potentially killing snapuserd.
std::string policy;
ReadPolicy(&policy);
+ CleanupApexSepolicy();
auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
if (snapuserd_helper) {
@@ -760,6 +973,13 @@
snapuserd_helper = nullptr;
}
+ // This restorecon is intentionally done before SelinuxSetEnforcement because the permissions
+ // needed to transition files from tmpfs to *_contexts_file context should not be granted to
+ // any process after selinux is set into enforcing mode.
+ if (selinux_android_restorecon("/dev/selinux/", SELINUX_ANDROID_RESTORECON_RECURSE) == -1) {
+ PLOG(FATAL) << "restorecon failed of /dev/selinux failed";
+ }
+
SelinuxSetEnforcement();
// We're in the kernel domain and want to transition to the init domain. File systems that
diff --git a/init/service.cpp b/init/service.cpp
index f7318cb..f6dd9b9 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -397,6 +397,14 @@
return {};
}
+static void ClosePipe(const std::array<int, 2>* pipe) {
+ for (const auto fd : *pipe) {
+ if (fd >= 0) {
+ close(fd);
+ }
+ }
+}
+
Result<void> Service::Start() {
auto reboot_on_failure = make_scope_guard([this] {
if (on_failure_reboot_target_) {
@@ -428,6 +436,12 @@
return {};
}
+ std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd(new std::array<int, 2>{-1, -1},
+ ClosePipe);
+ if (pipe(pipefd->data()) < 0) {
+ return ErrnoError() << "pipe()";
+ }
+
bool needs_console = (flags_ & SVC_CONSOLE);
if (needs_console) {
if (proc_attr_.console.empty()) {
@@ -532,6 +546,13 @@
LOG(ERROR) << "failed to write pid to files: " << result.error();
}
+ // Wait until the cgroups have been created and until the cgroup controllers have been
+ // activated.
+ if (std::byte byte; read((*pipefd)[0], &byte, 1) < 0) {
+ PLOG(ERROR) << "failed to read from notification channel";
+ }
+ pipefd.reset();
+
if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
LOG(ERROR) << "failed to set task profiles";
}
@@ -618,6 +639,10 @@
LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_);
}
+ if (write((*pipefd)[1], "", 1) < 0) {
+ return ErrnoError() << "sending notification failed";
+ }
+
NotifyStateChange("running");
reboot_on_failure.Disable();
return {};
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index f2383d7..263cb73 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -190,8 +190,6 @@
// Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.
fcntl(fd, F_SETFL, flags);
- LOG(INFO) << "Opened file '" << name << "', flags " << flags;
-
return Descriptor(ANDROID_FILE_ENV_PREFIX + name, std::move(fd));
}
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
index e11510e..5deaf31 100644
--- a/init/snapuserd_transition.cpp
+++ b/init/snapuserd_transition.cpp
@@ -32,6 +32,7 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
+#include <fs_avb/fs_avb.h>
#include <libsnapshot/snapshot.h>
#include <private/android_filesystem_config.h>
#include <procinfo/process_map.h>
@@ -247,6 +248,56 @@
}
}
+/*
+ * Before starting init second stage, we will wait
+ * for snapuserd daemon to be up and running; bionic libc
+ * may read /system/etc/selinux/plat_property_contexts file
+ * before invoking main() function. This will happen if
+ * init initializes property during second stage. Any access
+ * to /system without snapuserd daemon will lead to a deadlock.
+ *
+ * Thus, we do a simple probe by reading system partition. This
+ * read will eventually be serviced by daemon confirming that
+ * daemon is up and running. Furthermore, we are still in the kernel
+ * domain and sepolicy has not been enforced yet. Thus, access
+ * to these device mapper block devices are ok even though
+ * we may see audit logs.
+ */
+bool SnapuserdSelinuxHelper::TestSnapuserdIsReady() {
+ std::string dev = "/dev/block/mapper/system"s + fs_mgr_get_slot_suffix();
+ android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_DIRECT));
+ if (fd < 0) {
+ PLOG(ERROR) << "open " << dev << " failed";
+ return false;
+ }
+
+ void* addr;
+ ssize_t page_size = getpagesize();
+ if (posix_memalign(&addr, page_size, page_size) < 0) {
+ PLOG(ERROR) << "posix_memalign with page size " << page_size;
+ return false;
+ }
+
+ std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free);
+
+ int iter = 0;
+ while (iter < 10) {
+ ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), buffer.get(), page_size, 0));
+ if (n < 0) {
+ // Wait for sometime before retry
+ std::this_thread::sleep_for(100ms);
+ } else if (n == page_size) {
+ return true;
+ } else {
+ LOG(ERROR) << "pread returned: " << n << " from: " << dev << " expected: " << page_size;
+ }
+
+ iter += 1;
+ }
+
+ return false;
+}
+
void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() {
auto fd = GetRamdiskSnapuserdFd();
if (!fd) {
@@ -268,6 +319,13 @@
setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
+
+ if (!TestSnapuserdIsReady()) {
+ PLOG(FATAL) << "snapuserd daemon failed to launch";
+ } else {
+ LOG(INFO) << "snapuserd daemon is up and running";
+ }
+
return;
}
diff --git a/init/snapuserd_transition.h b/init/snapuserd_transition.h
index be22afd..557d105 100644
--- a/init/snapuserd_transition.h
+++ b/init/snapuserd_transition.h
@@ -56,6 +56,7 @@
private:
void RelaunchFirstStageSnapuserd();
void ExecSnapuserd();
+ bool TestSnapuserdIsReady();
std::unique_ptr<SnapshotManager> sm_;
BlockDevInitializer block_dev_init_;
diff --git a/libcutils/include/cutils/qtaguid.h b/libcutils/include/cutils/qtaguid.h
index 3f5e41f..a5ffb03 100644
--- a/libcutils/include/cutils/qtaguid.h
+++ b/libcutils/include/cutils/qtaguid.h
@@ -34,24 +34,6 @@
extern int qtaguid_untagSocket(int sockfd);
/*
- * For the given uid, switch counter sets.
- * The kernel only keeps a limited number of sets.
- * 2 for now.
- */
-extern int qtaguid_setCounterSet(int counterSetNum, uid_t uid);
-
-/*
- * Delete all tag info that relates to the given tag an uid.
- * If the tag is 0, then ALL info about the uid is freed.
- * The delete data also affects active tagged sockets, which are
- * then untagged.
- * The calling process can only operate on its own tags.
- * Unless it is part of the happy AID_NET_BW_ACCT group.
- * In which case it can clobber everything.
- */
-extern int qtaguid_deleteTagData(int tag, uid_t uid);
-
-/*
* Enable/disable qtaguid functionnality at a lower level.
* When pacified, the kernel will accept commands but do nothing.
*/
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 97e93f8..17a0070 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -75,7 +75,8 @@
#define ATRACE_TAG_AIDL (1<<24)
#define ATRACE_TAG_NNAPI (1<<25)
#define ATRACE_TAG_RRO (1<<26)
-#define ATRACE_TAG_LAST ATRACE_TAG_RRO
+#define ATRACE_TAG_THERMAL (1 << 27)
+#define ATRACE_TAG_LAST ATRACE_TAG_THERMAL
// Reserved for initialization.
#define ATRACE_TAG_NOT_READY (1ULL<<63)
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 23d1415..8e6b81c 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -132,6 +132,7 @@
#define AID_UWB 1083 /* UWB subsystem */
#define AID_THREAD_NETWORK 1084 /* Thread Network subsystem */
#define AID_DICED 1085 /* Android's DICE daemon */
+#define AID_DMESGD 1086 /* dmesg parsing daemon for kernel report collection */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libcutils/qtaguid.cpp b/libcutils/qtaguid.cpp
index 2fe877c..a987b85 100644
--- a/libcutils/qtaguid.cpp
+++ b/libcutils/qtaguid.cpp
@@ -34,8 +34,6 @@
public:
int (*netdTagSocket)(int, uint32_t, uid_t);
int (*netdUntagSocket)(int);
- int (*netdSetCounterSet)(uint32_t, uid_t);
- int (*netdDeleteTagData)(uint32_t, uid_t);
};
int stubTagSocket(int, uint32_t, uid_t) {
@@ -46,16 +44,8 @@
return -EREMOTEIO;
}
-int stubSetCounterSet(uint32_t, uid_t) {
- return -EREMOTEIO;
-}
-
-int stubDeleteTagData(uint32_t, uid_t) {
- return -EREMOTEIO;
-}
-
netdHandler initHandler(void) {
- netdHandler handler = {stubTagSocket, stubUntagSocket, stubSetCounterSet, stubDeleteTagData};
+ netdHandler handler = {stubTagSocket, stubUntagSocket};
void* netdClientHandle = dlopen("libnetd_client.so", RTLD_NOW);
if (!netdClientHandle) {
@@ -73,15 +63,6 @@
ALOGE("load netdUntagSocket handler failed: %s", dlerror());
}
- handler.netdSetCounterSet = (int (*)(uint32_t, uid_t))dlsym(netdClientHandle, "setCounterSet");
- if (!handler.netdSetCounterSet) {
- ALOGE("load netdSetCounterSet handler failed: %s", dlerror());
- }
-
- handler.netdDeleteTagData = (int (*)(uint32_t, uid_t))dlsym(netdClientHandle, "deleteTagData");
- if (!handler.netdDeleteTagData) {
- ALOGE("load netdDeleteTagData handler failed: %s", dlerror());
- }
return handler;
}
@@ -114,13 +95,3 @@
ALOGV("Untagging socket %d", sockfd);
return getHandler().netdUntagSocket(sockfd);
}
-
-int qtaguid_setCounterSet(int counterSetNum, uid_t uid) {
- ALOGV("Setting counters to set %d for uid %d", counterSetNum, uid);
- return getHandler().netdSetCounterSet(counterSetNum, uid);
-}
-
-int qtaguid_deleteTagData(int tag, uid_t uid) {
- ALOGV("Deleting tag data with tag %u for uid %d", tag, uid);
- return getHandler().netdDeleteTagData(tag, uid);
-}
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index f31b5f1..16103d2 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -44,4 +44,9 @@
shared_libs: ["android.hardware.graphics.allocator@2.0"],
header_libs: ["libhardware_headers"],
min_sdk_version: "29",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media.swcodec",
+ "test_com.android.media.swcodec",
+ ],
}
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 352847a..8c00326 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -231,7 +231,8 @@
const ACgroupController* controller = ACgroupFile_getController(i);
if (ACgroupController_getFlags(controller) &
CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
- std::string str = std::string("+") + ACgroupController_getName(controller);
+ std::string str("+");
+ str.append(ACgroupController_getName(controller));
if (!WriteStringToFile(str, path + "/cgroup.subtree_control")) {
return -errno;
}
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index be34f95..c5badc9 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -35,6 +35,8 @@
#ifndef __ANDROID_VNDK__
+bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
+
static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc";
bool UsePerAppMemcg();
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 0320b02..76d5e13 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -85,7 +85,7 @@
bool CgroupGetAttributePath(const std::string& attr_name, std::string* path) {
const TaskProfiles& tp = TaskProfiles::GetInstance();
- const ProfileAttribute* attr = tp.GetAttribute(attr_name);
+ const IProfileAttribute* attr = tp.GetAttribute(attr_name);
if (attr == nullptr) {
return false;
@@ -100,7 +100,7 @@
bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path) {
const TaskProfiles& tp = TaskProfiles::GetInstance();
- const ProfileAttribute* attr = tp.GetAttribute(attr_name);
+ const IProfileAttribute* attr = tp.GetAttribute(attr_name);
if (attr == nullptr) {
return false;
@@ -126,11 +126,16 @@
}
void DropTaskProfilesResourceCaching() {
- TaskProfiles::GetInstance().DropResourceCaching();
+ TaskProfiles::GetInstance().DropResourceCaching(ProfileAction::RCT_TASK);
+ TaskProfiles::GetInstance().DropResourceCaching(ProfileAction::RCT_PROCESS);
}
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
- return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles);
+ return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, false);
+}
+
+bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
+ return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, true);
}
bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) {
@@ -206,7 +211,7 @@
for (std::string cgroup_root_path : cgroups) {
std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir);
if (root == NULL) {
- PLOG(ERROR) << "Failed to open " << cgroup_root_path;
+ PLOG(ERROR) << __func__ << " failed to open " << cgroup_root_path;
} else {
dirent* dir;
while ((dir = readdir(root.get())) != nullptr) {
@@ -292,7 +297,8 @@
// This happens when process is already dead
return 0;
}
- PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
+ PLOG(WARNING) << __func__ << " failed to open process cgroup uid " << uid << " pid "
+ << initialPid;
return -1;
}
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 3834f91..ffcfeb8 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -51,6 +51,69 @@
static constexpr const char* TEMPLATE_TASK_PROFILE_API_FILE =
"/etc/task_profiles/task_profiles_%u.json";
+class FdCacheHelper {
+ public:
+ enum FdState {
+ FDS_INACCESSIBLE = -1,
+ FDS_APP_DEPENDENT = -2,
+ FDS_NOT_CACHED = -3,
+ };
+
+ static void Cache(const std::string& path, android::base::unique_fd& fd);
+ static void Drop(android::base::unique_fd& fd);
+ static void Init(const std::string& path, android::base::unique_fd& fd);
+ static bool IsCached(const android::base::unique_fd& fd) { return fd > FDS_INACCESSIBLE; }
+
+ private:
+ static bool IsAppDependentPath(const std::string& path);
+};
+
+void FdCacheHelper::Init(const std::string& path, android::base::unique_fd& fd) {
+ // file descriptors for app-dependent paths can't be cached
+ if (IsAppDependentPath(path)) {
+ // file descriptor is not cached
+ fd.reset(FDS_APP_DEPENDENT);
+ return;
+ }
+ // file descriptor can be cached later on request
+ fd.reset(FDS_NOT_CACHED);
+}
+
+void FdCacheHelper::Cache(const std::string& path, android::base::unique_fd& fd) {
+ if (fd != FDS_NOT_CACHED) {
+ return;
+ }
+
+ if (access(path.c_str(), W_OK) != 0) {
+ // file is not accessible
+ fd.reset(FDS_INACCESSIBLE);
+ return;
+ }
+
+ unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC)));
+ if (tmp_fd < 0) {
+ PLOG(ERROR) << "Failed to cache fd '" << path << "'";
+ fd.reset(FDS_INACCESSIBLE);
+ return;
+ }
+
+ fd = std::move(tmp_fd);
+}
+
+void FdCacheHelper::Drop(android::base::unique_fd& fd) {
+ if (fd == FDS_NOT_CACHED) {
+ return;
+ }
+
+ fd.reset(FDS_NOT_CACHED);
+}
+
+bool FdCacheHelper::IsAppDependentPath(const std::string& path) {
+ return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
+}
+
+IProfileAttribute::~IProfileAttribute() = default;
+
void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) {
controller_ = controller;
file_name_ = file_name;
@@ -122,6 +185,12 @@
return true;
}
+#else
+
+bool SetTimerSlackAction::ExecuteForTask(int) const {
+ return true;
+};
+
#endif
bool SetAttributeAction::ExecuteForProcess(uid_t, pid_t pid) const {
@@ -144,57 +213,11 @@
return true;
}
-void CachedFdProfileAction::EnableResourceCaching() {
- std::lock_guard<std::mutex> lock(fd_mutex_);
- if (fd_ != FDS_NOT_CACHED) {
- return;
- }
-
- std::string tasks_path = GetPath();
-
- if (access(tasks_path.c_str(), W_OK) != 0) {
- // file is not accessible
- fd_.reset(FDS_INACCESSIBLE);
- return;
- }
-
- unique_fd fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
- if (fd < 0) {
- PLOG(ERROR) << "Failed to cache fd '" << tasks_path << "'";
- fd_.reset(FDS_INACCESSIBLE);
- return;
- }
-
- fd_ = std::move(fd);
-}
-
-void CachedFdProfileAction::DropResourceCaching() {
- std::lock_guard<std::mutex> lock(fd_mutex_);
- if (fd_ == FDS_NOT_CACHED) {
- return;
- }
-
- fd_.reset(FDS_NOT_CACHED);
-}
-
-bool CachedFdProfileAction::IsAppDependentPath(const std::string& path) {
- return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
-}
-
-void CachedFdProfileAction::InitFd(const std::string& path) {
- // file descriptors for app-dependent paths can't be cached
- if (IsAppDependentPath(path)) {
- // file descriptor is not cached
- fd_.reset(FDS_APP_DEPENDENT);
- return;
- }
- // file descriptor can be cached later on request
- fd_.reset(FDS_NOT_CACHED);
-}
-
SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
: controller_(c), path_(p) {
- InitFd(controller_.GetTasksFilePath(path_));
+ FdCacheHelper::Init(controller_.GetTasksFilePath(path_), fd_[ProfileAction::RCT_TASK]);
+ // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them
+ FdCacheHelper::Init(controller_.GetProcsFilePath(path_, 0, 0), fd_[ProfileAction::RCT_PROCESS]);
}
bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_name) {
@@ -232,7 +255,40 @@
return false;
}
+ProfileAction::CacheUseResult SetCgroupAction::UseCachedFd(ResourceCacheType cache_type,
+ int id) const {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ if (FdCacheHelper::IsCached(fd_[cache_type])) {
+ // fd is cached, reuse it
+ if (!AddTidToCgroup(id, fd_[cache_type], controller()->name())) {
+ LOG(ERROR) << "Failed to add task into cgroup";
+ return ProfileAction::FAIL;
+ }
+ return ProfileAction::SUCCESS;
+ }
+
+ if (fd_[cache_type] == FdCacheHelper::FDS_INACCESSIBLE) {
+ // no permissions to access the file, ignore
+ return ProfileAction::SUCCESS;
+ }
+
+ if (cache_type == ResourceCacheType::RCT_TASK &&
+ fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {
+ // application-dependent path can't be used with tid
+ PLOG(ERROR) << "Application profile can't be applied to a thread";
+ return ProfileAction::FAIL;
+ }
+
+ return ProfileAction::UNUSED;
+}
+
bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+ CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, pid);
+ if (result != ProfileAction::UNUSED) {
+ return result == ProfileAction::SUCCESS;
+ }
+
+ // fd was not cached or cached fd can't be used
std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
if (tmp_fd < 0) {
@@ -248,28 +304,12 @@
}
bool SetCgroupAction::ExecuteForTask(int tid) const {
- std::lock_guard<std::mutex> lock(fd_mutex_);
- if (IsFdValid()) {
- // fd is cached, reuse it
- if (!AddTidToCgroup(tid, fd_, controller()->name())) {
- LOG(ERROR) << "Failed to add task into cgroup";
- return false;
- }
- return true;
+ CacheUseResult result = UseCachedFd(ProfileAction::RCT_TASK, tid);
+ if (result != ProfileAction::UNUSED) {
+ return result == ProfileAction::SUCCESS;
}
- if (fd_ == FDS_INACCESSIBLE) {
- // no permissions to access the file, ignore
- return true;
- }
-
- if (fd_ == FDS_APP_DEPENDENT) {
- // application-dependent path can't be used with tid
- PLOG(ERROR) << "Application profile can't be applied to a thread";
- return false;
- }
-
- // fd was not cached because cached fd can't be used
+ // fd was not cached or cached fd can't be used
std::string tasks_path = controller()->GetTasksFilePath(path_);
unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
if (tmp_fd < 0) {
@@ -284,10 +324,36 @@
return true;
}
+void SetCgroupAction::EnableResourceCaching(ResourceCacheType cache_type) {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ // Return early to prevent unnecessary calls to controller_.Get{Tasks|Procs}FilePath() which
+ // include regex evaluations
+ if (fd_[cache_type] != FdCacheHelper::FDS_NOT_CACHED) {
+ return;
+ }
+ switch (cache_type) {
+ case (ProfileAction::RCT_TASK):
+ FdCacheHelper::Cache(controller_.GetTasksFilePath(path_), fd_[cache_type]);
+ break;
+ case (ProfileAction::RCT_PROCESS):
+ // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them
+ FdCacheHelper::Cache(controller_.GetProcsFilePath(path_, 0, 0), fd_[cache_type]);
+ break;
+ default:
+ LOG(ERROR) << "Invalid cache type is specified!";
+ break;
+ }
+}
+
+void SetCgroupAction::DropResourceCaching(ResourceCacheType cache_type) {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ FdCacheHelper::Drop(fd_[cache_type]);
+}
+
WriteFileAction::WriteFileAction(const std::string& path, const std::string& value,
bool logfailures)
: path_(path), value_(value), logfailures_(logfailures) {
- InitFd(path_);
+ FdCacheHelper::Init(path_, fd_);
}
bool WriteFileAction::WriteValueToFile(const std::string& value, const std::string& path,
@@ -309,13 +375,43 @@
return true;
}
-bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+ProfileAction::CacheUseResult WriteFileAction::UseCachedFd(ResourceCacheType cache_type,
+ const std::string& value) const {
std::lock_guard<std::mutex> lock(fd_mutex_);
+ if (FdCacheHelper::IsCached(fd_)) {
+ // fd is cached, reuse it
+ if (!WriteStringToFd(value, fd_)) {
+ if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << path_;
+ return ProfileAction::FAIL;
+ }
+ return ProfileAction::SUCCESS;
+ }
+
+ if (fd_ == FdCacheHelper::FDS_INACCESSIBLE) {
+ // no permissions to access the file, ignore
+ return ProfileAction::SUCCESS;
+ }
+
+ if (cache_type == ResourceCacheType::RCT_TASK && fd_ == FdCacheHelper::FDS_APP_DEPENDENT) {
+ // application-dependent path can't be used with tid
+ PLOG(ERROR) << "Application profile can't be applied to a thread";
+ return ProfileAction::FAIL;
+ }
+ return ProfileAction::UNUSED;
+}
+
+bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
std::string value(value_);
- std::string path(path_);
value = StringReplace(value, "<uid>", std::to_string(uid), true);
value = StringReplace(value, "<pid>", std::to_string(pid), true);
+
+ CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, value);
+ if (result != ProfileAction::UNUSED) {
+ return result == ProfileAction::SUCCESS;
+ }
+
+ std::string path(path_);
path = StringReplace(path, "<uid>", std::to_string(uid), true);
path = StringReplace(path, "<pid>", std::to_string(pid), true);
@@ -323,41 +419,33 @@
}
bool WriteFileAction::ExecuteForTask(int tid) const {
- std::lock_guard<std::mutex> lock(fd_mutex_);
std::string value(value_);
int uid = getuid();
value = StringReplace(value, "<uid>", std::to_string(uid), true);
value = StringReplace(value, "<pid>", std::to_string(tid), true);
- if (IsFdValid()) {
- // fd is cached, reuse it
- if (!WriteStringToFd(value, fd_)) {
- if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << path_;
- return false;
- }
- return true;
- }
-
- if (fd_ == FDS_INACCESSIBLE) {
- // no permissions to access the file, ignore
- return true;
- }
-
- if (fd_ == FDS_APP_DEPENDENT) {
- // application-dependent path can't be used with tid
- PLOG(ERROR) << "Application profile can't be applied to a thread";
- return false;
+ CacheUseResult result = UseCachedFd(ProfileAction::RCT_TASK, value);
+ if (result != ProfileAction::UNUSED) {
+ return result == ProfileAction::SUCCESS;
}
return WriteValueToFile(value, path_, logfailures_);
}
+void WriteFileAction::EnableResourceCaching(ResourceCacheType) {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ FdCacheHelper::Cache(path_, fd_);
+}
+
+void WriteFileAction::DropResourceCaching(ResourceCacheType) {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ FdCacheHelper::Drop(fd_);
+}
+
bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
for (const auto& profile : profiles_) {
- if (!profile->ExecuteForProcess(uid, pid)) {
- PLOG(WARNING) << "ExecuteForProcess failed for aggregate profile";
- }
+ profile->ExecuteForProcess(uid, pid);
}
return true;
}
@@ -369,15 +457,15 @@
return true;
}
-void ApplyProfileAction::EnableResourceCaching() {
+void ApplyProfileAction::EnableResourceCaching(ResourceCacheType cache_type) {
for (const auto& profile : profiles_) {
- profile->EnableResourceCaching();
+ profile->EnableResourceCaching(cache_type);
}
}
-void ApplyProfileAction::DropResourceCaching() {
+void ApplyProfileAction::DropResourceCaching(ResourceCacheType cache_type) {
for (const auto& profile : profiles_) {
- profile->DropResourceCaching();
+ profile->DropResourceCaching(cache_type);
}
}
@@ -407,33 +495,33 @@
return true;
}
-void TaskProfile::EnableResourceCaching() {
+void TaskProfile::EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) {
if (res_cached_) {
return;
}
for (auto& element : elements_) {
- element->EnableResourceCaching();
+ element->EnableResourceCaching(cache_type);
}
res_cached_ = true;
}
-void TaskProfile::DropResourceCaching() {
+void TaskProfile::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) {
if (!res_cached_) {
return;
}
for (auto& element : elements_) {
- element->DropResourceCaching();
+ element->DropResourceCaching(cache_type);
}
res_cached_ = false;
}
-void TaskProfiles::DropResourceCaching() const {
+void TaskProfiles::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const {
for (auto& iter : profiles_) {
- iter.second->DropResourceCaching();
+ iter.second->DropResourceCaching(cache_type);
}
}
@@ -457,8 +545,7 @@
android::base::StringPrintf(TEMPLATE_TASK_PROFILE_API_FILE, api_level);
if (!access(api_profiles_path.c_str(), F_OK) || errno != ENOENT) {
if (!Load(CgroupMap::GetInstance(), api_profiles_path)) {
- LOG(ERROR) << "Loading " << api_profiles_path << " for [" << getpid()
- << "] failed";
+ LOG(ERROR) << "Loading " << api_profiles_path << " for [" << getpid() << "] failed";
}
}
}
@@ -641,7 +728,7 @@
return nullptr;
}
-const ProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) const {
+const IProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) const {
auto iter = attributes_.find(name);
if (iter != attributes_.end()) {
@@ -651,10 +738,13 @@
}
bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
- const std::vector<std::string>& profiles) {
+ const std::vector<std::string>& profiles, bool use_fd_cache) {
for (const auto& name : profiles) {
TaskProfile* profile = GetProfile(name);
if (profile != nullptr) {
+ if (use_fd_cache) {
+ profile->EnableResourceCaching(ProfileAction::RCT_PROCESS);
+ }
if (!profile->ExecuteForProcess(uid, pid)) {
PLOG(WARNING) << "Failed to apply " << name << " process profile";
}
@@ -671,7 +761,7 @@
TaskProfile* profile = GetProfile(name);
if (profile != nullptr) {
if (use_fd_cache) {
- profile->EnableResourceCaching();
+ profile->EnableResourceCaching(ProfileAction::RCT_TASK);
}
if (!profile->ExecuteForTask(tid)) {
PLOG(WARNING) << "Failed to apply " << name << " task profile";
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 278892d..2f48664 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -26,16 +26,26 @@
#include <android-base/unique_fd.h>
#include <cgroup_map.h>
-class ProfileAttribute {
+class IProfileAttribute {
+ public:
+ virtual ~IProfileAttribute() = 0;
+ virtual void Reset(const CgroupController& controller, const std::string& file_name) = 0;
+ virtual const CgroupController* controller() const = 0;
+ virtual const std::string& file_name() const = 0;
+ virtual bool GetPathForTask(int tid, std::string* path) const = 0;
+};
+
+class ProfileAttribute : public IProfileAttribute {
public:
ProfileAttribute(const CgroupController& controller, const std::string& file_name)
: controller_(controller), file_name_(file_name) {}
+ ~ProfileAttribute() = default;
- const CgroupController* controller() const { return &controller_; }
- const std::string& file_name() const { return file_name_; }
- void Reset(const CgroupController& controller, const std::string& file_name);
+ const CgroupController* controller() const override { return &controller_; }
+ const std::string& file_name() const override { return file_name_; }
+ void Reset(const CgroupController& controller, const std::string& file_name) override;
- bool GetPathForTask(int tid, std::string* path) const;
+ bool GetPathForTask(int tid, std::string* path) const override;
private:
CgroupController controller_;
@@ -45,14 +55,19 @@
// Abstract profile element
class ProfileAction {
public:
+ enum ResourceCacheType { RCT_TASK = 0, RCT_PROCESS, RCT_COUNT };
+
virtual ~ProfileAction() {}
// Default implementations will fail
virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; };
virtual bool ExecuteForTask(int) const { return false; };
- virtual void EnableResourceCaching() {}
- virtual void DropResourceCaching() {}
+ virtual void EnableResourceCaching(ResourceCacheType) {}
+ virtual void DropResourceCaching(ResourceCacheType) {}
+
+ protected:
+ enum CacheUseResult { SUCCESS, FAIL, UNUSED };
};
// Profile actions
@@ -60,22 +75,19 @@
public:
SetClampsAction(int boost, int clamp) noexcept : boost_(boost), clamp_(clamp) {}
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
protected:
int boost_;
int clamp_;
};
-// To avoid issues in sdk_mac build
-#if defined(__ANDROID__)
-
class SetTimerSlackAction : public ProfileAction {
public:
SetTimerSlackAction(unsigned long slack) noexcept : slack_(slack) {}
- virtual bool ExecuteForTask(int tid) const;
+ bool ExecuteForTask(int tid) const override;
private:
unsigned long slack_;
@@ -83,92 +95,61 @@
static bool IsTimerSlackSupported(int tid);
};
-#else
-
-class SetTimerSlackAction : public ProfileAction {
- public:
- SetTimerSlackAction(unsigned long) noexcept {}
-
- virtual bool ExecuteForTask(int) const { return true; }
-};
-
-#endif
-
// Set attribute profile element
class SetAttributeAction : public ProfileAction {
public:
- SetAttributeAction(const ProfileAttribute* attribute, const std::string& value)
+ SetAttributeAction(const IProfileAttribute* attribute, const std::string& value)
: attribute_(attribute), value_(value) {}
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
private:
- const ProfileAttribute* attribute_;
+ const IProfileAttribute* attribute_;
std::string value_;
};
-// Abstract profile element for cached fd
-class CachedFdProfileAction : public ProfileAction {
- public:
- virtual void EnableResourceCaching();
- virtual void DropResourceCaching();
-
- protected:
- enum FdState {
- FDS_INACCESSIBLE = -1,
- FDS_APP_DEPENDENT = -2,
- FDS_NOT_CACHED = -3,
- };
-
- android::base::unique_fd fd_;
- mutable std::mutex fd_mutex_;
-
- static bool IsAppDependentPath(const std::string& path);
-
- void InitFd(const std::string& path);
- bool IsFdValid() const { return fd_ > FDS_INACCESSIBLE; }
-
- virtual const std::string GetPath() const = 0;
-};
-
// Set cgroup profile element
-class SetCgroupAction : public CachedFdProfileAction {
+class SetCgroupAction : public ProfileAction {
public:
SetCgroupAction(const CgroupController& c, const std::string& p);
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
+ void EnableResourceCaching(ResourceCacheType cache_type) override;
+ void DropResourceCaching(ResourceCacheType cache_type) override;
const CgroupController* controller() const { return &controller_; }
- protected:
- const std::string GetPath() const override { return controller_.GetTasksFilePath(path_); }
-
private:
CgroupController controller_;
std::string path_;
+ android::base::unique_fd fd_[ProfileAction::RCT_COUNT];
+ mutable std::mutex fd_mutex_;
static bool AddTidToCgroup(int tid, int fd, const char* controller_name);
+ CacheUseResult UseCachedFd(ResourceCacheType cache_type, int id) const;
};
// Write to file action
-class WriteFileAction : public CachedFdProfileAction {
+class WriteFileAction : public ProfileAction {
public:
WriteFileAction(const std::string& path, const std::string& value, bool logfailures);
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
-
- protected:
- const std::string GetPath() const override { return path_; }
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
+ void EnableResourceCaching(ResourceCacheType cache_type) override;
+ void DropResourceCaching(ResourceCacheType cache_type) override;
private:
std::string path_, value_;
bool logfailures_;
+ android::base::unique_fd fd_;
+ mutable std::mutex fd_mutex_;
static bool WriteValueToFile(const std::string& value, const std::string& path,
bool logfailures);
+ CacheUseResult UseCachedFd(ResourceCacheType cache_type, const std::string& value) const;
};
class TaskProfile {
@@ -180,8 +161,8 @@
bool ExecuteForProcess(uid_t uid, pid_t pid) const;
bool ExecuteForTask(int tid) const;
- void EnableResourceCaching();
- void DropResourceCaching();
+ void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type);
+ void DropResourceCaching(ProfileAction::ResourceCacheType cache_type);
private:
bool res_cached_;
@@ -194,10 +175,10 @@
ApplyProfileAction(const std::vector<std::shared_ptr<TaskProfile>>& profiles)
: profiles_(profiles) {}
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
- virtual void EnableResourceCaching();
- virtual void DropResourceCaching();
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
+ void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
+ void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
private:
std::vector<std::shared_ptr<TaskProfile>> profiles_;
@@ -209,14 +190,15 @@
static TaskProfiles& GetInstance();
TaskProfile* GetProfile(const std::string& name) const;
- const ProfileAttribute* GetAttribute(const std::string& name) const;
- void DropResourceCaching() const;
- bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
+ const IProfileAttribute* GetAttribute(const std::string& name) const;
+ void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const;
+ bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
+ bool use_fd_cache);
bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache);
private:
std::map<std::string, std::shared_ptr<TaskProfile>> profiles_;
- std::map<std::string, std::unique_ptr<ProfileAttribute>> attributes_;
+ std::map<std::string, std::unique_ptr<IProfileAttribute>> attributes_;
TaskProfiles();
diff --git a/libsparse/img2simg.cpp b/libsparse/img2simg.cpp
index 4c2c6ca..3e24cc0 100644
--- a/libsparse/img2simg.cpp
+++ b/libsparse/img2simg.cpp
@@ -93,7 +93,7 @@
}
sparse_file_verbose(s);
- ret = sparse_file_read(s, in, false, false);
+ ret = sparse_file_read(s, in, SPARSE_READ_MODE_NORMAL, false);
if (ret) {
fprintf(stderr, "Failed to read file\n");
exit(-1);
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 9f91269..7c52c3f 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -225,23 +225,42 @@
int (*write)(void *priv, const void *data, size_t len, unsigned int block,
unsigned int nr_blocks),
void *priv);
+
+/**
+ * enum sparse_read_mode - The method to use when reading in files
+ * @SPARSE_READ_MODE_NORMAL: The input is a regular file. Constant chunks of
+ * data (including holes) will be be converted to
+ * fill chunks.
+ * @SPARSE_READ_MODE_SPARSE: The input is an Android sparse file.
+ * @SPARSE_READ_MODE_HOLE: The input is a regular file. Holes will be converted
+ * to "don't care" chunks. Other constant chunks will
+ * be converted to fill chunks.
+ */
+enum sparse_read_mode {
+ SPARSE_READ_MODE_NORMAL = false,
+ SPARSE_READ_MODE_SPARSE = true,
+ SPARSE_READ_MODE_HOLE,
+};
+
/**
* sparse_file_read - read a file into a sparse file cookie
*
* @s - sparse file cookie
* @fd - file descriptor to read from
- * @sparse - read a file in the Android sparse file format
+ * @mode - mode to use when reading the input file
* @crc - verify the crc of a file in the Android sparse file format
*
- * Reads a file into a sparse file cookie. If sparse is true, the file is
- * assumed to be in the Android sparse file format. If sparse is false, the
- * file will be sparsed by looking for block aligned chunks of all zeros or
- * another 32 bit value. If crc is true, the crc of the sparse file will be
- * verified.
+ * Reads a file into a sparse file cookie. If @mode is
+ * %SPARSE_READ_MODE_SPARSE, the file is assumed to be in the Android sparse
+ * file format. If @mode is %SPARSE_READ_MODE_NORMAL, the file will be sparsed
+ * by looking for block aligned chunks of all zeros or another 32 bit value. If
+ * @mode is %SPARSE_READ_MODE_HOLE, the file will be sparsed like
+ * %SPARSE_READ_MODE_NORMAL, but holes in the file will be converted to "don't
+ * care" chunks. If crc is true, the crc of the sparse file will be verified.
*
* Returns 0 on success, negative errno on error.
*/
-int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc);
+int sparse_file_read(struct sparse_file *s, int fd, enum sparse_read_mode mode, bool crc);
/**
* sparse_file_import - import an existing sparse file
diff --git a/libsparse/sparse_read.cpp b/libsparse/sparse_read.cpp
index 0f39172..028b6be 100644
--- a/libsparse/sparse_read.cpp
+++ b/libsparse/sparse_read.cpp
@@ -457,12 +457,10 @@
return 0;
}
-static int sparse_file_read_normal(struct sparse_file* s, int fd) {
+static int do_sparse_file_read_normal(struct sparse_file* s, int fd, uint32_t* buf, int64_t offset,
+ int64_t remain) {
int ret;
- uint32_t* buf = (uint32_t*)malloc(s->block_size);
- unsigned int block = 0;
- int64_t remain = s->len;
- int64_t offset = 0;
+ unsigned int block = offset / s->block_size;
unsigned int to_read;
unsigned int i;
bool sparse_block;
@@ -476,7 +474,6 @@
ret = read_all(fd, buf, to_read);
if (ret < 0) {
error("failed to read sparse file");
- free(buf);
return ret;
}
@@ -504,20 +501,93 @@
block++;
}
- free(buf);
return 0;
}
-int sparse_file_read(struct sparse_file* s, int fd, bool sparse, bool crc) {
- if (crc && !sparse) {
+static int sparse_file_read_normal(struct sparse_file* s, int fd) {
+ int ret;
+ uint32_t* buf = (uint32_t*)malloc(s->block_size);
+
+ if (!buf)
+ return -ENOMEM;
+
+ ret = do_sparse_file_read_normal(s, fd, buf, 0, s->len);
+ free(buf);
+ return ret;
+}
+
+#ifdef __linux__
+static int sparse_file_read_hole(struct sparse_file* s, int fd) {
+ int ret;
+ uint32_t* buf = (uint32_t*)malloc(s->block_size);
+ int64_t end = 0;
+ int64_t start = 0;
+
+ if (!buf) {
+ return -ENOMEM;
+ }
+
+ do {
+ start = lseek(fd, end, SEEK_DATA);
+ if (start < 0) {
+ if (errno == ENXIO)
+ /* The rest of the file is a hole */
+ break;
+
+ error("could not seek to data");
+ free(buf);
+ return -errno;
+ } else if (start > s->len) {
+ break;
+ }
+
+ end = lseek(fd, start, SEEK_HOLE);
+ if (end < 0) {
+ error("could not seek to end");
+ free(buf);
+ return -errno;
+ }
+ end = std::min(end, s->len);
+
+ start = ALIGN_DOWN(start, s->block_size);
+ end = ALIGN(end, s->block_size);
+ if (lseek(fd, start, SEEK_SET) < 0) {
+ free(buf);
+ return -errno;
+ }
+
+ ret = do_sparse_file_read_normal(s, fd, buf, start, end - start);
+ if (ret) {
+ free(buf);
+ return ret;
+ }
+ } while (end < s->len);
+
+ free(buf);
+ return 0;
+}
+#else
+static int sparse_file_read_hole(struct sparse_file* s __unused, int fd __unused) {
+ return -ENOTSUP;
+}
+#endif
+
+int sparse_file_read(struct sparse_file* s, int fd, enum sparse_read_mode mode, bool crc) {
+ if (crc && mode != SPARSE_READ_MODE_SPARSE) {
return -EINVAL;
}
- if (sparse) {
- SparseFileFdSource source(fd);
- return sparse_file_read_sparse(s, &source, crc);
- } else {
- return sparse_file_read_normal(s, fd);
+ switch (mode) {
+ case SPARSE_READ_MODE_SPARSE: {
+ SparseFileFdSource source(fd);
+ return sparse_file_read_sparse(s, &source, crc);
+ }
+ case SPARSE_READ_MODE_NORMAL:
+ return sparse_file_read_normal(s, fd);
+ case SPARSE_READ_MODE_HOLE:
+ return sparse_file_read_hole(s, fd);
+ default:
+ return -EINVAL;
}
}
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
index 3883317..9ae73d0 100644
--- a/libusbhost/Android.bp
+++ b/libusbhost/Android.bp
@@ -30,11 +30,9 @@
export_include_dirs: ["include"],
target: {
android: {
- cflags: [
- "-g",
- "-DUSE_LIBLOG",
- ],
+ header_libs: ["jni_headers"],
shared_libs: ["liblog"],
+ srcs: ["usbhost_jni.cpp"],
},
darwin: {
enabled: false,
diff --git a/libusbhost/include/usbhost/usbhost.h b/libusbhost/include/usbhost/usbhost.h
index 7e62542..01cd68b 100644
--- a/libusbhost/include/usbhost/usbhost.h
+++ b/libusbhost/include/usbhost/usbhost.h
@@ -21,6 +21,7 @@
extern "C" {
#endif
+#include <stddef.h>
#include <stdint.h>
#include <linux/version.h>
diff --git a/libusbhost/include/usbhost/usbhost_jni.h b/libusbhost/include/usbhost/usbhost_jni.h
new file mode 100644
index 0000000..4885d45
--- /dev/null
+++ b/libusbhost/include/usbhost/usbhost_jni.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include <jni.h>
+
+/**
+ * Reads USB descriptors from `fd`.
+ *
+ * Returns a byte[] on success,
+ * or returns NULL and logs an appropriate error on failure.
+ */
+jbyteArray usb_jni_read_descriptors(JNIEnv* env, int fd);
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 3bed0e3..d8f15cd 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -18,20 +18,9 @@
#define _GNU_SOURCE
#endif
-// #define DEBUG 1
-#if DEBUG
+#include <usbhost/usbhost.h>
-#ifdef USE_LIBLOG
-#define LOG_TAG "usbhost"
-#include "log/log.h"
-#define D ALOGD
-#else
-#define D printf
-#endif
-
-#else
-#define D(...)
-#endif
+#include "usbhost_private.h"
#include <stdio.h>
#include <stdlib.h>
@@ -48,12 +37,19 @@
#include <errno.h>
#include <ctype.h>
#include <poll.h>
-#include <pthread.h>
#include <linux/usbdevice_fs.h>
-#include <asm/byteorder.h>
-#include "usbhost/usbhost.h"
+// #define DEBUG 1
+#if defined(DEBUG)
+#if defined(__BIONIC__)
+#define D ALOGD
+#else
+#define D printf
+#endif
+#else
+#define D(...)
+#endif
#define DEV_DIR "/dev"
#define DEV_BUS_DIR DEV_DIR "/bus"
@@ -76,8 +72,6 @@
int wddbus;
};
-#define MAX_DESCRIPTORS_LENGTH 4096
-
struct usb_device {
char dev_name[64];
unsigned char desc[MAX_DESCRIPTORS_LENGTH];
diff --git a/libusbhost/usbhost_jni.cpp b/libusbhost/usbhost_jni.cpp
new file mode 100644
index 0000000..0da83dc
--- /dev/null
+++ b/libusbhost/usbhost_jni.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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 <usbhost/usbhost_jni.h>
+
+#include "usbhost_private.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+jbyteArray usb_jni_read_descriptors(JNIEnv* env, int fd) {
+ if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) {
+ ALOGE("usb_jni_read_descriptors(%d): lseek() failed: %s", fd, strerror(errno));
+ return NULL;
+ }
+
+ jbyte buf[MAX_DESCRIPTORS_LENGTH];
+ ssize_t n = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf)));
+ if (n == -1) {
+ ALOGE("usb_jni_read_descriptors: read failed: %s", strerror(errno));
+ return NULL;
+ }
+
+ jbyteArray result = env->NewByteArray(n);
+ if (result) env->SetByteArrayRegion(result, 0, n, buf);
+ return result;
+}
diff --git a/libusbhost/usbhost_private.h b/libusbhost/usbhost_private.h
new file mode 100644
index 0000000..72d7938
--- /dev/null
+++ b/libusbhost/usbhost_private.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#define LOG_TAG "usbhost"
+#include <log/log.h>
+
+// Somewhat arbitrary: Sony has reported needing more than 4KiB (but less
+// than 8KiB), and some frameworks code had 16KiB without any explanation,
+// so we went with the largest of those.
+#define MAX_DESCRIPTORS_LENGTH (16 * 1024)
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 234638b..53f6065 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -100,7 +100,6 @@
cflags: ["-fvisibility=protected"],
shared_libs: [
- "libprocessgroup",
"libvndksupport",
],
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 3bf5779..4dacdc6 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -84,14 +84,6 @@
delete t;
setpriority(PRIO_PROCESS, 0, prio);
- // A new thread will be in its parent's sched group by default,
- // so we just need to handle the background case.
- // currently set to system_background group which is different
- // from background group for app.
- if (prio >= ANDROID_PRIORITY_BACKGROUND) {
- SetTaskProfiles(0, {"SCHED_SP_SYSTEM"}, true);
- }
-
if (name) {
androidSetThreadName(name);
free(name);
@@ -307,27 +299,16 @@
int androidSetThreadPriority(pid_t tid, int pri)
{
int rc = 0;
- int lasterr = 0;
int curr_pri = getpriority(PRIO_PROCESS, tid);
if (curr_pri == pri) {
return rc;
}
- if (pri >= ANDROID_PRIORITY_BACKGROUND) {
- rc = SetTaskProfiles(tid, {"SCHED_SP_SYSTEM"}, true) ? 0 : -1;
- } else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) {
- rc = SetTaskProfiles(tid, {"SCHED_SP_FOREGROUND"}, true) ? 0 : -1;
- }
-
- if (rc) {
- lasterr = errno;
- }
-
if (setpriority(PRIO_PROCESS, tid, pri) < 0) {
rc = INVALID_OPERATION;
} else {
- errno = lasterr;
+ errno = 0;
}
return rc;
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index ce2ec0e..11f414f 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -1,5 +1,7 @@
LOCAL_PATH:= $(call my-dir)
+$(eval $(call declare-1p-copy-files,system/core/rootdir,))
+
#######################################
# init-debug.rc
include $(CLEAR_VARS)
@@ -110,9 +112,6 @@
ifdef BOARD_USES_METADATA_PARTITION
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/metadata
endif
-ifdef BOARD_USES_SYSTEM_DLKM_PARTITION
- LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_dlkm
-endif
# For /odm partition.
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm
@@ -152,6 +151,9 @@
# via /odm/lib/modules directly.
LOCAL_POST_INSTALL_CMD += ; ln -sf /odm/odm_dlkm/etc $(TARGET_ROOT_OUT)/odm_dlkm/etc
+# For /system_dlkm partition
+LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_dlkm
+
ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
else
@@ -197,15 +199,9 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
LOCAL_MODULE_STEM := $(LOCAL_MODULE)
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $(addsuffix .so,\
- $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
- $(HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
- $(UBSAN_RUNTIME_LIBRARY) \
- $(TSAN_RUNTIME_LIBRARY) \
- $(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
- $(2ND_HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
- $(2ND_UBSAN_RUNTIME_LIBRARY) \
- $(2ND_TSAN_RUNTIME_LIBRARY))
+$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := \
+ $(SANITIZER_STEMS) \
+ $(2ND_SANITIZER_STEMS)
$(LOCAL_BUILT_MODULE):
@echo "Generate: $@"
@mkdir -p $(dir $@)
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
index d62c41d..780ace5 100644
--- a/rootdir/etc/linker.config.json
+++ b/rootdir/etc/linker.config.json
@@ -20,6 +20,8 @@
"libicuuc.so",
// resolv
"libnetd_resolv.so",
+ // netd
+ "libnetd_updatable.so",
// nn
"libneuralnetworks.so",
// statsd
diff --git a/rootdir/init.no_zygote.rc b/rootdir/init.no_zygote.rc
new file mode 100644
index 0000000..8e0afcb
--- /dev/null
+++ b/rootdir/init.no_zygote.rc
@@ -0,0 +1,2 @@
+# This is an empty file that does not specify any zygote services.
+# It exists to support targets that do not include a zygote.
diff --git a/rootdir/init.rc b/rootdir/init.rc
index cd73498..c4c9eca 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -142,11 +142,21 @@
chown system system /dev/stune/background/tasks
chown system system /dev/stune/top-app/tasks
chown system system /dev/stune/rt/tasks
+ chown system system /dev/stune/cgroup.procs
+ chown system system /dev/stune/foreground/cgroup.procs
+ chown system system /dev/stune/background/cgroup.procs
+ chown system system /dev/stune/top-app/cgroup.procs
+ chown system system /dev/stune/rt/cgroup.procs
chmod 0664 /dev/stune/tasks
chmod 0664 /dev/stune/foreground/tasks
chmod 0664 /dev/stune/background/tasks
chmod 0664 /dev/stune/top-app/tasks
chmod 0664 /dev/stune/rt/tasks
+ chmod 0664 /dev/stune/cgroup.procs
+ chmod 0664 /dev/stune/foreground/cgroup.procs
+ chmod 0664 /dev/stune/background/cgroup.procs
+ chmod 0664 /dev/stune/top-app/cgroup.procs
+ chmod 0664 /dev/stune/rt/cgroup.procs
# cpuctl hierarchy for devices using utilclamp
mkdir /dev/cpuctl/foreground
@@ -172,6 +182,14 @@
chown system system /dev/cpuctl/system/tasks
chown system system /dev/cpuctl/system-background/tasks
chown system system /dev/cpuctl/dex2oat/tasks
+ chown system system /dev/cpuctl/cgroup.procs
+ chown system system /dev/cpuctl/foreground/cgroup.procs
+ chown system system /dev/cpuctl/background/cgroup.procs
+ chown system system /dev/cpuctl/top-app/cgroup.procs
+ chown system system /dev/cpuctl/rt/cgroup.procs
+ chown system system /dev/cpuctl/system/cgroup.procs
+ chown system system /dev/cpuctl/system-background/cgroup.procs
+ chown system system /dev/cpuctl/dex2oat/cgroup.procs
chmod 0664 /dev/cpuctl/tasks
chmod 0664 /dev/cpuctl/foreground/tasks
chmod 0664 /dev/cpuctl/background/tasks
@@ -180,12 +198,22 @@
chmod 0664 /dev/cpuctl/system/tasks
chmod 0664 /dev/cpuctl/system-background/tasks
chmod 0664 /dev/cpuctl/dex2oat/tasks
+ chmod 0664 /dev/cpuctl/cgroup.procs
+ chmod 0664 /dev/cpuctl/foreground/cgroup.procs
+ chmod 0664 /dev/cpuctl/background/cgroup.procs
+ chmod 0664 /dev/cpuctl/top-app/cgroup.procs
+ chmod 0664 /dev/cpuctl/rt/cgroup.procs
+ chmod 0664 /dev/cpuctl/system/cgroup.procs
+ chmod 0664 /dev/cpuctl/system-background/cgroup.procs
+ chmod 0664 /dev/cpuctl/dex2oat/cgroup.procs
# Create a cpu group for NNAPI HAL processes
mkdir /dev/cpuctl/nnapi-hal
chown system system /dev/cpuctl/nnapi-hal
chown system system /dev/cpuctl/nnapi-hal/tasks
+ chown system system /dev/cpuctl/nnapi-hal/cgroup.procs
chmod 0664 /dev/cpuctl/nnapi-hal/tasks
+ chmod 0664 /dev/cpuctl/nnapi-hal/cgroup.procs
write /dev/cpuctl/nnapi-hal/cpu.uclamp.min 1
write /dev/cpuctl/nnapi-hal/cpu.uclamp.latency_sensitive 1
@@ -193,19 +221,25 @@
mkdir /dev/cpuctl/camera-daemon
chown system system /dev/cpuctl/camera-daemon
chown system system /dev/cpuctl/camera-daemon/tasks
+ chown system system /dev/cpuctl/camera-daemon/cgroup.procs
chmod 0664 /dev/cpuctl/camera-daemon/tasks
+ chmod 0664 /dev/cpuctl/camera-daemon/cgroup.procs
# Create an stune group for camera-specific processes
mkdir /dev/stune/camera-daemon
chown system system /dev/stune/camera-daemon
chown system system /dev/stune/camera-daemon/tasks
+ chown system system /dev/stune/camera-daemon/cgroup.procs
chmod 0664 /dev/stune/camera-daemon/tasks
+ chmod 0664 /dev/stune/camera-daemon/cgroup.procs
# Create an stune group for NNAPI HAL processes
mkdir /dev/stune/nnapi-hal
chown system system /dev/stune/nnapi-hal
chown system system /dev/stune/nnapi-hal/tasks
+ chown system system /dev/stune/nnapi-hal/cgroup.procs
chmod 0664 /dev/stune/nnapi-hal/tasks
+ chmod 0664 /dev/stune/nnapi-hal/cgroup.procs
write /dev/stune/nnapi-hal/schedtune.boost 1
write /dev/stune/nnapi-hal/schedtune.prefer_idle 1
@@ -217,8 +251,12 @@
chown system system /dev/blkio/background
chown system system /dev/blkio/tasks
chown system system /dev/blkio/background/tasks
+ chown system system /dev/blkio/cgroup.procs
+ chown system system /dev/blkio/background/cgroup.procs
chmod 0664 /dev/blkio/tasks
chmod 0664 /dev/blkio/background/tasks
+ chmod 0664 /dev/blkio/cgroup.procs
+ chmod 0664 /dev/blkio/background/cgroup.procs
write /dev/blkio/blkio.weight 1000
write /dev/blkio/background/blkio.weight 200
write /dev/blkio/background/blkio.bfq.weight 10
@@ -367,6 +405,13 @@
chown system system /dev/cpuset/top-app/tasks
chown system system /dev/cpuset/restricted/tasks
chown system system /dev/cpuset/camera-daemon/tasks
+ chown system system /dev/cpuset/cgroup.procs
+ chown system system /dev/cpuset/foreground/cgroup.procs
+ chown system system /dev/cpuset/background/cgroup.procs
+ chown system system /dev/cpuset/system-background/cgroup.procs
+ chown system system /dev/cpuset/top-app/cgroup.procs
+ chown system system /dev/cpuset/restricted/cgroup.procs
+ chown system system /dev/cpuset/camera-daemon/cgroup.procs
# set system-background to 0775 so SurfaceFlinger can touch it
chmod 0775 /dev/cpuset/system-background
@@ -378,6 +423,13 @@
chmod 0664 /dev/cpuset/restricted/tasks
chmod 0664 /dev/cpuset/tasks
chmod 0664 /dev/cpuset/camera-daemon/tasks
+ chmod 0664 /dev/cpuset/foreground/cgroup.procs
+ chmod 0664 /dev/cpuset/background/cgroup.procs
+ chmod 0664 /dev/cpuset/system-background/cgroup.procs
+ chmod 0664 /dev/cpuset/top-app/cgroup.procs
+ chmod 0664 /dev/cpuset/restricted/cgroup.procs
+ chmod 0664 /dev/cpuset/cgroup.procs
+ chmod 0664 /dev/cpuset/camera-daemon/cgroup.procs
# make the PSI monitor accessible to others
chown system system /proc/pressure/memory
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index 38d8685..94f26d8 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -35,7 +35,10 @@
"liblog",
"libhardware_legacy",
],
- header_libs: ["libcutils_headers"],
+ header_libs: [
+ "libcutils_headers",
+ "libgsi_headers",
+ ],
static_libs: [
"libfstab",
diff --git a/trusty/storage/proxy/checkpoint_handling.cpp b/trusty/storage/proxy/checkpoint_handling.cpp
index 6c2fd36..3305d8d 100644
--- a/trusty/storage/proxy/checkpoint_handling.cpp
+++ b/trusty/storage/proxy/checkpoint_handling.cpp
@@ -18,9 +18,12 @@
#include "log.h"
#include <fstab/fstab.h>
+#include <unistd.h>
#include <cstring>
#include <string>
+#include <libgsi/libgsi.h>
+
namespace {
bool checkpointingDoneForever = false;
@@ -75,3 +78,15 @@
return 0;
}
+
+/**
+ * is_gsi_running() - Check if a GSI image is running via DSU.
+ *
+ * This function is equivalent to android::gsi::IsGsiRunning(), but this API is
+ * not yet vendor-accessible although the underlying metadata file is.
+ *
+ */
+bool is_gsi_running() {
+ /* TODO(b/210501710): Expose GSI image running state to vendor storageproxyd */
+ return !access(android::gsi::kGsiBootedIndicatorFile, F_OK);
+}
diff --git a/trusty/storage/proxy/checkpoint_handling.h b/trusty/storage/proxy/checkpoint_handling.h
index f1bf27c..dfe2947 100644
--- a/trusty/storage/proxy/checkpoint_handling.h
+++ b/trusty/storage/proxy/checkpoint_handling.h
@@ -32,6 +32,8 @@
*/
int is_data_checkpoint_active(bool* active);
+bool is_gsi_running();
+
#ifdef __cplusplus
}
#endif
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index c690a28..2620034 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -104,8 +104,11 @@
return -1;
}
- /* no-execute for user, no access for group and other */
- umask(S_IXUSR | S_IRWXG | S_IRWXO);
+ /*
+ * No access for group and other. We need execute access for user to create
+ * an accessible directory.
+ */
+ umask(S_IRWXG | S_IRWXO);
return 0;
}
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
index 2fde30f..c00c399 100644
--- a/trusty/storage/proxy/storage.c
+++ b/trusty/storage/proxy/storage.c
@@ -16,6 +16,7 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <libgen.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
@@ -24,26 +25,29 @@
#include <sys/types.h>
#include <unistd.h>
-#include "log.h"
+#include "checkpoint_handling.h"
#include "ipc.h"
+#include "log.h"
#include "storage.h"
#define FD_TBL_SIZE 64
#define MAX_READ_SIZE 4096
+#define ALTERNATE_DATA_DIR "alternate/"
+
enum sync_state {
SS_UNUSED = -1,
SS_CLEAN = 0,
SS_DIRTY = 1,
};
-static int ssdir_fd = -1;
static const char *ssdir_name;
static enum sync_state fs_state;
-static enum sync_state dir_state;
static enum sync_state fd_state[FD_TBL_SIZE];
+static bool alternate_mode;
+
static struct {
struct storage_file_read_resp hdr;
uint8_t data[MAX_READ_SIZE];
@@ -53,10 +57,6 @@
{
uint32_t handle = fd;
- if (open_flags & O_CREAT) {
- dir_state = SS_DIRTY;
- }
-
if (handle < FD_TBL_SIZE) {
fd_state[fd] = SS_CLEAN; /* fd clean */
if (open_flags & O_TRUNC) {
@@ -187,7 +187,6 @@
goto err_response;
}
- dir_state = SS_DIRTY;
rc = unlink(path);
if (rc < 0) {
rc = errno;
@@ -211,11 +210,21 @@
return ipc_respond(msg, NULL, 0);
}
+static void sync_parent(const char* path) {
+ int parent_fd;
+ char* parent_path = dirname(path);
+ parent_fd = TEMP_FAILURE_RETRY(open(parent_path, O_RDONLY));
+ if (parent_fd >= 0) {
+ fsync(parent_fd);
+ close(parent_fd);
+ } else {
+ ALOGE("%s: failed to open parent directory \"%s\" for sync: %s\n", __func__, parent_path,
+ strerror(errno));
+ }
+}
-int storage_file_open(struct storage_msg *msg,
- const void *r, size_t req_len)
-{
- char *path = NULL;
+int storage_file_open(struct storage_msg* msg, const void* r, size_t req_len) {
+ char* path = NULL;
const struct storage_file_open_req *req = r;
struct storage_file_open_resp resp = {0};
@@ -234,6 +243,24 @@
goto err_response;
}
+ /*
+ * TODO(b/210501710): Expose GSI image running state to vendor
+ * storageproxyd. We want to control data file paths in vendor_init, but we
+ * don't have access to the necessary property there yet. When we have
+ * access to that property we can set the root data path read-only and only
+ * allow creation of files in alternate/. Checking paths here temporarily
+ * until that is fixed.
+ *
+ * We are just checking for "/" instead of "alternate/" because we still
+ * want to still allow access to "persist/" in alternate mode (for now, this
+ * may change in the future).
+ */
+ if (alternate_mode && !strchr(req->name, '/')) {
+ ALOGE("%s: Cannot open root data file \"%s\" in alternate mode\n", __func__, req->name);
+ msg->result = STORAGE_ERR_ACCESS;
+ goto err_response;
+ }
+
int rc = asprintf(&path, "%s/%s", ssdir_name, req->name);
if (rc < 0) {
ALOGE("%s: asprintf failed\n", __func__);
@@ -247,6 +274,24 @@
open_flags |= O_TRUNC;
if (req->flags & STORAGE_FILE_OPEN_CREATE) {
+ /*
+ * Create the alternate parent dir if needed & allowed.
+ *
+ * TODO(b/210501710): Expose GSI image running state to vendor
+ * storageproxyd. This directory should be created by vendor_init, once
+ * it has access to the necessary bit of information.
+ */
+ if (strstr(req->name, ALTERNATE_DATA_DIR) == req->name) {
+ char* parent_path = dirname(path);
+ rc = mkdir(parent_path, S_IRWXU);
+ if (rc == 0) {
+ sync_parent(parent_path);
+ } else if (errno != EEXIST) {
+ ALOGE("%s: Could not create parent directory \"%s\": %s\n", __func__, parent_path,
+ strerror(errno));
+ }
+ }
+
/* open or create */
if (req->flags & STORAGE_FILE_OPEN_CREATE_EXCLUSIVE) {
/* create exclusive */
@@ -279,6 +324,10 @@
msg->result = translate_errno(rc);
goto err_response;
}
+
+ if (open_flags & O_CREAT) {
+ sync_parent(path);
+ }
free(path);
/* at this point rc contains storage file fd */
@@ -467,17 +516,14 @@
int storage_init(const char *dirname)
{
+ /* If there is an active DSU image, use the alternate fs mode. */
+ alternate_mode = is_gsi_running();
+
fs_state = SS_CLEAN;
- dir_state = SS_CLEAN;
for (uint i = 0; i < FD_TBL_SIZE; i++) {
fd_state[i] = SS_UNUSED; /* uninstalled */
}
- ssdir_fd = open(dirname, O_RDONLY);
- if (ssdir_fd < 0) {
- ALOGE("failed to open ss root dir \"%s\": %s\n",
- dirname, strerror(errno));
- }
ssdir_name = dirname;
return 0;
}
@@ -501,25 +547,16 @@
}
}
- /* check if we need to sync the directory */
- if (dir_state == SS_DIRTY) {
- if (fs_state == SS_CLEAN) {
- rc = fsync(ssdir_fd);
- if (rc < 0) {
- ALOGE("fsync for ssdir failed: %s\n", strerror(errno));
- return rc;
- }
- }
- dir_state = SS_CLEAN; /* set to clean */
- }
-
- /* check if we need to sync the whole fs */
+ /* check if we need to sync all filesystems */
if (fs_state == SS_DIRTY) {
- rc = syscall(SYS_syncfs, ssdir_fd);
- if (rc < 0) {
- ALOGE("syncfs failed: %s\n", strerror(errno));
- return rc;
- }
+ /*
+ * We sync all filesystems here because we don't know what filesystem
+ * needs syncing if there happen to be other filesystems symlinked under
+ * the root data directory. This should not happen in the normal case
+ * because our fd table is large enough to handle the few open files we
+ * use.
+ */
+ sync();
fs_state = SS_CLEAN;
}