Merge "Revert "init_first_stage: Disable ThinLTO"" into main
diff --git a/OWNERS b/OWNERS
index 682a067..96b4f54 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1 +1,2 @@
+# Bug component: 128577
enh@google.com
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index ca59ef3..0c8760c 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -72,9 +72,6 @@
],
init_rc: ["bootstat.rc"],
product_variables: {
- pdk: {
- enabled: false,
- },
debuggable: {
init_rc: ["bootstat-debug.rc"],
},
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index d20de6b..267571b 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -12,6 +12,7 @@
"-Wno-unused-argument",
"-Wno-unused-function",
"-Wno-nullability-completeness",
+ "-Wno-reorder-init-list",
"-Os",
"-fno-finite-loops",
"-DANDROID_DEBUGGABLE=0",
@@ -188,6 +189,7 @@
cc_library_static {
name: "libdebuggerd",
defaults: ["debuggerd_defaults"],
+ ramdisk_available: true,
recovery_available: true,
vendor_ramdisk_available: true,
@@ -221,9 +223,6 @@
"libbase",
"libcutils",
],
- runtime_libs: [
- "libdexfile", // libdexfile_support dependency
- ],
whole_static_libs: [
"libasync_safe",
@@ -250,6 +249,19 @@
"libdexfile",
],
},
+ ramdisk: {
+ exclude_static_libs: [
+ "libdexfile_support",
+ ],
+ exclude_runtime_libs: [
+ "libdexfile",
+ ],
+ },
+ android: {
+ runtime_libs: [
+ "libdexfile", // libdexfile_support dependency
+ ],
+ },
},
product_variables: {
diff --git a/debuggerd/crasher/arm/crashglue.S b/debuggerd/crasher/arm/crashglue.S
index e4adf40..3001ca1 100644
--- a/debuggerd/crasher/arm/crashglue.S
+++ b/debuggerd/crasher/arm/crashglue.S
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2006, 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.
+ */
+
.globl crash1
.type crash1, %function
crash1:
@@ -23,10 +39,11 @@
ldr lr, [lr]
b .
.cfi_endproc
+ .size crash1, .-crash1
-.globl crashnostack
-.type crashnostack, %function
-crashnostack:
+.globl crash_no_stack
+.type crash_no_stack, %function
+crash_no_stack:
.cfi_startproc
mov r1, sp
.cfi_def_cfa_register r1
@@ -35,3 +52,4 @@
ldr r0, [r0]
b .
.cfi_endproc
+ .size crash_no_stack, .-crash_no_stack
diff --git a/debuggerd/crasher/arm64/crashglue.S b/debuggerd/crasher/arm64/crashglue.S
index 97c824e..90ba9a1 100644
--- a/debuggerd/crasher/arm64/crashglue.S
+++ b/debuggerd/crasher/arm64/crashglue.S
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2013, 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.
+ */
+
.globl crash1
.type crash1, %function
crash1:
@@ -41,11 +57,12 @@
ldr x30, [x30]
b .
.cfi_endproc
+ .size crash1, .-crash1
-.globl crashnostack
-.type crashnostack, %function
-crashnostack:
+.globl crash_no_stack
+.type crash_no_stack, %function
+crash_no_stack:
.cfi_startproc
mov x1, sp
.cfi_def_cfa_register x1
@@ -54,3 +71,41 @@
ldr x0, [x0]
b .
.cfi_endproc
+ .size crash_no_stack, .-crash_no_stack
+
+
+.globl crash_bti
+.type crash_bti, %function
+crash_bti:
+ .cfi_startproc
+ adr x16, 1f
+ br x16
+1: // Deliberatly not a bti instruction so we crash here.
+ b .
+ .cfi_endproc
+ .size crash_bti, .-crash_bti
+
+
+.globl crash_pac
+.type crash_pac, %function
+crash_pac:
+ .cfi_startproc
+ paciasp
+ // Since sp is a pac input, this ensures a mismatch.
+ sub sp, sp, #16
+ autiasp
+ b .
+ .cfi_endproc
+ .size crash_pac, .-crash_pac
+
+// Set the PAC and BTI bits for this object file.
+.section .note.gnu.property, "a"
+.balign 8
+.long 4
+.long 0x10
+.long 0x5
+.asciz "GNU"
+.long 0xc0000000
+.long 4
+.long 0x3
+.long 0
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 12ba502..3b52776 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -19,6 +19,7 @@
#include <assert.h>
#include <dirent.h>
#include <errno.h>
+#include <error.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
@@ -29,6 +30,9 @@
#include <sys/prctl.h>
#include <unistd.h>
+#include <android-base/file.h>
+#include <android-base/strings.h>
+
// We test both kinds of logging.
#include <android-base/logging.h>
#include <log/log.h>
@@ -59,8 +63,10 @@
// Avoid name mangling so that stacks are more readable.
extern "C" {
-void crash1(void);
-void crashnostack(void);
+void crash1();
+void crash_no_stack();
+void crash_bti();
+void crash_pac();
int do_action(const char* arg);
@@ -196,13 +202,6 @@
fprintf(stderr, " fdsan_file close a file descriptor that's owned by a FILE*\n");
fprintf(stderr, " fdsan_dir close a file descriptor that's owned by a DIR*\n");
fprintf(stderr, " seccomp fail a seccomp check\n");
-#if defined(__arm__)
- fprintf(stderr, " kuser_helper_version call kuser_helper_version\n");
- fprintf(stderr, " kuser_get_tls call kuser_get_tls\n");
- fprintf(stderr, " kuser_cmpxchg call kuser_cmpxchg\n");
- fprintf(stderr, " kuser_memory_barrier call kuser_memory_barrier\n");
- fprintf(stderr, " kuser_cmpxchg64 call kuser_cmpxchg64\n");
-#endif
fprintf(stderr, " xom read execute-only memory\n");
fprintf(stderr, "\n");
fprintf(stderr, " LOG_ALWAYS_FATAL call liblog LOG_ALWAYS_FATAL\n");
@@ -223,6 +222,20 @@
fprintf(stderr, "\n");
fprintf(stderr, " no_new_privs set PR_SET_NO_NEW_PRIVS and then abort\n");
fprintf(stderr, "\n");
+#if defined(__arm__)
+ fprintf(stderr, "Also, since this is an arm32 binary:\n");
+ fprintf(stderr, " kuser_helper_version call kuser_helper_version\n");
+ fprintf(stderr, " kuser_get_tls call kuser_get_tls\n");
+ fprintf(stderr, " kuser_cmpxchg call kuser_cmpxchg\n");
+ fprintf(stderr, " kuser_memory_barrier call kuser_memory_barrier\n");
+ fprintf(stderr, " kuser_cmpxchg64 call kuser_cmpxchg64\n");
+#endif
+#if defined(__aarch64__)
+ fprintf(stderr, "Also, since this is an arm64 binary:\n");
+ fprintf(stderr, " bti fail a branch target identification (BTI) check\n");
+ fprintf(stderr, " pac fail a pointer authentication (PAC) check\n");
+#endif
+ fprintf(stderr, "\n");
fprintf(stderr, "prefix any of the above with 'thread-' to run on a new thread\n");
fprintf(stderr, "prefix any of the above with 'exhaustfd-' to exhaust\n");
fprintf(stderr, "all available file descriptors before crashing.\n");
@@ -231,6 +244,21 @@
return EXIT_FAILURE;
}
+[[maybe_unused]] static void CheckCpuFeature(const std::string& name) {
+ std::string cpuinfo;
+ if (!android::base::ReadFileToString("/proc/cpuinfo", &cpuinfo)) {
+ error(1, errno, "couldn't read /proc/cpuinfo");
+ }
+ std::vector<std::string> lines = android::base::Split(cpuinfo, "\n");
+ for (std::string_view line : lines) {
+ if (!android::base::ConsumePrefix(&line, "Features\t:")) continue;
+ std::vector<std::string> features = android::base::Split(std::string(line), " ");
+ if (std::find(features.begin(), features.end(), name) == features.end()) {
+ error(1, 0, "/proc/cpuinfo does not report feature '%s'", name.c_str());
+ }
+ }
+}
+
noinline int do_action(const char* arg) {
// Prefixes.
if (!strncmp(arg, "wait-", strlen("wait-"))) {
@@ -256,7 +284,7 @@
} else if (!strcasecmp(arg, "stack-overflow")) {
overflow_stack(nullptr);
} else if (!strcasecmp(arg, "nostack")) {
- crashnostack();
+ crash_no_stack();
} else if (!strcasecmp(arg, "exit")) {
exit(1);
} else if (!strcasecmp(arg, "call-null")) {
@@ -350,6 +378,14 @@
} else if (!strcasecmp(arg, "kuser_cmpxchg64")) {
return __kuser_cmpxchg64(0, 0, 0);
#endif
+#if defined(__aarch64__)
+ } else if (!strcasecmp(arg, "bti")) {
+ CheckCpuFeature("bti");
+ crash_bti();
+ } else if (!strcasecmp(arg, "pac")) {
+ CheckCpuFeature("paca");
+ crash_pac();
+#endif
} else if (!strcasecmp(arg, "no_new_privs")) {
if (prctl(PR_SET_NO_NEW_PRIVS, 1) != 0) {
fprintf(stderr, "prctl(PR_SET_NO_NEW_PRIVS, 1) failed: %s\n", strerror(errno));
diff --git a/debuggerd/crasher/riscv64/crashglue.S b/debuggerd/crasher/riscv64/crashglue.S
index 42f59b3..804a511 100644
--- a/debuggerd/crasher/riscv64/crashglue.S
+++ b/debuggerd/crasher/riscv64/crashglue.S
@@ -1,3 +1,19 @@
+/*
+ * Copyright 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.
+ */
+
.globl crash1
crash1:
.cfi_startproc
@@ -43,10 +59,11 @@
ld t2, 0(zero)
j .
.cfi_endproc
+ .size crash1, .-crash1
-.globl crashnostack
-crashnostack:
+.globl crash_no_stack
+crash_no_stack:
.cfi_startproc
mv t1, sp
.cfi_def_cfa_register t1
@@ -54,3 +71,4 @@
ld t2, 0(zero)
j .
.cfi_endproc
+ .size crash_no_stack, .-crash_no_stack
diff --git a/debuggerd/crasher/x86/crashglue.S b/debuggerd/crasher/x86/crashglue.S
index e8eb3a7..fe7c648 100644
--- a/debuggerd/crasher/x86/crashglue.S
+++ b/debuggerd/crasher/x86/crashglue.S
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2010, 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.
+ */
+
.globl crash1
crash1:
movl $0xa5a50000, %eax
@@ -6,13 +22,15 @@
movl $0, %edx
jmp *%edx
+ .size crash1, .-crash1
-.globl crashnostack
-crashnostack:
+.globl crash_no_stack
+crash_no_stack:
.cfi_startproc
movl %esp, %eax
.cfi_def_cfa_register %eax
movl $0, %esp
movl (%esp), %ebx
.cfi_endproc
+ .size crash_no_stack, .-crash_no_stack
diff --git a/debuggerd/crasher/x86_64/crashglue.S b/debuggerd/crasher/x86_64/crashglue.S
index 8f67214..ae13aa7 100644
--- a/debuggerd/crasher/x86_64/crashglue.S
+++ b/debuggerd/crasher/x86_64/crashglue.S
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2010, 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.
+ */
+
.globl crash1
crash1:
movl $0xa5a50000, %eax
@@ -6,13 +22,15 @@
movl $0, %edx
jmp *%rdx
+ .size crash1, .-crash1
-.globl crashnostack
-crashnostack:
+.globl crash_no_stack
+crash_no_stack:
.cfi_startproc
movq %rsp, %rax
.cfi_def_cfa_register %rax
movq $0, %rsp
movq (%rsp), %rbx
.cfi_endproc
+ .size crash_no_stack, .-crash_no_stack
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 52c1c25..19ff7eb 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -2264,10 +2264,14 @@
ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)");
- // Assumes that the open files section comes after the map section.
- // If that assumption changes, the regex below needs to change.
+ // Verifies that the fault address error message is at the end of the
+ // maps section. To do this, the check below looks for the start of the
+ // open files section or the start of the log file section. It's possible
+ // for either of these sections to be present after the maps section right
+ // now.
+ // If the sections move around, this check might need to be modified.
match_str = android::base::StringPrintf(
- R"(\n--->Fault address falls at %s after any mapped regions\n\nopen files:)",
+ R"(\n--->Fault address falls at %s after any mapped regions\n(---------|\nopen files:))",
format_pointer(crash_uptr).c_str());
ASSERT_MATCH(result, match_str);
}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 1e5365d..01365f2 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -552,8 +552,14 @@
}
debugger_process_info process_info = {};
+ if (g_callbacks.get_process_info) {
+ process_info = g_callbacks.get_process_info();
+ }
uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr);
if (signal_number == BIONIC_SIGNAL_DEBUGGER) {
+ // Applications can set abort messages via android_set_abort_message without
+ // actually aborting; ignore those messages in non-fatal dumps.
+ process_info.abort_msg = nullptr;
if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
// Allow for the abort message to be explicitly specified via the sigqueue value.
// Keep the bottom bit intact for representing whether we want a backtrace or a tombstone.
@@ -562,8 +568,6 @@
info->si_ptr = reinterpret_cast<void*>(si_val & 1);
}
}
- } else if (g_callbacks.get_process_info) {
- process_info = g_callbacks.get_process_info();
}
gwp_asan_callbacks_t gwp_asan_callbacks = {};
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 7b2e068..744bfab 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -493,27 +493,48 @@
}
}
+// This creates a fake log message that indicates an error occurred when
+// reading the log.
+static void add_error_log_msg(Tombstone* tombstone, const std::string&& error_msg) {
+ LogBuffer buffer;
+ buffer.set_name("ERROR");
+
+ LogMessage* log_msg = buffer.add_logs();
+ log_msg->set_timestamp("00-00 00:00:00.000");
+ log_msg->set_pid(0);
+ log_msg->set_tid(0);
+ log_msg->set_priority(ANDROID_LOG_ERROR);
+ log_msg->set_tag("");
+ log_msg->set_message(error_msg);
+
+ *tombstone->add_log_buffers() = std::move(buffer);
+
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "%s", error_msg.c_str());
+}
+
static void dump_log_file(Tombstone* tombstone, const char* logger, pid_t pid) {
logger_list* logger_list = android_logger_list_open(android_name_to_log_id(logger),
ANDROID_LOG_NONBLOCK, kMaxLogMessages, pid);
+ if (logger_list == nullptr) {
+ add_error_log_msg(tombstone, android::base::StringPrintf("Cannot open log file %s", logger));
+ return;
+ }
LogBuffer buffer;
-
while (true) {
log_msg log_entry;
ssize_t actual = android_logger_list_read(logger_list, &log_entry);
-
if (actual < 0) {
if (actual == -EINTR) {
// interrupted by signal, retry
continue;
}
- if (actual == -EAGAIN) {
- // non-blocking EOF; we're done
- break;
- } else {
- break;
+ // Don't consider EAGAIN an error since this is a non-blocking call.
+ if (actual != -EAGAIN) {
+ add_error_log_msg(tombstone, android::base::StringPrintf("reading log %s failed (%s)",
+ logger, strerror(-actual)));
}
+ break;
} else if (actual == 0) {
break;
}
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index 8e6abdf..eed81fc 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -81,6 +81,8 @@
if (!tombstone.command_line().empty()) {
process_name = tombstone.command_line()[0].c_str();
CB(should_log, "Cmdline: %s", android::base::Join(tombstone.command_line(), " ").c_str());
+ } else {
+ CB(should_log, "Cmdline: <unknown>");
}
CB(should_log, "pid: %d, tid: %d, name: %s >>> %s <<<", tombstone.pid(), thread.id(),
thread.name().c_str(), process_name);
diff --git a/debuggerd/proto/Android.bp b/debuggerd/proto/Android.bp
index 73cf573..804f805 100644
--- a/debuggerd/proto/Android.bp
+++ b/debuggerd/proto/Android.bp
@@ -35,6 +35,7 @@
"com.android.runtime",
],
+ ramdisk_available: true,
recovery_available: true,
vendor_ramdisk_available: true,
}
diff --git a/debuggerd/seccomp_policy/crash_dump.arm.policy b/debuggerd/seccomp_policy/crash_dump.arm.policy
index 8fd03c4..a70ab20 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm.policy
@@ -20,6 +20,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+setsockopt: 1
sysinfo: 1
process_vm_readv: 1
tgkill: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.arm64.policy b/debuggerd/seccomp_policy/crash_dump.arm64.policy
index 8241f0e..adf8738 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy
@@ -19,6 +19,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+setsockopt: 1
sysinfo: 1
process_vm_readv: 1
tgkill: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index 0cb8e08..972a575 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -26,6 +26,7 @@
recvmsg: 1
recvfrom: 1
sysinfo: 1
+setsockopt: 1
process_vm_readv: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.riscv64.policy b/debuggerd/seccomp_policy/crash_dump.riscv64.policy
index 281e231..94a5677 100644
--- a/debuggerd/seccomp_policy/crash_dump.riscv64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.riscv64.policy
@@ -19,6 +19,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+setsockopt: 1
sysinfo: 1
process_vm_readv: 1
tgkill: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86.policy b/debuggerd/seccomp_policy/crash_dump.x86.policy
index 8fd03c4..a70ab20 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86.policy
@@ -20,6 +20,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+setsockopt: 1
sysinfo: 1
process_vm_readv: 1
tgkill: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86_64.policy b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
index 281e231..94a5677 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86_64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
@@ -19,6 +19,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+setsockopt: 1
sysinfo: 1
process_vm_readv: 1
tgkill: 1
diff --git a/fastboot/README.md b/fastboot/README.md
index 63db5c3..28e623c 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -165,6 +165,43 @@
using the new bootloader.
+## Flashing Logic
+
+Fastboot binary will follow directions listed out fastboot-info.txt
+build artifact for fastboot flashall && fastboot update comamnds.
+This build artifact will live inside of ANDROID_PRODUCT_OUT &&
+target_files_package && updatepackage.
+
+
+The currently defined commands are:
+
+ flash %s Flash a given partition. Optional arguments include
+ --slot-other, {filename_path}, --apply-vbmeta
+
+ reboot %s Reboot to either bootloader or fastbootd
+
+ update-super Updates the super partition
+
+ if-wipe Conditionally run some other functionality if
+ wipe is specified
+
+ erase %s Erase a given partition (can only be used in conjunction)
+ with if-wipe -> eg. if-wipe erase cache
+
+Flashing Optimization:
+
+ After generating the list of tasks to execute, Fastboot will try and
+ optimize the flashing of the dynamic partitions by constructing an
+ optimized flash super task. Fastboot will explicitly pattern match the
+ following commands and try and concatenate it into this task. (doing so
+ will allow us to avoid the reboot into userspace fastbootd which takes
+ significant time)
+
+ //Optimizable Block
+ reboot fastboot
+ update-super ---> generate optimized flash super task
+ $FOR EACH {dynamic partition}
+ flash {dynamic partition}
## Client Variables
diff --git a/fastboot/constants.h b/fastboot/constants.h
index ad169d1..a803307 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -69,6 +69,7 @@
#define FB_VAR_VARIANT "variant"
#define FB_VAR_OFF_MODE_CHARGE_STATE "off-mode-charge"
#define FB_VAR_BATTERY_VOLTAGE "battery-voltage"
+#define FB_VAR_BATTERY_SOC "battery-soc"
#define FB_VAR_BATTERY_SOC_OK "battery-soc-ok"
#define FB_VAR_SUPER_PARTITION_NAME "super-partition-name"
#define FB_VAR_SNAPSHOT_UPDATE_STATUS "snapshot-update-status"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index e929f42..bd936ae 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -134,6 +134,7 @@
{FB_VAR_IS_FORCE_DEBUGGABLE, {GetIsForceDebuggable, nullptr}},
{FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
{FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
+ {FB_VAR_BATTERY_SOC, {GetBatterySoC, nullptr}},
{FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
{FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
{FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
@@ -639,6 +640,12 @@
return UpdateSuper(device, args[1], wipe);
}
+static bool IsLockedDsu() {
+ std::string active_dsu;
+ android::gsi::GetActiveDsu(&active_dsu);
+ return android::base::EndsWith(active_dsu, ".lock");
+}
+
bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args) {
if (args.size() != 2) {
return device->WriteFail("Invalid arguments");
@@ -653,6 +660,11 @@
return device->WriteStatus(FastbootResult::FAIL, "No GSI is installed");
}
+ if ((args[1] == "wipe" || args[1] == "disable") && GetDeviceLockStatus() && IsLockedDsu()) {
+ // Block commands that modify the states of locked DSU
+ return device->WriteFail("Command not available on locked DSU/devices");
+ }
+
if (args[1] == "wipe") {
if (!android::gsi::UninstallGsi()) {
return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
@@ -661,6 +673,17 @@
if (!android::gsi::DisableGsi()) {
return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
}
+ } else if (args[1] == "status") {
+ std::string active_dsu;
+ if (!android::gsi::IsGsiRunning()) {
+ device->WriteInfo("Not running");
+ } else if (!android::gsi::GetActiveDsu(&active_dsu)) {
+ return device->WriteFail(strerror(errno));
+ } else {
+ device->WriteInfo("Running active DSU: " + active_dsu);
+ }
+ } else {
+ return device->WriteFail("Invalid arguments");
}
return device->WriteStatus(FastbootResult::OKAY, "Success");
}
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index 6b6a982..0dc4e97 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -151,7 +151,8 @@
}
BootControlClient* FastbootDevice::boot1_1() const {
- if (boot_control_hal_->GetVersion() >= android::hal::BootControlVersion::BOOTCTL_V1_1) {
+ if (boot_control_hal_ &&
+ boot_control_hal_->GetVersion() >= android::hal::BootControlVersion::BOOTCTL_V1_1) {
return boot_control_hal_.get();
}
return nullptr;
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index d2a7947..2847e35 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -130,6 +130,21 @@
return true;
}
+bool GetBatterySoCHelper(FastbootDevice* device, int32_t* battery_soc) {
+ using aidl::android::hardware::health::HealthInfo;
+
+ auto health_hal = device->health_hal();
+ if (!health_hal) {
+ return false;
+ }
+
+ HealthInfo health_info;
+ auto res = health_hal->getHealthInfo(&health_info);
+ if (!res.isOk()) return false;
+ *battery_soc = health_info.batteryLevel;
+ return true;
+}
+
bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& /* args */,
std::string* message) {
int32_t battery_voltage = 0;
@@ -185,6 +200,17 @@
return false;
}
+bool GetBatterySoC(FastbootDevice* device, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ int32_t battery_soc = 0;
+ if (GetBatterySoCHelper(device, &battery_soc)) {
+ *message = std::to_string(battery_soc);
+ return true;
+ }
+ *message = "Unable to get battery soc";
+ return false;
+}
+
bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& /* args */,
std::string* message) {
std::string suffix = device->GetCurrentSlot();
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index 3b2d484..9a46786 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -63,6 +63,8 @@
std::string* message);
bool GetBatteryVoltage(FastbootDevice* device, const std::vector<std::string>& args,
std::string* message);
+bool GetBatterySoC(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& args,
std::string* message);
bool GetSuperPartitionName(FastbootDevice* device, const std::vector<std::string>& args,
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 61c7204..56b90b9 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -28,7 +28,6 @@
#include "fastboot.h"
-#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
@@ -44,12 +43,10 @@
#include <unistd.h>
#include <chrono>
-#include <fstream>
#include <functional>
#include <iostream>
#include <memory>
#include <regex>
-#include <sstream>
#include <string>
#include <thread>
#include <utility>
@@ -79,7 +76,6 @@
#include "fastboot_driver_interface.h"
#include "fs.h"
#include "storage.h"
-#include "super_flash_helper.h"
#include "task.h"
#include "tcp.h"
#include "transport.h"
@@ -93,7 +89,6 @@
using android::base::Split;
using android::base::Trim;
using android::base::unique_fd;
-using namespace std::string_literals;
using namespace std::placeholders;
#define FASTBOOT_INFO_VERSION 1
@@ -120,6 +115,9 @@
static std::vector<Image> images = {
// clang-format off
{ "boot", "boot.img", "boot.sig", "boot", false, ImageType::BootCritical },
+ { "bootloader",
+ "bootloader.img", "", "bootloader",
+ true, ImageType::Extra },
{ "init_boot",
"init_boot.img", "init_boot.sig",
"init_boot",
@@ -132,6 +130,7 @@
{ "odm_dlkm", "odm_dlkm.img", "odm_dlkm.sig", "odm_dlkm", true, ImageType::Normal },
{ "product", "product.img", "product.sig", "product", true, ImageType::Normal },
{ "pvmfw", "pvmfw.img", "pvmfw.sig", "pvmfw", true, ImageType::BootCritical },
+ { "radio", "radio.img", "", "radio", true, ImageType::Extra },
{ "recovery", "recovery.img", "recovery.sig", "recovery", true, ImageType::BootCritical },
{ "super", "super.img", "super.sig", "super", true, ImageType::Extra },
{ "system", "system.img", "system.sig", "system", false, ImageType::Normal },
@@ -346,23 +345,21 @@
//
// The returned Transport is a singleton, so multiple calls to this function will return the same
// object, and the caller should not attempt to delete the returned Transport.
-static Transport* open_device(const char* local_serial, bool wait_for_device = true,
- bool announce = true) {
+static std::unique_ptr<Transport> open_device(const char* local_serial, bool wait_for_device = true,
+ bool announce = true) {
const Result<NetworkSerial, FastbootError> network_serial = ParseNetworkSerial(local_serial);
- Transport* transport = nullptr;
+ std::unique_ptr<Transport> transport;
while (true) {
if (network_serial.ok()) {
std::string error;
if (network_serial->protocol == Socket::Protocol::kTcp) {
- transport = tcp::Connect(network_serial->address, network_serial->port, &error)
- .release();
+ transport = tcp::Connect(network_serial->address, network_serial->port, &error);
} else if (network_serial->protocol == Socket::Protocol::kUdp) {
- transport = udp::Connect(network_serial->address, network_serial->port, &error)
- .release();
+ transport = udp::Connect(network_serial->address, network_serial->port, &error);
}
- if (transport == nullptr && announce) {
+ if (!transport && announce) {
LOG(ERROR) << "error: " << error;
}
} else if (network_serial.error().code() ==
@@ -374,12 +371,12 @@
Expect(network_serial);
}
- if (transport != nullptr) {
+ if (transport) {
return transport;
}
if (!wait_for_device) {
- return nullptr;
+ return transport;
}
if (announce) {
@@ -390,9 +387,9 @@
}
}
-static Transport* NetworkDeviceConnected(bool print = false) {
- Transport* transport = nullptr;
- Transport* result = nullptr;
+static std::unique_ptr<Transport> NetworkDeviceConnected(bool print = false) {
+ std::unique_ptr<Transport> transport;
+ std::unique_ptr<Transport> result;
ConnectedDevicesStorage storage;
std::set<std::string> devices;
@@ -405,11 +402,11 @@
transport = open_device(device.c_str(), false, false);
if (print) {
- PrintDevice(device.c_str(), transport == nullptr ? "offline" : "fastboot");
+ PrintDevice(device.c_str(), transport ? "offline" : "fastboot");
}
- if (transport != nullptr) {
- result = transport;
+ if (transport) {
+ result = std::move(transport);
}
}
@@ -427,21 +424,21 @@
//
// The returned Transport is a singleton, so multiple calls to this function will return the same
// object, and the caller should not attempt to delete the returned Transport.
-static Transport* open_device() {
+static std::unique_ptr<Transport> open_device() {
if (serial != nullptr) {
return open_device(serial);
}
bool announce = true;
- Transport* transport = nullptr;
+ std::unique_ptr<Transport> transport;
while (true) {
transport = usb_open(match_fastboot(nullptr));
- if (transport != nullptr) {
+ if (transport) {
return transport;
}
transport = NetworkDeviceConnected();
- if (transport != nullptr) {
+ if (transport) {
return transport;
}
@@ -451,6 +448,8 @@
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
+
+ return transport;
}
static int Connect(int argc, char* argv[]) {
@@ -462,8 +461,7 @@
const char* local_serial = *argv;
Expect(ParseNetworkSerial(local_serial));
- const Transport* transport = open_device(local_serial, false);
- if (transport == nullptr) {
+ if (!open_device(local_serial, false)) {
return 1;
}
@@ -527,6 +525,7 @@
usb_open(list_devices_callback);
NetworkDeviceConnected(/* print */ true);
}
+
void syntax_error(const char* fmt, ...) {
fprintf(stderr, "fastboot: usage: ");
@@ -630,6 +629,9 @@
" --skip-reboot Don't reboot device after flashing.\n"
" --disable-verity Sets disable-verity when flashing vbmeta.\n"
" --disable-verification Sets disable-verification when flashing vbmeta.\n"
+ " --disable-super-optimization\n"
+ " Disables optimizations on flashing super partition.\n"
+ " --disable-fastboot-info Will collects tasks from image list rather than $OUT/fastboot-info.txt.\n"
" --fs-options=OPTION[,OPTION]\n"
" Enable filesystem features. OPTION supports casefold, projid, compress\n"
// TODO: remove --unbuffered?
@@ -1183,9 +1185,10 @@
return partition_size;
}
-static void copy_avb_footer(const std::string& partition, struct fastboot_buffer* buf) {
+static void copy_avb_footer(const ImageSource* source, const std::string& partition,
+ struct fastboot_buffer* buf) {
if (buf->sz < AVB_FOOTER_SIZE || is_logical(partition) ||
- should_flash_in_userspace(partition)) {
+ should_flash_in_userspace(source, partition)) {
return;
}
// If overflows and negative, it should be < buf->sz.
@@ -1242,9 +1245,9 @@
}
}
-static void flash_buf(const std::string& partition, struct fastboot_buffer* buf,
- const bool apply_vbmeta) {
- copy_avb_footer(partition, buf);
+static void flash_buf(const ImageSource* source, const std::string& partition,
+ struct fastboot_buffer* buf, const bool apply_vbmeta) {
+ copy_avb_footer(source, partition, buf);
// Rewrite vbmeta if that's what we're flashing and modification has been requested.
if (g_disable_verity || g_disable_verification) {
@@ -1278,7 +1281,7 @@
return current_slot;
}
-static int get_slot_count() {
+static int get_slot_count(fastboot::IFastBootDriver* fb) {
std::string var;
int count = 0;
if (fb->GetVar("slot-count", &var) != fastboot::SUCCESS ||
@@ -1288,8 +1291,8 @@
return count;
}
-bool supports_AB() {
- return get_slot_count() >= 2;
+bool supports_AB(fastboot::IFastBootDriver* fb) {
+ return get_slot_count(fb) >= 2;
}
// Given a current slot, this returns what the 'other' slot is.
@@ -1301,7 +1304,7 @@
}
static std::string get_other_slot(const std::string& current_slot) {
- return get_other_slot(current_slot, get_slot_count());
+ return get_other_slot(current_slot, get_slot_count(fb));
}
static std::string get_other_slot(int count) {
@@ -1309,7 +1312,7 @@
}
static std::string get_other_slot() {
- return get_other_slot(get_current_slot(), get_slot_count());
+ return get_other_slot(get_current_slot(), get_slot_count(fb));
}
static std::string verify_slot(const std::string& slot_name, bool allow_all) {
@@ -1318,7 +1321,7 @@
if (allow_all) {
return "all";
} else {
- int count = get_slot_count();
+ int count = get_slot_count(fb);
if (count > 0) {
return "a";
} else {
@@ -1327,7 +1330,7 @@
}
}
- int count = get_slot_count();
+ int count = get_slot_count(fb);
if (count == 0) die("Device does not support slots");
if (slot == "other") {
@@ -1400,7 +1403,7 @@
slot.c_str());
}
if (has_slot == "yes") {
- for (int i = 0; i < get_slot_count(); i++) {
+ for (int i = 0; i < get_slot_count(fb); i++) {
do_for_partition(part, std::string(1, (char)(i + 'a')), func, force_slot);
}
} else {
@@ -1411,14 +1414,6 @@
}
}
-bool is_retrofit_device(fastboot::IFastBootDriver* fb) {
- std::string value;
- if (fb->GetVar("super-partition-name", &value) != fastboot::SUCCESS) {
- return false;
- }
- return android::base::StartsWith(value, "system_");
-}
-
// Fetch a partition from the device to a given fd. This is a wrapper over FetchToFd to fetch
// the full image.
static uint64_t fetch_partition(const std::string& partition, borrowed_fd fd,
@@ -1515,13 +1510,13 @@
fb->ResizePartition(pname, std::to_string(buf.image_size));
}
std::string flash_pname = repack_ramdisk(pname, &buf, fp->fb);
- flash_buf(flash_pname, &buf, apply_vbmeta);
+ flash_buf(fp->source.get(), flash_pname, &buf, apply_vbmeta);
}
// Sets slot_override as the active slot. If slot_override is blank,
// set current slot as active instead. This clears slot-unbootable.
static void set_active(const std::string& slot_override) {
- if (!supports_AB()) return;
+ if (!supports_AB(fb)) return;
if (slot_override != "") {
fb->SetActive(slot_override);
@@ -1540,9 +1535,7 @@
void reboot_to_userspace_fastboot() {
fb->RebootTo("fastboot");
-
- auto* old_transport = fb->set_transport(nullptr);
- delete old_transport;
+ fb->set_transport(nullptr);
// Give the current connection time to close.
std::this_thread::sleep_for(std::chrono::seconds(1));
@@ -1671,7 +1664,7 @@
}
for (size_t i = 0; i < tasks->size(); i++) {
if (auto flash_task = tasks->at(i)->AsFlashTask()) {
- if (should_flash_in_userspace(*metadata.get(), flash_task->GetPartitionAndSlot())) {
+ if (FlashTask::IsDynamicParitition(fp->source.get(), flash_task)) {
if (!loc) {
loc = i;
}
@@ -1753,25 +1746,15 @@
}
tasks.emplace_back(std::move(task));
}
- if (auto flash_super_task = OptimizedFlashSuperTask::InitializeFromTasks(fp, tasks)) {
- auto it = tasks.begin();
- for (size_t i = 0; i < tasks.size(); i++) {
- if (auto flash_task = tasks[i]->AsFlashTask()) {
- if (should_flash_in_userspace(flash_task->GetPartitionAndSlot())) {
- break;
- }
- }
- if (auto wipe_task = tasks[i]->AsWipeTask()) {
- break;
- }
- it++;
- }
- tasks.insert(it, std::move(flash_super_task));
+
+ if (auto flash_super_task = OptimizedFlashSuperTask::Initialize(fp, tasks)) {
+ tasks.emplace_back(std::move(flash_super_task));
} else {
if (!AddResizeTasks(fp, &tasks)) {
LOG(WARNING) << "Failed to add resize tasks";
- };
+ }
}
+
return tasks;
}
@@ -1794,6 +1777,7 @@
CancelSnapshotIfNeeded();
tasks_ = CollectTasks();
+
for (auto& task : tasks_) {
task->Run();
}
@@ -1808,7 +1792,19 @@
} else {
tasks = CollectTasksFromImageList();
}
-
+ if (fp_->exclude_dynamic_partitions) {
+ auto is_non_static_flash_task = [&](const auto& task) -> bool {
+ if (auto flash_task = task->AsFlashTask()) {
+ if (!should_flash_in_userspace(fp_->source.get(),
+ flash_task->GetPartitionAndSlot())) {
+ return false;
+ }
+ }
+ return true;
+ };
+ tasks.erase(std::remove_if(tasks.begin(), tasks.end(), is_non_static_flash_task),
+ tasks.end());
+ }
return tasks;
}
@@ -1836,7 +1832,7 @@
fp_->secondary_slot = get_other_slot();
}
if (fp_->secondary_slot == "") {
- if (supports_AB()) {
+ if (supports_AB(fb)) {
fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
}
fp_->skip_secondary = true;
@@ -1866,30 +1862,22 @@
// or in bootloader fastboot.
std::vector<std::unique_ptr<Task>> tasks;
AddFlashTasks(boot_images_, tasks);
- if (auto flash_super_task = OptimizedFlashSuperTask::Initialize(fp_, os_images_)) {
+
+ // Sync the super partition. This will reboot to userspace fastboot if needed.
+ tasks.emplace_back(std::make_unique<UpdateSuperTask>(fp_));
+
+ AddFlashTasks(os_images_, tasks);
+
+ if (auto flash_super_task = OptimizedFlashSuperTask::Initialize(fp_, tasks)) {
tasks.emplace_back(std::move(flash_super_task));
} else {
- // Sync the super partition. This will reboot to userspace fastboot if needed.
- tasks.emplace_back(std::make_unique<UpdateSuperTask>(fp_));
// Resize any logical partition to 0, so each partition is reset to 0
// extents, and will achieve more optimal allocation.
- for (const auto& [image, slot] : os_images_) {
- // Retrofit devices have two super partitions, named super_a and super_b.
- // On these devices, secondary slots must be flashed as physical
- // partitions (otherwise they would not mount on first boot). To enforce
- // this, we delete any logical partitions for the "other" slot.
- if (is_retrofit_device(fp_->fb)) {
- std::string partition_name = image->part_name + "_"s + slot;
- if (image->IsSecondary() && should_flash_in_userspace(partition_name)) {
- fp_->fb->DeletePartition(partition_name);
- }
- tasks.emplace_back(std::make_unique<DeleteTask>(fp_, partition_name));
- }
- tasks.emplace_back(std::make_unique<ResizeTask>(fp_, image->part_name, "0", slot));
+ if (!AddResizeTasks(fp_, &tasks)) {
+ LOG(WARNING) << "Failed to add resize tasks";
}
}
- AddFlashTasks(os_images_, tasks);
return tasks;
}
@@ -1935,9 +1923,7 @@
if (error != 0) {
die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
}
- ZipImageSource zp = ZipImageSource(zip);
- fp->source = &zp;
- fp->wants_wipe = false;
+ fp->source.reset(new ZipImageSource(zip));
FlashAllTool tool(fp);
tool.Flash();
@@ -1958,8 +1944,7 @@
}
static void do_flashall(FlashingPlan* fp) {
- LocalImageSource s = LocalImageSource();
- fp->source = &s;
+ fp->source.reset(new LocalImageSource());
FlashAllTool tool(fp);
tool.Flash();
}
@@ -2075,7 +2060,8 @@
if (!load_buf_fd(std::move(fd), &buf, fp)) {
die("Cannot read image: %s", strerror(errno));
}
- flash_buf(partition, &buf, is_vbmeta_partition(partition));
+
+ flash_buf(fp->source.get(), partition, &buf, is_vbmeta_partition(partition));
return;
failed:
@@ -2089,18 +2075,26 @@
}
}
-bool should_flash_in_userspace(const std::string& partition_name) {
- if (!get_android_product_out()) {
+bool should_flash_in_userspace(const ImageSource* source, const std::string& partition_name) {
+ if (!source) {
+ if (!get_android_product_out()) {
+ return false;
+ }
+ auto path = find_item_given_name("super_empty.img");
+ if (path.empty() || access(path.c_str(), R_OK)) {
+ return false;
+ }
+ auto metadata = android::fs_mgr::ReadFromImageFile(path);
+ if (!metadata) {
+ return false;
+ }
+ return should_flash_in_userspace(*metadata.get(), partition_name);
+ }
+ std::vector<char> contents;
+ if (!source->ReadFile("super_empty.img", &contents)) {
return false;
}
- auto path = find_item_given_name("super_empty.img");
- if (path.empty() || access(path.c_str(), R_OK)) {
- return false;
- }
- auto metadata = android::fs_mgr::ReadFromImageFile(path);
- if (!metadata) {
- return false;
- }
+ auto metadata = android::fs_mgr::ReadFromImageBlob(contents.data(), contents.size());
return should_flash_in_userspace(*metadata.get(), partition_name);
}
@@ -2124,7 +2118,7 @@
if (metadata.block_devices.size() > 1) {
ok = WriteSplitImageFiles(temp_dir.path, metadata, block_size, {}, true);
} else {
- auto image_path = temp_dir.path + "/"s + super_bdev_name + ".img";
+ auto image_path = std::string(temp_dir.path) + "/" + std::string(super_bdev_name) + ".img";
ok = WriteToImageFile(image_path, metadata, block_size, {}, true);
}
if (!ok) {
@@ -2143,7 +2137,7 @@
image_name = partition + ".img";
}
- auto image_path = temp_dir.path + "/"s + image_name;
+ auto image_path = std::string(temp_dir.path) + "/" + image_name;
auto flash = [&](const std::string& partition_name) {
do_flash(partition_name.c_str(), image_path.c_str(), false, fp);
};
@@ -2213,6 +2207,7 @@
{"disable-verification", no_argument, 0, 0},
{"disable-verity", no_argument, 0, 0},
{"disable-super-optimization", no_argument, 0, 0},
+ {"exclude-dynamic-partitions", no_argument, 0, 0},
{"disable-fastboot-info", no_argument, 0, 0},
{"force", no_argument, 0, 0},
{"fs-options", required_argument, 0, 0},
@@ -2254,6 +2249,9 @@
g_disable_verity = true;
} else if (name == "disable-super-optimization") {
fp->should_optimize_flash_super = false;
+ } else if (name == "exclude-dynamic-partitions") {
+ fp->exclude_dynamic_partitions = true;
+ fp->should_optimize_flash_super = false;
} else if (name == "disable-fastboot-info") {
fp->should_use_fastboot_info = false;
} else if (name == "force") {
@@ -2355,8 +2353,8 @@
return show_help();
}
- Transport* transport = open_device();
- if (transport == nullptr) {
+ std::unique_ptr<Transport> transport = open_device();
+ if (!transport) {
return 1;
}
fastboot::DriverCallbacks driver_callbacks = {
@@ -2366,7 +2364,7 @@
.text = TextMessage,
};
- fastboot::FastBootDriver fastboot_driver(transport, driver_callbacks, false);
+ fastboot::FastBootDriver fastboot_driver(std::move(transport), driver_callbacks, false);
fb = &fastboot_driver;
fp->fb = &fastboot_driver;
@@ -2557,14 +2555,12 @@
std::make_unique<ResizeTask>(fp.get(), partition, size, fp->slot_override);
resize_task->Run();
} else if (command == "gsi") {
- std::string arg = next_arg(&args);
- if (arg == "wipe") {
- fb->RawCommand("gsi:wipe", "wiping GSI");
- } else if (arg == "disable") {
- fb->RawCommand("gsi:disable", "disabling GSI");
- } else {
- syntax_error("expected 'wipe' or 'disable'");
+ if (args.empty()) syntax_error("invalid gsi command");
+ std::string cmd("gsi");
+ while (!args.empty()) {
+ cmd += ":" + next_arg(&args);
}
+ fb->RawCommand(cmd, "");
} else if (command == "wipe-super") {
std::string image;
if (args.empty()) {
@@ -2611,9 +2607,6 @@
}
fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
- auto* old_transport = fb->set_transport(nullptr);
- delete old_transport;
-
return 0;
}
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index f6ffb64..2c40890 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -27,11 +27,11 @@
*/
#pragma once
+#include <functional>
+#include <memory>
#include <string>
-#include "fastboot_driver.h"
#include "fastboot_driver_interface.h"
#include "filesystem.h"
-#include "super_flash_helper.h"
#include "task.h"
#include "util.h"
@@ -90,7 +90,7 @@
// If the image uses the default slot, or the user specified "all", then
// the paired string will be empty. If the image requests a specific slot
// (for example, system_other) it is specified instead.
- ImageSource* source;
+ std::unique_ptr<ImageSource> source;
bool wants_wipe = false;
bool skip_reboot = false;
bool wants_set_active = false;
@@ -98,6 +98,7 @@
bool force_flash = false;
bool should_optimize_flash_super = true;
bool should_use_fastboot_info = true;
+ bool exclude_dynamic_partitions = false;
uint64_t sparse_limit = 0;
std::string slot_override;
@@ -148,7 +149,7 @@
};
char* get_android_product_out();
-bool should_flash_in_userspace(const std::string& partition_name);
+bool should_flash_in_userspace(const ImageSource* source, const std::string& partition_name);
bool is_userspace_fastboot();
void do_flash(const char* pname, const char* fname, const bool apply_vbmeta,
const FlashingPlan* fp);
@@ -181,13 +182,12 @@
};
Result<NetworkSerial, FastbootError> ParseNetworkSerial(const std::string& serial);
-bool supports_AB();
std::string GetPartitionName(const ImageEntry& entry, const std::string& current_slot_);
void flash_partition_files(const std::string& partition, const std::vector<SparsePtr>& files);
int64_t get_sparse_limit(int64_t size, const FlashingPlan* fp);
std::vector<SparsePtr> resparse_file(sparse_file* s, int64_t max_size);
-bool is_retrofit_device(fastboot::IFastBootDriver* fb);
+bool supports_AB(fastboot::IFastBootDriver* fb);
bool is_logical(const std::string& partition);
void fb_perform_format(const std::string& partition, int skip_if_not_supported,
const std::string& type_override, const std::string& size_override,
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 9770ab2..e5ef66b 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -58,9 +58,10 @@
namespace fastboot {
/*************************** PUBLIC *******************************/
-FastBootDriver::FastBootDriver(Transport* transport, DriverCallbacks driver_callbacks,
+FastBootDriver::FastBootDriver(std::unique_ptr<Transport> transport,
+ DriverCallbacks driver_callbacks,
bool no_checks)
- : transport_(transport),
+ : transport_(std::move(transport)),
prolog_(std::move(driver_callbacks.prolog)),
epilog_(std::move(driver_callbacks.epilog)),
info_(std::move(driver_callbacks.info)),
@@ -627,9 +628,8 @@
return 0;
}
-Transport* FastBootDriver::set_transport(Transport* transport) {
- std::swap(transport_, transport);
- return transport;
+void FastBootDriver::set_transport(std::unique_ptr<Transport> transport) {
+ transport_ = std::move(transport);
}
} // End namespace fastboot
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 8774ead..49cebd9 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -27,9 +27,9 @@
*/
#pragma once
#include <cstdlib>
-#include <deque>
#include <functional>
#include <limits>
+#include <memory>
#include <string>
#include <vector>
@@ -37,10 +37,8 @@
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <bootimg.h>
-#include <inttypes.h>
#include <sparse/sparse.h>
-#include "constants.h"
#include "fastboot_driver_interface.h"
#include "transport.h"
@@ -63,7 +61,7 @@
static constexpr uint32_t MAX_DOWNLOAD_SIZE = std::numeric_limits<uint32_t>::max();
static constexpr size_t TRANSPORT_CHUNK_SIZE = 1024;
- FastBootDriver(Transport* transport, DriverCallbacks driver_callbacks = {},
+ FastBootDriver(std::unique_ptr<Transport> transport, DriverCallbacks driver_callbacks = {},
bool no_checks = false);
~FastBootDriver();
@@ -124,9 +122,7 @@
std::string Error();
RetCode WaitForDisconnect() override;
- // Note: set_transport will return the previous transport.
- Transport* set_transport(Transport* transport);
- Transport* transport() const { return transport_; }
+ void set_transport(std::unique_ptr<Transport> transport);
RetCode RawCommand(const std::string& cmd, const std::string& message,
std::string* response = nullptr, std::vector<std::string>* info = nullptr,
@@ -143,7 +139,7 @@
std::string ErrnoStr(const std::string& msg);
- Transport* transport_;
+ std::unique_ptr<Transport> transport_;
private:
RetCode SendBuffer(android::base::borrowed_fd fd, size_t size);
diff --git a/fastboot/fastboot_driver_test.cpp b/fastboot/fastboot_driver_test.cpp
index 6f6cf8c..d2033b0 100644
--- a/fastboot/fastboot_driver_test.cpp
+++ b/fastboot/fastboot_driver_test.cpp
@@ -16,6 +16,7 @@
#include "fastboot_driver.h"
+#include <memory>
#include <optional>
#include <gtest/gtest.h>
@@ -30,13 +31,14 @@
};
TEST_F(DriverTest, GetVar) {
- MockTransport transport;
- FastBootDriver driver(&transport);
+ std::unique_ptr<MockTransport> transport_pointer = std::make_unique<MockTransport>();
+ MockTransport* transport = transport_pointer.get();
+ FastBootDriver driver(std::move(transport_pointer));
- EXPECT_CALL(transport, Write(_, _))
+ EXPECT_CALL(*transport, Write(_, _))
.With(AllArgs(RawData("getvar:version")))
.WillOnce(ReturnArg<1>());
- EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY0.4")));
+ EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY0.4")));
std::string output;
ASSERT_EQ(driver.GetVar("version", &output), SUCCESS) << driver.Error();
@@ -44,14 +46,15 @@
}
TEST_F(DriverTest, InfoMessage) {
- MockTransport transport;
- FastBootDriver driver(&transport);
+ std::unique_ptr<MockTransport> transport_pointer = std::make_unique<MockTransport>();
+ MockTransport* transport = transport_pointer.get();
+ FastBootDriver driver(std::move(transport_pointer));
- EXPECT_CALL(transport, Write(_, _))
+ EXPECT_CALL(*transport, Write(_, _))
.With(AllArgs(RawData("oem dmesg")))
.WillOnce(ReturnArg<1>());
- EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("INFOthis is an info line")));
- EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY")));
+ EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("INFOthis is an info line")));
+ EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY")));
std::vector<std::string> info;
ASSERT_EQ(driver.RawCommand("oem dmesg", "", nullptr, &info), SUCCESS) << driver.Error();
@@ -60,28 +63,29 @@
}
TEST_F(DriverTest, TextMessage) {
- MockTransport transport;
std::string text;
+ std::unique_ptr<MockTransport> transport_pointer = std::make_unique<MockTransport>();
+ MockTransport* transport = transport_pointer.get();
DriverCallbacks callbacks{[](const std::string&) {}, [](int) {}, [](const std::string&) {},
[&text](const std::string& extra_text) { text += extra_text; }};
- FastBootDriver driver(&transport, callbacks);
+ FastBootDriver driver(std::move(transport_pointer), callbacks);
- EXPECT_CALL(transport, Write(_, _))
+ EXPECT_CALL(*transport, Write(_, _))
.With(AllArgs(RawData("oem trusty runtest trusty.hwaes.bench")))
.WillOnce(ReturnArg<1>());
- EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("TEXTthis is a text line")));
- EXPECT_CALL(transport, Read(_, _))
+ EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("TEXTthis is a text line")));
+ EXPECT_CALL(*transport, Read(_, _))
.WillOnce(Invoke(
CopyData("TEXT, albeit very long and split over multiple TEXT messages.")));
- EXPECT_CALL(transport, Read(_, _))
+ EXPECT_CALL(*transport, Read(_, _))
.WillOnce(Invoke(CopyData("TEXT Indeed we can do that now with a TEXT message whenever "
"we feel like it.")));
- EXPECT_CALL(transport, Read(_, _))
+ EXPECT_CALL(*transport, Read(_, _))
.WillOnce(Invoke(CopyData("TEXT Isn't that truly super cool?")));
- EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY")));
+ EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY")));
std::vector<std::string> info;
ASSERT_EQ(driver.RawCommand("oem trusty runtest trusty.hwaes.bench", "", nullptr, &info),
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
index 9b5e5f7..94a53ed 100644
--- a/fastboot/fuzzy_fastboot/fixtures.cpp
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -128,7 +128,7 @@
return MatchFastboot(info, device_serial);
};
for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {
- std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+ std::unique_ptr<UsbTransport> usb = usb_open(matcher, USB_TIMEOUT);
if (usb)
transport = std::unique_ptr<TransportSniffer>(
new TransportSniffer(std::move(usb), serial_port));
@@ -143,7 +143,7 @@
} else {
ASSERT_EQ(device_path, cb_scratch); // The path can not change
}
- fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+ fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(std::move(transport), {}, true));
// No error checking since non-A/B devices may not support the command
fb->GetVar("current-slot", &initial_slot);
}
@@ -200,7 +200,7 @@
if (IsFastbootOverTcp()) {
ConnectTcpFastbootDevice();
device_path = cb_scratch;
- fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+ fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(std::move(transport), {}, true));
return;
}
@@ -212,7 +212,7 @@
return MatchFastboot(info, device_serial);
};
while (!transport) {
- std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+ std::unique_ptr<UsbTransport> usb = usb_open(matcher, USB_TIMEOUT);
if (usb) {
transport = std::unique_ptr<TransportSniffer>(
new TransportSniffer(std::move(usb), serial_port));
@@ -220,7 +220,7 @@
std::this_thread::sleep_for(1s);
}
device_path = cb_scratch;
- fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+ fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(std::move(transport), {}, true));
}
void FastBootTest::SetLockState(bool unlock, bool assert_change) {
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index e635937..79f3939 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -50,6 +50,7 @@
#include <gtest/gtest.h>
#include <sparse/sparse.h>
+#include "constants.h"
#include "fastboot_driver.h"
#include "usb.h"
@@ -166,16 +167,15 @@
const auto matcher = [](usb_ifc_info* info) -> int {
return FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
};
- Transport* transport = nullptr;
+ std::unique_ptr<Transport> transport;
for (int i = 0; i < FastBootTest::MAX_USB_TRIES && !transport; i++) {
transport = usb_open(matcher);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
- ASSERT_NE(transport, nullptr) << "Could not find the fastboot device after: "
- << 10 * FastBootTest::MAX_USB_TRIES << "ms";
+ ASSERT_NE(transport.get(), nullptr) << "Could not find the fastboot device after: "
+ << 10 * FastBootTest::MAX_USB_TRIES << "ms";
if (transport) {
transport->Close();
- delete transport;
}
}
@@ -929,8 +929,7 @@
ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
std::string resp;
- EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
- << "Device is unresponsive to getvar command";
+ EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS) << "Device is unresponsive to getvar command";
}
TEST_F(Fuzz, CommandTooLarge) {
@@ -986,11 +985,10 @@
TEST_F(Fuzz, SparseZeroBlkSize) {
// handcrafted malform sparse file with zero as block size
const std::vector<char> buf = {
- '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
- '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x00', '\x00', '\x00', '\x00', '\xc2', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'
- };
+ '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc2', '\xca', '\x00', '\x00', '\x01',
+ '\x00', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1005,13 +1003,10 @@
TEST_F(Fuzz, SparseVeryLargeBlkSize) {
// handcrafted sparse file with block size of ~4GB and divisible 4
const std::vector<char> buf = {
- '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00',
- '\x1c', '\x00', '\x0c', '\x00', '\xF0', '\xFF', '\xFF', '\xFF',
- '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x00', '\x00', '\x00', '\x00', '\xc3', '\xca', '\x00', '\x00',
- '\x01', '\x00', '\x00', '\x00', '\x0c', '\x00', '\x00', '\x00',
- '\x11', '\x22', '\x33', '\x44'
- };
+ '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+ '\x00', '\xF0', '\xFF', '\xFF', '\xFF', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc3', '\xca', '\x00', '\x00', '\x01',
+ '\x00', '\x00', '\x00', '\x0c', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1022,11 +1017,10 @@
TEST_F(Fuzz, SparseTrimmed) {
// handcrafted malform sparse file which is trimmed
const std::vector<char> buf = {
- '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
- '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x00', '\x00', '\x00', '\x80', '\x11', '\x22', '\x33', '\x44'
- };
+ '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+ '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x80', '\x11', '\x22', '\x33', '\x44'};
ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1041,11 +1035,10 @@
TEST_F(Fuzz, SparseInvalidChurk) {
// handcrafted malform sparse file with invalid churk
const std::vector<char> buf = {
- '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
- '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
- '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'
- };
+ '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+ '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01',
+ '\x00', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1895,9 +1888,10 @@
if (!fastboot::FastBootTest::IsFastbootOverTcp()) {
printf("<Waiting for Device>\n");
const auto matcher = [](usb_ifc_info* info) -> int {
- return fastboot::FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
+ return fastboot::FastBootTest::MatchFastboot(info,
+ fastboot::FastBootTest::device_serial);
};
- Transport* transport = nullptr;
+ std::unique_ptr<Transport> transport;
while (!transport) {
transport = usb_open(matcher);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
diff --git a/fastboot/task.cpp b/fastboot/task.cpp
index bf64f0e..25c5a6e 100644
--- a/fastboot/task.cpp
+++ b/fastboot/task.cpp
@@ -15,7 +15,7 @@
//
#include "task.h"
-#include <iostream>
+#include "fastboot_driver.h"
#include <android-base/logging.h>
#include <android-base/parseint.h>
@@ -30,9 +30,19 @@
const bool apply_vbmeta, const FlashingPlan* fp)
: pname_(pname), fname_(fname), slot_(slot), apply_vbmeta_(apply_vbmeta), fp_(fp) {}
+bool FlashTask::IsDynamicParitition(const ImageSource* source, const FlashTask* task) {
+ std::vector<char> contents;
+ if (!source->ReadFile("super_empty.img", &contents)) {
+ return false;
+ }
+ auto metadata = android::fs_mgr::ReadFromImageBlob(contents.data(), contents.size());
+ return should_flash_in_userspace(*metadata.get(), task->GetPartitionAndSlot());
+}
+
void FlashTask::Run() {
auto flash = [&](const std::string& partition) {
- if (should_flash_in_userspace(partition) && !is_userspace_fastboot()) {
+ if (should_flash_in_userspace(fp_->source.get(), partition) && !is_userspace_fastboot() &&
+ !fp_->force_flash) {
die("The partition you are trying to flash is dynamic, and "
"should be flashed via fastbootd. Please run:\n"
"\n"
@@ -46,7 +56,7 @@
do_for_partitions(pname_, slot_, flash, true);
}
-std::string FlashTask::ToString() {
+std::string FlashTask::ToString() const {
std::string apply_vbmeta_string = "";
if (apply_vbmeta_) {
apply_vbmeta_string = " --apply_vbmeta";
@@ -54,7 +64,7 @@
return "flash" + apply_vbmeta_string + " " + pname_ + " " + fname_;
}
-std::string FlashTask::GetPartitionAndSlot() {
+std::string FlashTask::GetPartitionAndSlot() const {
auto slot = slot_;
if (slot.empty()) {
slot = get_current_slot();
@@ -92,7 +102,7 @@
}
}
-std::string RebootTask::ToString() {
+std::string RebootTask::ToString() const {
return "reboot " + reboot_target_;
}
@@ -120,85 +130,43 @@
// Send the data to the device.
flash_partition_files(super_name_, files);
}
-std::string OptimizedFlashSuperTask::ToString() {
+
+std::string OptimizedFlashSuperTask::ToString() const {
return "optimized-flash-super";
}
-std::unique_ptr<OptimizedFlashSuperTask> OptimizedFlashSuperTask::Initialize(
- const FlashingPlan* fp, std::vector<ImageEntry>& os_images) {
- if (!fp->should_optimize_flash_super) {
- LOG(INFO) << "super optimization is disabled";
- return nullptr;
- }
- if (!supports_AB()) {
- LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
- return nullptr;
- }
- if (fp->slot_override == "all") {
- LOG(VERBOSE) << "Cannot optimize flashing super for all slots";
- return nullptr;
- }
-
- // Does this device use dynamic partitions at all?
- unique_fd fd = fp->source->OpenFile("super_empty.img");
-
- if (fd < 0) {
- LOG(VERBOSE) << "could not open super_empty.img";
- return nullptr;
- }
-
- std::string super_name;
- // Try to find whether there is a super partition.
- if (fp->fb->GetVar("super-partition-name", &super_name) != fastboot::SUCCESS) {
- super_name = "super";
- }
-
- uint64_t partition_size;
- std::string partition_size_str;
- if (fp->fb->GetVar("partition-size:" + super_name, &partition_size_str) != fastboot::SUCCESS) {
- LOG(VERBOSE) << "Cannot optimize super flashing: could not determine super partition";
- return nullptr;
- }
- partition_size_str = fb_fix_numeric_var(partition_size_str);
- if (!android::base::ParseUint(partition_size_str, &partition_size)) {
- LOG(VERBOSE) << "Could not parse " << super_name << " size: " << partition_size_str;
- return nullptr;
- }
-
- std::unique_ptr<SuperFlashHelper> helper = std::make_unique<SuperFlashHelper>(*fp->source);
- if (!helper->Open(fd)) {
- return nullptr;
- }
-
- for (const auto& entry : os_images) {
- auto partition = GetPartitionName(entry, fp->current_slot);
- auto image = entry.first;
-
- if (!helper->AddPartition(partition, image->img_name, image->optional_if_no_image)) {
- return nullptr;
+// This looks for a block within tasks that has the following pattern [reboot fastboot,
+// update-super, $LIST_OF_DYNAMIC_FLASH_TASKS] and returns true if this is found.Theoretically
+// this check is just a pattern match and could break if fastboot-info has a bunch of junk commands
+// but all devices should pretty much follow this pattern
+bool OptimizedFlashSuperTask::CanOptimize(const ImageSource* source,
+ const std::vector<std::unique_ptr<Task>>& tasks) {
+ for (size_t i = 0; i < tasks.size(); i++) {
+ auto reboot_task = tasks[i]->AsRebootTask();
+ if (!reboot_task || reboot_task->GetTarget() != "fastboot") {
+ continue;
}
+ // The check for i >= tasks.size() - 2 is because we are peeking two tasks ahead. We need to
+ // check for an update-super && flash {dynamic_partition}
+ if (i >= tasks.size() - 2 || !tasks[i + 1]->AsUpdateSuperTask()) {
+ continue;
+ }
+ auto flash_task = tasks[i + 2]->AsFlashTask();
+ if (!FlashTask::IsDynamicParitition(source, flash_task)) {
+ continue;
+ }
+ return true;
}
-
- auto s = helper->GetSparseLayout();
- if (!s) return nullptr;
-
- // Remove images that we already flashed, just in case we have non-dynamic OS images.
- auto remove_if_callback = [&](const ImageEntry& entry) -> bool {
- return helper->WillFlash(GetPartitionName(entry, fp->current_slot));
- };
- os_images.erase(std::remove_if(os_images.begin(), os_images.end(), remove_if_callback),
- os_images.end());
- return std::make_unique<OptimizedFlashSuperTask>(super_name, std::move(helper), std::move(s),
- partition_size, fp);
+ return false;
}
-std::unique_ptr<OptimizedFlashSuperTask> OptimizedFlashSuperTask::InitializeFromTasks(
+std::unique_ptr<OptimizedFlashSuperTask> OptimizedFlashSuperTask::Initialize(
const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks) {
if (!fp->should_optimize_flash_super) {
LOG(INFO) << "super optimization is disabled";
return nullptr;
}
- if (!supports_AB()) {
+ if (!supports_AB(fp->fb)) {
LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
return nullptr;
}
@@ -206,6 +174,9 @@
LOG(VERBOSE) << "Cannot optimize flashing super for all slots";
return nullptr;
}
+ if (!CanOptimize(fp->source.get(), tasks)) {
+ return nullptr;
+ }
// Does this device use dynamic partitions at all?
unique_fd fd = fp->source->OpenFile("super_empty.img");
@@ -248,17 +219,21 @@
auto s = helper->GetSparseLayout();
if (!s) return nullptr;
- // Remove images that we already flashed, just in case we have non-dynamic OS images.
+
+ // Remove tasks that are concatenated into this optimized task
auto remove_if_callback = [&](const auto& task) -> bool {
if (auto flash_task = task->AsFlashTask()) {
return helper->WillFlash(flash_task->GetPartitionAndSlot());
} else if (auto update_super_task = task->AsUpdateSuperTask()) {
return true;
} else if (auto reboot_task = task->AsRebootTask()) {
- return true;
+ if (reboot_task->GetTarget() == "fastboot") {
+ return true;
+ }
}
return false;
};
+
tasks.erase(std::remove_if(tasks.begin(), tasks.end(), remove_if_callback), tasks.end());
return std::make_unique<OptimizedFlashSuperTask>(super_name, std::move(helper), std::move(s),
@@ -288,7 +263,7 @@
}
fp_->fb->RawCommand(command, "Updating super partition");
}
-std::string UpdateSuperTask::ToString() {
+std::string UpdateSuperTask::ToString() const {
return "update-super";
}
@@ -305,7 +280,7 @@
do_for_partitions(pname_, slot_, resize_partition, false);
}
-std::string ResizeTask::ToString() {
+std::string ResizeTask::ToString() const {
return "resize " + pname_;
}
@@ -315,7 +290,7 @@
fp_->fb->DeletePartition(pname_);
}
-std::string DeleteTask::ToString() {
+std::string DeleteTask::ToString() const {
return "delete " + pname_;
}
@@ -335,6 +310,6 @@
fb_perform_format(pname_, 1, partition_type, "", fp_->fs_options, fp_);
}
-std::string WipeTask::ToString() {
+std::string WipeTask::ToString() const {
return "erase " + pname_;
}
diff --git a/fastboot/task.h b/fastboot/task.h
index f7c8801..a98c874 100644
--- a/fastboot/task.h
+++ b/fastboot/task.h
@@ -15,10 +15,8 @@
//
#pragma once
-#include <sstream>
#include <string>
-#include "fastboot_driver.h"
#include "super_flash_helper.h"
#include "util.h"
@@ -29,18 +27,21 @@
class FlashTask;
class RebootTask;
class UpdateSuperTask;
+class OptimizedFlashSuperTask;
class WipeTask;
-
+class ResizeTask;
class Task {
public:
Task() = default;
virtual void Run() = 0;
- virtual std::string ToString() = 0;
+ virtual std::string ToString() const = 0;
virtual FlashTask* AsFlashTask() { return nullptr; }
virtual RebootTask* AsRebootTask() { return nullptr; }
virtual UpdateSuperTask* AsUpdateSuperTask() { return nullptr; }
+ virtual OptimizedFlashSuperTask* AsOptimizedFlashSuperTask() { return nullptr; }
virtual WipeTask* AsWipeTask() { return nullptr; }
+ virtual ResizeTask* AsResizeTask() { return nullptr; }
virtual ~Task() = default;
};
@@ -51,12 +52,13 @@
const bool apply_vbmeta, const FlashingPlan* fp);
virtual FlashTask* AsFlashTask() override { return this; }
+ static bool IsDynamicParitition(const ImageSource* source, const FlashTask* task);
void Run() override;
- std::string ToString() override;
- std::string GetPartition() { return pname_; }
- std::string GetImageName() { return fname_; }
- std::string GetSlot() { return slot_; }
- std::string GetPartitionAndSlot();
+ std::string ToString() const override;
+ std::string GetPartition() const { return pname_; }
+ std::string GetImageName() const { return fname_; }
+ std::string GetSlot() const { return slot_; }
+ std::string GetPartitionAndSlot() const;
private:
const std::string pname_;
@@ -72,7 +74,8 @@
RebootTask(const FlashingPlan* fp, const std::string& reboot_target);
virtual RebootTask* AsRebootTask() override { return this; }
void Run() override;
- std::string ToString() override;
+ std::string ToString() const override;
+ std::string GetTarget() const { return reboot_target_; };
private:
const std::string reboot_target_ = "";
@@ -83,13 +86,15 @@
public:
OptimizedFlashSuperTask(const std::string& super_name, std::unique_ptr<SuperFlashHelper> helper,
SparsePtr sparse_layout, uint64_t super_size, const FlashingPlan* fp);
- static std::unique_ptr<OptimizedFlashSuperTask> Initialize(const FlashingPlan* fp,
- std::vector<ImageEntry>& os_images);
- static std::unique_ptr<OptimizedFlashSuperTask> InitializeFromTasks(
+ virtual OptimizedFlashSuperTask* AsOptimizedFlashSuperTask() override { return this; }
+
+ static std::unique_ptr<OptimizedFlashSuperTask> Initialize(
const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks);
- using ImageEntry = std::pair<const Image*, std::string>;
+ static bool CanOptimize(const ImageSource* source,
+ const std::vector<std::unique_ptr<Task>>& tasks);
+
void Run() override;
- std::string ToString() override;
+ std::string ToString() const override;
private:
const std::string super_name_;
@@ -105,7 +110,7 @@
virtual UpdateSuperTask* AsUpdateSuperTask() override { return this; }
void Run() override;
- std::string ToString() override;
+ std::string ToString() const override;
private:
const FlashingPlan* fp_;
@@ -116,7 +121,8 @@
ResizeTask(const FlashingPlan* fp, const std::string& pname, const std::string& size,
const std::string& slot);
void Run() override;
- std::string ToString() override;
+ std::string ToString() const override;
+ virtual ResizeTask* AsResizeTask() override { return this; }
private:
const FlashingPlan* fp_;
@@ -129,7 +135,7 @@
public:
DeleteTask(const FlashingPlan* fp, const std::string& pname);
void Run() override;
- std::string ToString() override;
+ std::string ToString() const override;
private:
const FlashingPlan* fp_;
@@ -141,7 +147,7 @@
WipeTask(const FlashingPlan* fp, const std::string& pname);
virtual WipeTask* AsWipeTask() override { return this; }
void Run() override;
- std::string ToString() override;
+ std::string ToString() const override;
private:
const FlashingPlan* fp_;
diff --git a/fastboot/task_test.cpp b/fastboot/task_test.cpp
index 1ba3f4a..9cde1a8 100644
--- a/fastboot/task_test.cpp
+++ b/fastboot/task_test.cpp
@@ -19,11 +19,10 @@
#include "fastboot_driver_mock.h"
#include <gtest/gtest.h>
-#include <fstream>
#include <iostream>
#include <memory>
-#include <unordered_map>
#include "android-base/strings.h"
+#include "gmock/gmock.h"
using android::base::Split;
using testing::_;
@@ -193,8 +192,7 @@
GTEST_SKIP();
}
- LocalImageSource s;
- fp->source = &s;
+ fp->source.reset(new LocalImageSource);
fp->sparse_limit = std::numeric_limits<int64_t>::max();
fastboot::MockFastbootDriver fb;
@@ -235,3 +233,138 @@
<< "size of fastboot-info task list: " << fastboot_info_tasks.size()
<< " size of hardcoded task list: " << hardcoded_tasks.size();
}
+TEST_F(ParseTest, IsDynamicParitiontest) {
+ if (!get_android_product_out()) {
+ GTEST_SKIP();
+ }
+
+ fp->source.reset(new LocalImageSource);
+
+ fastboot::MockFastbootDriver fb;
+ fp->fb = &fb;
+ fp->should_optimize_flash_super = true;
+ fp->should_use_fastboot_info = true;
+
+ std::vector<std::pair<std::string, bool>> test_cases = {
+ {"flash boot", false},
+ {"flash init_boot", false},
+ {"flash --apply-vbmeta vbmeta", false},
+ {"flash product", true},
+ {"flash system", true},
+ {"flash --slot-other system system_other.img", true},
+ };
+ for (auto& test : test_cases) {
+ std::unique_ptr<Task> task =
+ ParseFastbootInfoLine(fp.get(), android::base::Tokenize(test.first, " "));
+ auto flash_task = task->AsFlashTask();
+ ASSERT_FALSE(flash_task == nullptr);
+ ASSERT_EQ(FlashTask::IsDynamicParitition(fp->source.get(), flash_task), test.second);
+ }
+}
+
+TEST_F(ParseTest, CanOptimizeTest) {
+ if (!get_android_product_out()) {
+ GTEST_SKIP();
+ }
+
+ fp->source.reset(new LocalImageSource);
+ fp->sparse_limit = std::numeric_limits<int64_t>::max();
+
+ fastboot::MockFastbootDriver fb;
+ fp->fb = &fb;
+ fp->should_optimize_flash_super = false;
+ fp->should_use_fastboot_info = true;
+
+ std::vector<std::pair<std::vector<std::string>, bool>> patternmatchtest = {
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ true},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ true},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ false},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "update-super", "flash product",
+ "flash system", "flash system_ext", "flash odm", "if-wipe erase userdata"},
+ false},
+ };
+
+ auto remove_if_callback = [&](const auto& task) -> bool { return !!task->AsResizeTask(); };
+
+ for (auto& test : patternmatchtest) {
+ std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp.get(), test.first);
+ tasks.erase(std::remove_if(tasks.begin(), tasks.end(), remove_if_callback), tasks.end());
+ ASSERT_EQ(OptimizedFlashSuperTask::CanOptimize(fp->source.get(), tasks), test.second);
+ }
+}
+
+// Note: this test is exclusively testing that optimized flash super pattern matches a given task
+// list and is able to optimized based on a correct sequence of tasks
+TEST_F(ParseTest, OptimizedFlashSuperPatternMatchTest) {
+ if (!get_android_product_out()) {
+ GTEST_SKIP();
+ }
+
+ fp->source.reset(new LocalImageSource);
+ fp->sparse_limit = std::numeric_limits<int64_t>::max();
+
+ fastboot::MockFastbootDriver fb;
+ fp->fb = &fb;
+ fp->should_optimize_flash_super = true;
+ fp->should_use_fastboot_info = true;
+
+ ON_CALL(fb, GetVar("super-partition-name", _, _))
+ .WillByDefault(testing::Return(fastboot::BAD_ARG));
+
+ ON_CALL(fb, GetVar("slot-count", _, _))
+ .WillByDefault(testing::DoAll(testing::SetArgPointee<1>("2"),
+ testing::Return(fastboot::SUCCESS)));
+
+ ON_CALL(fb, GetVar("partition-size:super", _, _))
+ .WillByDefault(testing::DoAll(testing::SetArgPointee<1>("1000"),
+ testing::Return(fastboot::SUCCESS)));
+
+ std::vector<std::pair<std::vector<std::string>, bool>> patternmatchtest = {
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ true},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ true},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+ "flash product", "flash system", "flash system_ext", "flash odm",
+ "if-wipe erase userdata"},
+ false},
+ {{"flash boot", "flash init_boot", "flash vendor_boot", "update-super", "flash product",
+ "flash system", "flash system_ext", "flash odm", "if-wipe erase userdata"},
+ false},
+ };
+
+ for (auto& test : patternmatchtest) {
+ std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp.get(), test.first);
+ // Check to make sure we have an optimized flash super task && no more dynamic partition
+ // flashing tasks
+ auto&& IsOptimized = [](const FlashingPlan* fp,
+ const std::vector<std::unique_ptr<Task>>& tasks) {
+ bool contains_optimized_task = false;
+ for (auto& task : tasks) {
+ if (auto optimized_task = task->AsOptimizedFlashSuperTask()) {
+ contains_optimized_task = true;
+ }
+ if (auto flash_task = task->AsFlashTask()) {
+ if (FlashTask::IsDynamicParitition(fp->source.get(), flash_task)) {
+ return false;
+ }
+ }
+ }
+ return contains_optimized_task;
+ };
+ ASSERT_EQ(IsOptimized(fp.get(), tasks), test.second);
+ }
+}
diff --git a/fastboot/usb.h b/fastboot/usb.h
index 69581ab..d85cb81 100644
--- a/fastboot/usb.h
+++ b/fastboot/usb.h
@@ -29,6 +29,7 @@
#pragma once
#include <functional>
+#include <memory>
#include "transport.h"
@@ -66,4 +67,4 @@
typedef std::function<int(usb_ifc_info*)> ifc_match_func;
// 0 is non blocking
-UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms = 0);
+std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t timeout_ms = 0);
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index 964488c..37bb304 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -503,9 +503,15 @@
return 0;
}
-UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms) {
+std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t timeout_ms) {
+ std::unique_ptr<UsbTransport> result;
std::unique_ptr<usb_handle> handle = find_usb_device("/sys/bus/usb/devices", callback);
- return handle ? new LinuxUsbTransport(std::move(handle), timeout_ms) : nullptr;
+
+ if (handle) {
+ result = std::make_unique<LinuxUsbTransport>(std::move(handle), timeout_ms);
+ }
+
+ return result;
}
/* Wait for the system to notice the device is gone, so that a subsequent
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 8b852f5..28300b2 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -469,16 +469,20 @@
/*
* Definitions of this file's public functions.
*/
-
-UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms) {
+std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t timeout_ms) {
+ std::unique_ptr<UsbTransport> result;
std::unique_ptr<usb_handle> handle;
if (init_usb(callback, &handle) < 0) {
/* Something went wrong initializing USB. */
- return nullptr;
+ return result;
}
- return new OsxUsbTransport(std::move(handle), timeout_ms);
+ if (handle) {
+ result = std::make_unique<OsxUsbTransport>(std::move(handle), timeout_ms);
+ }
+
+ return result;
}
OsxUsbTransport::~OsxUsbTransport() {
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index 67bf8a3..56a6e7d 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -381,7 +381,13 @@
return handle;
}
-UsbTransport* usb_open(ifc_match_func callback, uint32_t) {
+std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t) {
+ std::unique_ptr<UsbTransport> result;
std::unique_ptr<usb_handle> handle = find_usb_device(callback);
- return handle ? new WindowsUsbTransport(std::move(handle)) : nullptr;
+
+ if (handle) {
+ result = std::make_unique<WindowsUsbTransport>(std::move(handle));
+ }
+
+ return result;
}
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 15600fd..87db98b 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -49,7 +49,6 @@
sanitize: {
misc_undefined: ["integer"],
},
- local_include_dirs: ["include/"],
cflags: [
"-Wall",
"-Werror",
@@ -60,7 +59,7 @@
name: "libfs_mgr_defaults",
defaults: ["fs_mgr_defaults"],
export_include_dirs: ["include"],
- include_dirs: ["system/vold"],
+ local_include_dirs: ["include/"],
cflags: [
"-D_FILE_OFFSET_BITS=64",
],
@@ -90,8 +89,6 @@
static_libs: [
"libavb",
"libfs_avb",
- "libfstab",
- "libdm",
"libgsi",
],
export_static_lib_headers: [
@@ -175,43 +172,22 @@
}
cc_library_static {
- // Do not ever make this a shared library as long as it is vendor_available.
- // It does not have a stable interface.
- name: "libfstab",
- vendor_available: true,
+ name: "libfs_mgr_file_wait",
+ defaults: ["fs_mgr_defaults"],
+ export_include_dirs: ["include"],
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ ],
+ srcs: [
+ "file_wait.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+ host_supported: true,
ramdisk_available: true,
vendor_ramdisk_available: true,
recovery_available: true,
- apex_available: [
- "//apex_available:anyapex",
- "//apex_available:platform",
- ],
- host_supported: true,
- defaults: ["fs_mgr_defaults"],
- srcs: [
- "fs_mgr_fstab.cpp",
- "fs_mgr_boot_config.cpp",
- "fs_mgr_slotselect.cpp",
- ],
- target: {
- darwin: {
- enabled: false,
- },
- vendor: {
- cflags: [
- // Skipping entries in fstab should only be done in a system
- // process as the config file is in /system_ext.
- // Remove the op from the vendor variant.
- "-DNO_SKIP_MOUNT",
- ],
- },
- },
- export_include_dirs: ["include_fstab"],
- header_libs: [
- "libbase_headers",
- "libgsi_headers",
- ],
- min_sdk_version: "31",
}
cc_binary {
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index d357e45..324f50a 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -16,7 +16,8 @@
"name": "fiemap_writer_test"
},
{
- "name": "fs_mgr_vendor_overlay_test"
+ "name": "fs_mgr_vendor_overlay_test",
+ "keywords": ["internal"]
},
{
"name": "vts_libsnapshot_test"
@@ -28,22 +29,28 @@
//{"name": "vabc_legacy_tests"},
{
"name": "cow_api_test"
+ },
+ {
+ "name": "snapuserd_test"
}
],
"kernel-presubmit": [
{
- "name": "vts_libdm_test"
+ "name": "libdm_test"
},
{
- "name": "vts_core_liblp_test"
+ "name": "liblp_test"
},
{
"name": "vts_libsnapshot_test"
},
{
"name": "vab_legacy_tests"
- }
+ },
// TODO: b/279009697
//{"name": "vabc_legacy_tests"}
+ {
+ "name": "snapuserd_test"
+ }
]
}
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index e568a9b..d55f8d3 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -2227,8 +2227,8 @@
}
bool fs_mgr_mount_overlayfs_fstab_entry(const FstabEntry& entry) {
- auto overlayfs_valid_result = fs_mgr_overlayfs_valid();
- if (overlayfs_valid_result == OverlayfsValidResult::kNotSupported) {
+ const auto overlayfs_check_result = android::fs_mgr::CheckOverlayfs();
+ if (!overlayfs_check_result.supported) {
LERROR << __FUNCTION__ << "(): kernel does not support overlayfs";
return false;
}
@@ -2280,10 +2280,7 @@
}
}
- auto options = "lowerdir=" + lowerdir;
- if (overlayfs_valid_result == OverlayfsValidResult::kOverrideCredsRequired) {
- options += ",override_creds=off";
- }
+ const auto options = "lowerdir=" + lowerdir + overlayfs_check_result.mount_flags;
// Use "overlay-" + entry.blk_device as the mount() source, so that adb-remout-test don't
// confuse this with adb remount overlay, whose device name is "overlay".
@@ -2339,30 +2336,34 @@
return context;
}
-OverlayfsValidResult fs_mgr_overlayfs_valid() {
- // Overlayfs available in the kernel, and patched for override_creds?
- if (access("/sys/module/overlay/parameters/override_creds", F_OK) == 0) {
- return OverlayfsValidResult::kOverrideCredsRequired;
- }
+namespace android {
+namespace fs_mgr {
+
+OverlayfsCheckResult CheckOverlayfs() {
if (!fs_mgr_filesystem_available("overlay")) {
- return OverlayfsValidResult::kNotSupported;
+ return {.supported = false};
}
struct utsname uts;
if (uname(&uts) == -1) {
- return OverlayfsValidResult::kNotSupported;
+ return {.supported = false};
}
int major, minor;
if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
- return OverlayfsValidResult::kNotSupported;
+ return {.supported = false};
}
- if (major < 4) {
- return OverlayfsValidResult::kOk;
+ // Overlayfs available in the kernel, and patched for override_creds?
+ if (access("/sys/module/overlay/parameters/override_creds", F_OK) == 0) {
+ auto mount_flags = ",override_creds=off"s;
+ if (major > 5 || (major == 5 && minor >= 15)) {
+ mount_flags += ",userxattr"s;
+ }
+ return {.supported = true, .mount_flags = mount_flags};
}
- if (major > 4) {
- return OverlayfsValidResult::kNotSupported;
+ if (major < 4 || (major == 4 && minor <= 3)) {
+ return {.supported = true};
}
- if (minor > 3) {
- return OverlayfsValidResult::kNotSupported;
- }
- return OverlayfsValidResult::kOk;
+ return {.supported = false};
}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
deleted file mode 100644
index 75d1e0d..0000000
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2017 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 <algorithm>
-#include <iterator>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/properties.h>
-
-#include "fs_mgr_priv.h"
-
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_cmdline(const std::string& cmdline) {
- static constexpr char quote = '"';
-
- std::vector<std::pair<std::string, std::string>> result;
- size_t base = 0;
- while (true) {
- // skip quoted spans
- auto found = base;
- while (((found = cmdline.find_first_of(" \"", found)) != cmdline.npos) &&
- (cmdline[found] == quote)) {
- // unbalanced quote is ok
- if ((found = cmdline.find(quote, found + 1)) == cmdline.npos) break;
- ++found;
- }
- std::string piece;
- auto source = cmdline.substr(base, found - base);
- std::remove_copy(source.begin(), source.end(),
- std::back_insert_iterator<std::string>(piece), quote);
- auto equal_sign = piece.find('=');
- if (equal_sign == piece.npos) {
- if (!piece.empty()) {
- // no difference between <key> and <key>=
- result.emplace_back(std::move(piece), "");
- }
- } else {
- result.emplace_back(piece.substr(0, equal_sign), piece.substr(equal_sign + 1));
- }
- if (found == cmdline.npos) break;
- base = found + 1;
- }
-
- return result;
-}
-
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_proc_bootconfig(
- const std::string& cmdline) {
- static constexpr char quote = '"';
-
- std::vector<std::pair<std::string, std::string>> result;
- for (auto& line : android::base::Split(cmdline, "\n")) {
- line.erase(std::remove(line.begin(), line.end(), quote), line.end());
- auto equal_sign = line.find('=');
- if (equal_sign == line.npos) {
- if (!line.empty()) {
- // no difference between <key> and <key>=
- result.emplace_back(std::move(line), "");
- }
- } else {
- result.emplace_back(android::base::Trim(line.substr(0, equal_sign)),
- android::base::Trim(line.substr(equal_sign + 1)));
- }
- }
-
- return result;
-}
-
-bool fs_mgr_get_boot_config_from_bootconfig(const std::string& bootconfig,
- const std::string& android_key, std::string* out_val) {
- FS_MGR_CHECK(out_val != nullptr);
-
- const std::string bootconfig_key("androidboot." + android_key);
- for (const auto& [key, value] : fs_mgr_parse_proc_bootconfig(bootconfig)) {
- if (key == bootconfig_key) {
- *out_val = value;
- return true;
- }
- }
-
- *out_val = "";
- return false;
-}
-
-bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& android_key,
- std::string* out_val) {
- FS_MGR_CHECK(out_val != nullptr);
-
- const std::string cmdline_key("androidboot." + android_key);
- for (const auto& [key, value] : fs_mgr_parse_cmdline(cmdline)) {
- if (key == cmdline_key) {
- *out_val = value;
- return true;
- }
- }
-
- *out_val = "";
- return false;
-}
-
-// Tries to get the given boot config value from bootconfig.
-// Returns true if successfully found, false otherwise.
-bool fs_mgr_get_boot_config_from_bootconfig_source(const std::string& key, std::string* out_val) {
- std::string bootconfig;
- if (!android::base::ReadFileToString("/proc/bootconfig", &bootconfig)) return false;
- if (!bootconfig.empty() && bootconfig.back() == '\n') {
- bootconfig.pop_back();
- }
- return fs_mgr_get_boot_config_from_bootconfig(bootconfig, key, out_val);
-}
-
-// Tries to get the given boot config value from kernel cmdline.
-// Returns true if successfully found, false otherwise.
-bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
- std::string cmdline;
- if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) return false;
- if (!cmdline.empty() && cmdline.back() == '\n') {
- cmdline.pop_back();
- }
- return fs_mgr_get_boot_config_from_kernel(cmdline, key, out_val);
-}
-
-// Tries to get the boot config value in device tree, properties and
-// kernel cmdline (in that order). Returns 'true' if successfully
-// found, 'false' otherwise.
-bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
- FS_MGR_CHECK(out_val != nullptr);
-
- // firstly, check the device tree
- if (is_dt_compatible()) {
- std::string file_name = get_android_dt_dir() + "/" + key;
- if (android::base::ReadFileToString(file_name, out_val)) {
- if (!out_val->empty()) {
- out_val->pop_back(); // Trims the trailing '\0' out.
- return true;
- }
- }
- }
-
- // next, check if we have "ro.boot" property already
- *out_val = android::base::GetProperty("ro.boot." + key, "");
- if (!out_val->empty()) {
- return true;
- }
-
- // next, check if we have the property in bootconfig
- if (fs_mgr_get_boot_config_from_bootconfig_source(key, out_val)) {
- return true;
- }
-
- // finally, fallback to kernel cmdline, properties may not be ready yet
- if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
- return true;
- }
-
- return false;
-}
diff --git a/fs_mgr/fs_mgr_overlayfs_control.cpp b/fs_mgr/fs_mgr_overlayfs_control.cpp
index 69a2ac0..2cc0d2d 100644
--- a/fs_mgr/fs_mgr_overlayfs_control.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_control.cpp
@@ -18,17 +18,10 @@
#include <errno.h>
#include <fcntl.h>
#include <linux/fs.h>
-#include <selinux/selinux.h>
-#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/param.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/types.h>
-#include <sys/utsname.h>
-#include <sys/vfs.h>
#include <unistd.h>
#include <algorithm>
@@ -38,13 +31,9 @@
#include <vector>
#include <android-base/file.h>
-#include <android-base/macros.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <ext4_utils/ext4_utils.h>
#include <fs_mgr.h>
-#include <fs_mgr/file_wait.h>
#include <fs_mgr_dm_linear.h>
#include <fs_mgr_overlayfs.h>
#include <fstab/fstab.h>
@@ -71,22 +60,25 @@
constexpr char kDataScratchSizeMbProp[] = "fs_mgr.overlayfs.data_scratch_size_mb";
+constexpr char kPhysicalDevice[] = "/dev/block/by-name/";
+constexpr char kScratchImageMetadata[] = "/metadata/gsi/remount/lp_metadata";
+
+constexpr char kMkF2fs[] = "/system/bin/make_f2fs";
+constexpr char kMkExt4[] = "/system/bin/mke2fs";
+
// Return true if everything is mounted, but before adb is started. Right
// after 'trigger load_persist_props_action' is done.
static bool fs_mgr_boot_completed() {
return android::base::GetBoolProperty("ro.persistent_properties.ready", false);
}
-constexpr auto kPhysicalDevice = "/dev/block/by-name/";
-constexpr char kScratchImageMetadata[] = "/metadata/gsi/remount/lp_metadata";
-
// Note: this is meant only for recovery/first-stage init.
static bool ScratchIsOnData() {
// The scratch partition of DSU is managed by gsid.
if (fs_mgr_is_dsu_running()) {
return false;
}
- return fs_mgr_access(kScratchImageMetadata);
+ return access(kScratchImageMetadata, F_OK) == 0;
}
static bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
@@ -131,7 +123,7 @@
}
std::string fs_mgr_overlayfs_setup_dir(const std::string& dir) {
- auto top = dir + kOverlayTopDir;
+ auto top = dir + "/" + kOverlayTopDir;
AutoSetFsCreateCon createcon(kOverlayfsFileContext);
if (!createcon.Ok()) {
@@ -195,10 +187,6 @@
return SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
}
-static std::string fs_mgr_overlayfs_super_device(uint32_t slot_number) {
- return kPhysicalDevice + fs_mgr_get_super_partition_name(slot_number);
-}
-
static bool fs_mgr_overlayfs_has_logical(const Fstab& fstab) {
for (const auto& entry : fstab) {
if (entry.fs_mgr_flags.logical) {
@@ -258,8 +246,8 @@
}
auto slot_number = fs_mgr_overlayfs_slot_number();
- auto super_device = fs_mgr_overlayfs_super_device(slot_number);
- if (!fs_mgr_rw_access(super_device)) {
+ const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+ if (access(super_device.c_str(), R_OK | W_OK)) {
return OverlayfsTeardownResult::Ok;
}
@@ -290,9 +278,9 @@
bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string& mount_point,
bool* change, bool* should_destroy_scratch = nullptr) {
- const auto top = overlay + kOverlayTopDir;
+ const auto top = overlay + "/" + kOverlayTopDir;
- if (!fs_mgr_access(top)) {
+ if (access(top.c_str(), F_OK)) {
if (should_destroy_scratch) *should_destroy_scratch = true;
return true;
}
@@ -300,7 +288,7 @@
auto cleanup_all = mount_point.empty();
const auto partition_name = android::base::Basename(mount_point);
const auto oldpath = top + (cleanup_all ? "" : ("/" + partition_name));
- const auto newpath = cleanup_all ? overlay + "/." + (kOverlayTopDir + 1) + ".teardown"
+ const auto newpath = cleanup_all ? overlay + "/." + kOverlayTopDir + ".teardown"
: top + "/." + partition_name + ".teardown";
auto ret = fs_mgr_rm_all(newpath);
if (!rename(oldpath.c_str(), newpath.c_str())) {
@@ -346,72 +334,6 @@
return ret;
}
-// Mount kScratchMountPoint
-bool MountScratch(const std::string& device_path, bool readonly = false) {
- if (readonly) {
- if (!fs_mgr_access(device_path)) {
- LOG(ERROR) << "Path does not exist: " << device_path;
- return false;
- }
- } else if (!fs_mgr_rw_access(device_path)) {
- LOG(ERROR) << "Path does not exist or is not readwrite: " << device_path;
- return false;
- }
-
- std::vector<const char*> filesystem_candidates;
- if (fs_mgr_is_f2fs(device_path)) {
- filesystem_candidates = {"f2fs", "ext4"};
- } else if (fs_mgr_is_ext4(device_path)) {
- filesystem_candidates = {"ext4", "f2fs"};
- } else {
- LOG(ERROR) << "Scratch partition is not f2fs or ext4";
- return false;
- }
-
- AutoSetFsCreateCon createcon(kOverlayfsFileContext);
- if (!createcon.Ok()) {
- return false;
- }
- if (mkdir(kScratchMountPoint, 0755) && (errno != EEXIST)) {
- PERROR << "create " << kScratchMountPoint;
- return false;
- }
-
- FstabEntry entry;
- entry.blk_device = device_path;
- entry.mount_point = kScratchMountPoint;
- entry.flags = MS_NOATIME | MS_RDONLY;
- if (!readonly) {
- entry.flags &= ~MS_RDONLY;
- entry.flags |= MS_SYNCHRONOUS;
- entry.fs_options = "nodiscard";
- fs_mgr_set_blk_ro(device_path, false);
- }
- // check_fs requires apex runtime library
- if (fs_mgr_overlayfs_already_mounted("/data", false)) {
- entry.fs_mgr_flags.check = true;
- }
- bool mounted = false;
- for (auto fs_type : filesystem_candidates) {
- entry.fs_type = fs_type;
- if (fs_mgr_do_mount_one(entry) == 0) {
- mounted = true;
- break;
- }
- }
- if (!createcon.Restore()) {
- return false;
- }
- if (!mounted) {
- rmdir(kScratchMountPoint);
- return false;
- }
- return true;
-}
-
-const std::string kMkF2fs("/system/bin/make_f2fs");
-const std::string kMkExt4("/system/bin/mke2fs");
-
// Note: The scratch partition of DSU is managed by gsid, and should be initialized during
// first-stage-mount. Just check if the DM device for DSU scratch partition is created or not.
static std::string GetDsuScratchDevice() {
@@ -456,14 +378,14 @@
// thus do not rely on fsck to correct problems that could creep in.
auto fs_type = ""s;
auto command = ""s;
- if (!access(kMkF2fs.c_str(), X_OK) && fs_mgr_filesystem_available("f2fs")) {
+ if (!access(kMkF2fs, X_OK) && fs_mgr_filesystem_available("f2fs")) {
fs_type = "f2fs";
- command = kMkF2fs + " -w ";
+ command = kMkF2fs + " -w "s;
command += std::to_string(getpagesize());
command += " -f -d1 -l" + android::base::Basename(kScratchMountPoint);
- } else if (!access(kMkExt4.c_str(), X_OK) && fs_mgr_filesystem_available("ext4")) {
+ } else if (!access(kMkExt4, X_OK) && fs_mgr_filesystem_available("ext4")) {
fs_type = "ext4";
- command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M " + kScratchMountPoint;
+ command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M "s + kScratchMountPoint;
} else {
LERROR << "No supported mkfs command or filesystem driver available, supported filesystems "
"are: f2fs, ext4";
@@ -506,7 +428,7 @@
auto partition_create = !*partition_exists;
auto slot_number = fs_mgr_overlayfs_slot_number();
- auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+ const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
auto builder = MetadataBuilder::New(super_device, slot_number);
if (!builder) {
LERROR << "open " << super_device << " metadata";
@@ -646,8 +568,8 @@
static bool CanUseSuperPartition(const Fstab& fstab) {
auto slot_number = fs_mgr_overlayfs_slot_number();
- auto super_device = fs_mgr_overlayfs_super_device(slot_number);
- if (!fs_mgr_rw_access(super_device) || !fs_mgr_overlayfs_has_logical(fstab)) {
+ const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+ if (access(super_device.c_str(), R_OK | W_OK) || !fs_mgr_overlayfs_has_logical(fstab)) {
return false;
}
auto metadata = ReadMetadata(super_device, slot_number);
@@ -697,8 +619,8 @@
// If the partition exists, assume first that it can be mounted.
if (partition_exists) {
if (MountScratch(scratch_device)) {
- if (fs_mgr_access(std::string(kScratchMountPoint) + kOverlayTopDir) ||
- fs_mgr_filesystem_has_space(kScratchMountPoint)) {
+ const auto top = kScratchMountPoint + "/"s + kOverlayTopDir;
+ if (access(top.c_str(), F_OK) == 0 || fs_mgr_filesystem_has_space(kScratchMountPoint)) {
return true;
}
// declare it useless, no overrides and no free space
@@ -717,32 +639,6 @@
return MountScratch(scratch_device);
}
-// NOTE: OverlayfsSetupAllowed() must be "stricter" than OverlayfsTeardownAllowed().
-// Setup is allowed only if teardown is also allowed.
-bool OverlayfsSetupAllowed(bool verbose = false) {
- if (!kAllowOverlayfs) {
- if (verbose) {
- LOG(ERROR) << "Overlayfs remounts can only be used in debuggable builds";
- }
- return false;
- }
- // Check mandatory kernel patches.
- if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
- if (verbose) {
- LOG(ERROR) << "Kernel does not support overlayfs";
- }
- return false;
- }
- // in recovery or fastbootd, not allowed!
- if (fs_mgr_in_recovery()) {
- if (verbose) {
- LOG(ERROR) << "Unsupported overlayfs setup from recovery";
- }
- return false;
- }
- return true;
-}
-
constexpr bool OverlayfsTeardownAllowed() {
// Never allow on non-debuggable build.
return kAllowOverlayfs;
@@ -792,7 +688,7 @@
continue;
}
} else {
- if (GetEntryForMountPoint(&fstab, overlay_mount_point) == nullptr) {
+ if (!fs_mgr_overlayfs_already_mounted(overlay_mount_point, false /* overlay */)) {
continue;
}
}
@@ -844,7 +740,7 @@
if (!info.device.empty()) {
return {std::move(info)};
}
- if (!fs_mgr_in_recovery()) {
+ if (!InRecovery()) {
return {};
}
@@ -867,8 +763,7 @@
}
// Avoid uart spam by first checking for a scratch partition.
- auto metadata_slot = fs_mgr_overlayfs_slot_number();
- auto super_device = fs_mgr_overlayfs_super_device(metadata_slot);
+ const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
auto metadata = ReadCurrentMetadata(super_device);
if (!metadata) {
return {};
@@ -1030,7 +925,7 @@
if (!OverlayfsTeardownAllowed()) {
return;
}
- if (!fs_mgr_in_recovery()) {
+ if (!InRecovery()) {
LERROR << __FUNCTION__ << "(): must be called within recovery.";
return;
}
diff --git a/fs_mgr/fs_mgr_overlayfs_control.h b/fs_mgr/fs_mgr_overlayfs_control.h
index 50e83e8..b175101 100644
--- a/fs_mgr/fs_mgr_overlayfs_control.h
+++ b/fs_mgr/fs_mgr_overlayfs_control.h
@@ -14,8 +14,6 @@
#pragma once
-#include <string>
-
#include <fstab/fstab.h>
// If "mount_point" is non-null, set up exactly one overlay.
diff --git a/fs_mgr/fs_mgr_overlayfs_mount.cpp b/fs_mgr/fs_mgr_overlayfs_mount.cpp
index c057c2b..9f17c06 100644
--- a/fs_mgr/fs_mgr_overlayfs_mount.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_mount.cpp
@@ -14,26 +14,20 @@
* limitations under the License.
*/
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <selinux/selinux.h>
-#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/mount.h>
-#include <sys/param.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/types.h>
-#include <sys/utsname.h>
#include <sys/vfs.h>
#include <unistd.h>
#include <algorithm>
#include <memory>
-#include <optional>
#include <string>
#include <vector>
@@ -45,7 +39,6 @@
#include <ext4_utils/ext4_utils.h>
#include <fs_mgr.h>
#include <fs_mgr/file_wait.h>
-#include <fs_mgr_dm_linear.h>
#include <fs_mgr_overlayfs.h>
#include <fstab/fstab.h>
#include <libdm/dm.h>
@@ -62,39 +55,21 @@
constexpr char kPreferCacheBackingStorageProp[] = "fs_mgr.overlayfs.prefer_cache_backing_storage";
-bool fs_mgr_access(const std::string& path) {
- return access(path.c_str(), F_OK) == 0;
-}
+constexpr char kCacheMountPoint[] = "/cache";
+constexpr char kPhysicalDevice[] = "/dev/block/by-name/";
-const auto kLowerdirOption = "lowerdir=";
-const auto kUpperdirOption = "upperdir=";
-
-bool fs_mgr_in_recovery() {
- // Check the existence of recovery binary instead of using the compile time
- // __ANDROID_RECOVERY__ macro.
- // If BOARD_USES_RECOVERY_AS_BOOT is true, both normal and recovery boot
- // mode would use the same init binary, which would mean during normal boot
- // the '/init' binary is actually a symlink pointing to
- // init_second_stage.recovery, which would be compiled with
- // __ANDROID_RECOVERY__ defined.
- return fs_mgr_access("/system/bin/recovery");
-}
+constexpr char kLowerdirOption[] = "lowerdir=";
+constexpr char kUpperdirOption[] = "upperdir=";
bool fs_mgr_is_dsu_running() {
// Since android::gsi::CanBootIntoGsi() or android::gsi::MarkSystemAsGsi() is
// never called in recovery, the return value of android::gsi::IsGsiRunning()
// is not well-defined. In this case, just return false as being in recovery
// implies not running a DSU system.
- if (fs_mgr_in_recovery()) return false;
+ if (InRecovery()) return false;
return android::gsi::IsGsiRunning();
}
-const auto kCacheMountPoint = "/cache";
-
-static bool IsABDevice() {
- return !android::base::GetProperty("ro.boot.slot_suffix", "").empty();
-}
-
std::vector<const std::string> OverlayMountPoints() {
// Never fallback to legacy cache mount point if within a DSU system,
// because running a DSU system implies the device supports dynamic
@@ -105,7 +80,8 @@
// For non-A/B devices prefer cache backing storage if
// kPreferCacheBackingStorageProp property set.
- if (!IsABDevice() && android::base::GetBoolProperty(kPreferCacheBackingStorageProp, false) &&
+ if (fs_mgr_get_slot_suffix().empty() &&
+ android::base::GetBoolProperty(kPreferCacheBackingStorageProp, false) &&
android::base::GetIntProperty("ro.vendor.api_level", -1) < __ANDROID_API_T__) {
return {kCacheMountPoint, kScratchMountPoint};
}
@@ -118,11 +94,6 @@
return !stat(path.c_str(), &st) && S_ISDIR(st.st_mode);
}
-bool fs_mgr_rw_access(const std::string& path) {
- if (path.empty()) return false;
- return access(path.c_str(), R_OK | W_OK) == 0;
-}
-
// At less than 1% or 8MB of free space return value of false,
// means we will try to wrap with overlayfs.
bool fs_mgr_filesystem_has_space(const std::string& mount_point) {
@@ -141,13 +112,11 @@
(static_cast<uint64_t>(vst.f_bfree) * vst.f_frsize) >= kSizeThreshold;
}
-const auto kPhysicalDevice = "/dev/block/by-name/";
-
static bool fs_mgr_update_blk_device(FstabEntry* entry) {
if (entry->fs_mgr_flags.logical) {
fs_mgr_update_logical_partition(entry);
}
- if (fs_mgr_access(entry->blk_device)) {
+ if (access(entry->blk_device.c_str(), F_OK) == 0) {
return true;
}
if (entry->blk_device != "/dev/root") {
@@ -155,10 +124,10 @@
}
// special case for system-as-root (taimen and others)
- auto blk_device = std::string(kPhysicalDevice) + "system";
- if (!fs_mgr_access(blk_device)) {
+ auto blk_device = kPhysicalDevice + "system"s;
+ if (access(blk_device.c_str(), F_OK)) {
blk_device += fs_mgr_get_slot_suffix();
- if (!fs_mgr_access(blk_device)) {
+ if (access(blk_device.c_str(), F_OK)) {
return false;
}
}
@@ -237,28 +206,17 @@
if (!fs_mgr_is_dir(mount_point)) return "";
const auto base = android::base::Basename(mount_point) + "/";
for (const auto& overlay_mount_point : OverlayMountPoints()) {
- auto dir = overlay_mount_point + kOverlayTopDir + "/" + base;
+ auto dir = overlay_mount_point + "/" + kOverlayTopDir + "/" + base;
auto upper = dir + kUpperName;
if (!fs_mgr_is_dir(upper)) continue;
auto work = dir + kWorkName;
if (!fs_mgr_is_dir(work)) continue;
- if (!fs_mgr_rw_access(work)) continue;
+ if (access(work.c_str(), R_OK | W_OK)) continue;
return dir;
}
return "";
}
-static inline bool KernelSupportsUserXattrs() {
- struct utsname uts;
- uname(&uts);
-
- int major, minor;
- if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
- return false;
- }
- return major > 5 || (major == 5 && minor >= 15);
-}
-
const std::string fs_mgr_mount_point(const std::string& mount_point) {
if ("/"s != mount_point) return mount_point;
return "/system";
@@ -270,13 +228,7 @@
auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
if (candidate.empty()) return "";
auto ret = kLowerdirOption + mount_point + "," + kUpperdirOption + candidate + kUpperName +
- ",workdir=" + candidate + kWorkName;
- if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kOverrideCredsRequired) {
- ret += ",override_creds=off";
- }
- if (KernelSupportsUserXattrs()) {
- ret += ",userxattr";
- }
+ ",workdir=" + candidate + kWorkName + android::fs_mgr::CheckOverlayfs().mount_flags;
for (const auto& flag : android::base::Split(entry.fs_options, ",")) {
if (android::base::StartsWith(flag, "context=")) {
ret += "," + flag;
@@ -527,13 +479,13 @@
}
// Mount kScratchMountPoint
-static bool MountScratch(const std::string& device_path, bool readonly = false) {
+bool MountScratch(const std::string& device_path, bool readonly) {
if (readonly) {
- if (!fs_mgr_access(device_path)) {
+ if (access(device_path.c_str(), F_OK)) {
LOG(ERROR) << "Path does not exist: " << device_path;
return false;
}
- } else if (!fs_mgr_rw_access(device_path)) {
+ } else if (access(device_path.c_str(), R_OK | W_OK)) {
LOG(ERROR) << "Path does not exist or is not readwrite: " << device_path;
return false;
}
@@ -589,9 +541,6 @@
return true;
}
-const std::string kMkF2fs("/system/bin/make_f2fs");
-const std::string kMkExt4("/system/bin/mke2fs");
-
// Note: The scratch partition of DSU is managed by gsid, and should be initialized during
// first-stage-mount. Just check if the DM device for DSU scratch partition is created or not.
static std::string GetDsuScratchDevice() {
@@ -633,7 +582,7 @@
// NOTE: OverlayfsSetupAllowed() must be "stricter" than OverlayfsTeardownAllowed().
// Setup is allowed only if teardown is also allowed.
-bool OverlayfsSetupAllowed(bool verbose = false) {
+bool OverlayfsSetupAllowed(bool verbose) {
if (!kAllowOverlayfs) {
if (verbose) {
LOG(ERROR) << "Overlayfs remounts can only be used in debuggable builds";
@@ -641,14 +590,14 @@
return false;
}
// Check mandatory kernel patches.
- if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
+ if (!android::fs_mgr::CheckOverlayfs().supported) {
if (verbose) {
LOG(ERROR) << "Kernel does not support overlayfs";
}
return false;
}
// in recovery or fastbootd, not allowed!
- if (fs_mgr_in_recovery()) {
+ if (InRecovery()) {
if (verbose) {
LOG(ERROR) << "Unsupported overlayfs setup from recovery";
}
@@ -728,7 +677,7 @@
// if verity is still disabled, i.e. no reboot occurred), and skips calling
// fs_mgr_overlayfs_mount_all().
auto scratch_device = GetBootScratchDevice();
- if (!fs_mgr_rw_access(scratch_device)) {
+ if (access(scratch_device.c_str(), R_OK | W_OK)) {
return;
}
if (!WaitForFile(scratch_device, 10s)) {
@@ -737,7 +686,8 @@
if (!MountScratch(scratch_device, true /* readonly */)) {
return;
}
- auto has_overlayfs_dir = fs_mgr_access(std::string(kScratchMountPoint) + kOverlayTopDir);
+ const auto top = kScratchMountPoint + "/"s + kOverlayTopDir;
+ const bool has_overlayfs_dir = access(top.c_str(), F_OK) == 0;
fs_mgr_overlayfs_umount_scratch();
if (has_overlayfs_dir) {
MountScratch(scratch_device);
@@ -783,15 +733,18 @@
bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only) {
Fstab fstab;
- if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+ if (!ReadFstabFromProcMounts(&fstab)) {
return false;
}
const auto lowerdir = kLowerdirOption + mount_point;
- for (const auto& entry : fstab) {
- if (overlay_only && "overlay" != entry.fs_type && "overlayfs" != entry.fs_type) continue;
- if (mount_point != entry.mount_point) continue;
- if (!overlay_only) return true;
- const auto options = android::base::Split(entry.fs_options, ",");
+ for (const auto& entry : GetEntriesForMountPoint(&fstab, mount_point)) {
+ if (!overlay_only) {
+ return true;
+ }
+ if (entry->fs_type != "overlay" && entry->fs_type != "overlayfs") {
+ continue;
+ }
+ const auto options = android::base::Split(entry->fs_options, ",");
for (const auto& opt : options) {
if (opt == lowerdir) {
return true;
diff --git a/fs_mgr/fs_mgr_overlayfs_mount.h b/fs_mgr/fs_mgr_overlayfs_mount.h
index ae3ea84..f0afac1 100644
--- a/fs_mgr/fs_mgr_overlayfs_mount.h
+++ b/fs_mgr/fs_mgr_overlayfs_mount.h
@@ -20,9 +20,12 @@
#include <fstab/fstab.h>
-bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true);
-bool fs_mgr_wants_overlayfs(android::fs_mgr::FstabEntry* entry);
-android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
+constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
+
+constexpr char kScratchMountPoint[] = "/mnt/scratch";
+constexpr char kOverlayTopDir[] = "overlay";
+constexpr char kUpperName[] = "upper";
+constexpr char kWorkName[] = "work";
#if ALLOW_ADBD_DISABLE_VERITY
constexpr bool kAllowOverlayfs = true;
@@ -45,18 +48,13 @@
bool restored_ = false;
};
-constexpr auto kScratchMountPoint = "/mnt/scratch";
-constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
-
-constexpr auto kUpperName = "upper";
-constexpr auto kWorkName = "work";
-constexpr auto kOverlayTopDir = "/overlay";
-
bool fs_mgr_is_dsu_running();
-bool fs_mgr_in_recovery();
-bool fs_mgr_access(const std::string& path);
-bool fs_mgr_rw_access(const std::string& path);
bool fs_mgr_filesystem_has_space(const std::string& mount_point);
const std::string fs_mgr_mount_point(const std::string& mount_point);
+bool OverlayfsSetupAllowed(bool verbose = false);
+bool MountScratch(const std::string& device_path, bool readonly = false);
bool fs_mgr_overlayfs_umount_scratch();
std::vector<const std::string> OverlayMountPoints();
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true);
+bool fs_mgr_wants_overlayfs(android::fs_mgr::FstabEntry* entry);
+android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 46cdb62..7e4d5e5 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -23,15 +23,7 @@
#include <fs_mgr.h>
#include <fstab/fstab.h>
-#include "fs_mgr_priv_boot_config.h"
-
-/* The CHECK() in logging.h will use program invocation name as the tag.
- * Thus, the log will have prefix "init: " when libfs_mgr is statically
- * linked in the init process. This might be opaque when debugging.
- * Appends "in libfs_mgr" at the end of the abort message to explicitly
- * indicate the check happens in fs_mgr.
- */
-#define FS_MGR_CHECK(x) CHECK(x) << "in libfs_mgr "
+#include "libfstab/fstab_priv.h"
#define FS_MGR_TAG "[libfs_mgr] "
@@ -89,28 +81,25 @@
using namespace std::chrono_literals;
bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);
-bool fs_mgr_update_for_slotselect(android::fs_mgr::Fstab* fstab);
bool fs_mgr_is_device_unlocked();
-const std::string& get_android_dt_dir();
-bool is_dt_compatible();
bool fs_mgr_is_ext4(const std::string& blk_device);
bool fs_mgr_is_f2fs(const std::string& blk_device);
-bool fs_mgr_teardown_verity(android::fs_mgr::FstabEntry* fstab);
-
bool fs_mgr_filesystem_available(const std::string& filesystem);
std::string fs_mgr_get_context(const std::string& mount_point);
-enum class OverlayfsValidResult {
- kNotSupported = 0,
- kOk,
- kOverrideCredsRequired,
-};
-OverlayfsValidResult fs_mgr_overlayfs_valid();
-
namespace android {
namespace fs_mgr {
+
bool UnmapDevice(const std::string& name);
+
+struct OverlayfsCheckResult {
+ bool supported;
+ std::string mount_flags;
+};
+
+OverlayfsCheckResult CheckOverlayfs();
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/fs_mgr_priv_boot_config.h b/fs_mgr/fs_mgr_priv_boot_config.h
deleted file mode 100644
index 6a38401..0000000
--- a/fs_mgr/fs_mgr_priv_boot_config.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#ifndef __CORE_FS_MGR_PRIV_BOOTCONFIG_H
-#define __CORE_FS_MGR_PRIV_BOOTCONFIG_H
-
-#include <sys/cdefs.h>
-#include <string>
-#include <utility>
-#include <vector>
-
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_cmdline(const std::string& cmdline);
-
-bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& key,
- std::string* out_val);
-bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val);
-bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_proc_bootconfig(
- const std::string& bootconfig);
-bool fs_mgr_get_boot_config_from_bootconfig(const std::string& bootconfig, const std::string& key,
- std::string* out_val);
-bool fs_mgr_get_boot_config_from_bootconfig_source(const std::string& key, std::string* out_val);
-
-#endif /* __CORE_FS_MGR_PRIV_BOOTCONFIG_H */
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 4c45828..4b3a5d3 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -128,12 +128,11 @@
}
static android::sp<android::os::IVold> GetVold() {
+ auto sm = android::defaultServiceManager();
while (true) {
- if (auto sm = android::defaultServiceManager()) {
- if (auto binder = sm->getService(android::String16("vold"))) {
- if (auto vold = android::interface_cast<android::os::IVold>(binder)) {
- return vold;
- }
+ if (auto binder = sm->checkService(android::String16("vold"))) {
+ if (auto vold = android::interface_cast<android::os::IVold>(binder)) {
+ return vold;
}
}
std::this_thread::sleep_for(2s);
diff --git a/fs_mgr/fs_mgr_vendor_overlay.cpp b/fs_mgr/fs_mgr_vendor_overlay.cpp
index 6b32b4d..bacfa4b 100644
--- a/fs_mgr/fs_mgr_vendor_overlay.cpp
+++ b/fs_mgr/fs_mgr_vendor_overlay.cpp
@@ -85,10 +85,8 @@
return false;
}
- auto options = kLowerdirOption + source_directory + ":" + vendor_mount_point;
- if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kOverrideCredsRequired) {
- options += ",override_creds=off";
- }
+ const auto options = kLowerdirOption + source_directory + ":" + vendor_mount_point +
+ android::fs_mgr::CheckOverlayfs().mount_flags;
auto report = "__mount(source=overlay,target="s + vendor_mount_point + ",type=overlay," +
options + ")=";
auto ret = mount("overlay", vendor_mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME,
@@ -120,7 +118,7 @@
const auto vendor_overlay_dirs = fs_mgr_get_vendor_overlay_dirs(vndk_version);
if (vendor_overlay_dirs.empty()) return true;
- if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
+ if (!android::fs_mgr::CheckOverlayfs().supported) {
LINFO << "vendor overlay: kernel does not support overlayfs";
return false;
}
diff --git a/fs_mgr/include_fstab b/fs_mgr/include_fstab
new file mode 120000
index 0000000..728737f
--- /dev/null
+++ b/fs_mgr/include_fstab
@@ -0,0 +1 @@
+libfstab/include
\ No newline at end of file
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index ddda648..c8d5756 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -24,6 +24,7 @@
vendor_ramdisk_available: true,
recovery_available: true,
export_include_dirs: ["include"],
+ host_supported: true,
}
filegroup {
diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp
index 41e534a..439aac9 100644
--- a/fs_mgr/libfiemap/binder.cpp
+++ b/fs_mgr/libfiemap/binder.cpp
@@ -77,7 +77,7 @@
static FiemapStatus ToFiemapStatus(const char* func, const binder::Status& status) {
if (!status.isOk()) {
- LOG(ERROR) << func << " binder returned: " << status.toString8().string();
+ LOG(ERROR) << func << " binder returned: " << status.toString8().c_str();
if (status.serviceSpecificErrorCode() != 0) {
return FiemapStatus::FromErrorCode(status.serviceSpecificErrorCode());
} else {
@@ -106,7 +106,7 @@
auto status = manager_->deleteBackingImage(name);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return true;
@@ -122,7 +122,7 @@
auto status = manager_->mapImageDevice(name, timeout_ms_count, &map);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
*path = map.path;
@@ -133,7 +133,7 @@
auto status = manager_->unmapImageDevice(name);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return true;
@@ -144,7 +144,7 @@
auto status = manager_->backingImageExists(name, &retval);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return retval;
@@ -155,7 +155,7 @@
auto status = manager_->isImageMapped(name, &retval);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return retval;
@@ -175,7 +175,7 @@
auto status = manager_->getAllBackingImages(&retval);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
}
return retval;
}
@@ -189,7 +189,7 @@
auto status = manager_->removeAllImages();
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return true;
@@ -199,7 +199,7 @@
auto status = manager_->disableImage(name);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return true;
@@ -209,7 +209,7 @@
auto status = manager_->removeDisabledImages();
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return true;
@@ -219,7 +219,7 @@
auto status = manager_->getMappedImageDevice(name, device);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return !device->empty();
@@ -230,7 +230,7 @@
auto status = manager_->isImageDisabled(name, &retval);
if (!status.isOk()) {
LOG(ERROR) << __PRETTY_FUNCTION__
- << " binder returned: " << status.exceptionMessage().string();
+ << " binder returned: " << status.exceptionMessage().c_str();
return false;
}
return retval;
@@ -249,7 +249,7 @@
auto status = service->openImageService(dir, &manager);
if (!status.isOk() || !manager) {
- LOG(ERROR) << "Could not acquire IImageManager: " << status.exceptionMessage().string();
+ LOG(ERROR) << "Could not acquire IImageManager: " << status.exceptionMessage().c_str();
return nullptr;
}
return std::make_unique<ImageManagerBinder>(std::move(service), std::move(manager));
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index bd97a78..c37329c 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -27,6 +27,7 @@
#include <sys/vfs.h>
#include <unistd.h>
+#include <cstring>
#include <string>
#include <utility>
@@ -518,7 +519,8 @@
ASSERT_EQ(ret, 0);
// mount the file system
- ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint_.c_str(), "f2fs", 0, nullptr), 0);
+ ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint_.c_str(), "f2fs", 0, nullptr), 0)
+ << strerror(errno);
}
void TearDown() override {
diff --git a/fs_mgr/libfs_avb/avb_ops.cpp b/fs_mgr/libfs_avb/avb_ops.cpp
index a119bfc..cc19776 100644
--- a/fs_mgr/libfs_avb/avb_ops.cpp
+++ b/fs_mgr/libfs_avb/avb_ops.cpp
@@ -108,8 +108,8 @@
// Converts a partition name (with ab_suffix) to the corresponding mount point.
// e.g., "system_a" => "/system",
// e.g., "vendor_a" => "/vendor",
-static std::string DeriveMountPoint(const std::string& partition_name) {
- const std::string ab_suffix = fs_mgr_get_slot_suffix();
+static std::string DeriveMountPoint(const std::string& partition_name,
+ const std::string& ab_suffix) {
std::string mount_point(partition_name);
auto found = partition_name.rfind(ab_suffix);
if (found != std::string::npos) {
@@ -119,7 +119,7 @@
return "/" + mount_point;
}
-FsManagerAvbOps::FsManagerAvbOps() {
+FsManagerAvbOps::FsManagerAvbOps(const std::string& slot_suffix) {
// We only need to provide the implementation of read_from_partition()
// operation since that's all what is being used by the avb_slot_verify().
// Other I/O operations are only required in bootloader but not in
@@ -135,6 +135,11 @@
// Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.
avb_ops_.user_data = this;
+
+ slot_suffix_ = slot_suffix;
+ if (slot_suffix_.empty()) {
+ slot_suffix_ = fs_mgr_get_slot_suffix();
+ }
}
// Given a partition name (with ab_suffix), e.g., system_a, returns the corresponding
@@ -149,7 +154,7 @@
return "";
}
- const auto mount_point = DeriveMountPoint(partition_name);
+ const auto mount_point = DeriveMountPoint(partition_name, slot_suffix_);
if (mount_point.empty()) return "";
auto fstab_entry = GetEntryForMountPoint(&fstab_, mount_point);
diff --git a/fs_mgr/libfs_avb/avb_ops.h b/fs_mgr/libfs_avb/avb_ops.h
index 12686a6..709091e 100644
--- a/fs_mgr/libfs_avb/avb_ops.h
+++ b/fs_mgr/libfs_avb/avb_ops.h
@@ -48,7 +48,7 @@
//
class FsManagerAvbOps {
public:
- FsManagerAvbOps();
+ explicit FsManagerAvbOps(const std::string& slot_suffix = {});
static FsManagerAvbOps* GetInstanceFromAvbOps(AvbOps* ops) {
return reinterpret_cast<FsManagerAvbOps*>(ops->user_data);
@@ -66,6 +66,7 @@
std::string GetPartitionPath(const char* partition_name);
AvbOps avb_ops_;
Fstab fstab_;
+ std::string slot_suffix_;
};
} // namespace fs_mgr
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index a288876..fb22423 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -182,6 +182,11 @@
// class AvbHandle
// ---------------
+AvbHandle::AvbHandle() : status_(AvbHandleStatus::kUninitialized) {
+ slot_suffix_ = fs_mgr_get_slot_suffix();
+ other_slot_suffix_ = fs_mgr_get_other_slot_suffix();
+}
+
AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(
const std::string& partition_name, const std::string& ab_suffix,
const std::string& ab_other_suffix, const std::string& expected_public_key_path,
@@ -194,6 +199,9 @@
return nullptr;
}
+ avb_handle->slot_suffix_ = ab_suffix;
+ avb_handle->other_slot_suffix_ = ab_other_suffix;
+
std::string expected_key_blob;
if (!expected_public_key_path.empty()) {
if (access(expected_public_key_path.c_str(), F_OK) != 0) {
@@ -373,9 +381,14 @@
return avb_handle;
}
-AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta() {
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const std::string& slot_suffix) {
// Loads inline vbmeta images, starting from /vbmeta.
- return LoadAndVerifyVbmeta("vbmeta", fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix(),
+ auto suffix = slot_suffix;
+ if (suffix.empty()) {
+ suffix = fs_mgr_get_slot_suffix();
+ }
+ auto other_suffix = android::fs_mgr::OtherSlotSuffix(suffix);
+ return LoadAndVerifyVbmeta("vbmeta", suffix, other_suffix,
{} /* expected_public_key, already checked by bootloader */,
HashAlgorithm::kSHA256,
IsAvbPermissive(), /* allow_verification_error */
@@ -399,7 +412,7 @@
? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
: AVB_SLOT_VERIFY_FLAGS_NONE;
AvbSlotVerifyResult verify_result =
- avb_ops.AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->vbmeta_images_);
+ avb_ops.AvbSlotVerify(avb_handle->slot_suffix_, flags, &avb_handle->vbmeta_images_);
// Only allow the following verify results:
// - AVB_SLOT_VERIFY_RESULT_OK.
@@ -492,7 +505,7 @@
}
if (!LoadAvbHashtreeToEnableVerity(fstab_entry, wait_for_verity_dev, vbmeta_images_,
- fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix())) {
+ slot_suffix_, other_slot_suffix_)) {
return AvbHashtreeResult::kFail;
}
@@ -526,8 +539,8 @@
if (vbmeta_images_.size() < 1) {
return "";
}
- std::string avb_partition_name = DeriveAvbPartitionName(fstab_entry, fs_mgr_get_slot_suffix(),
- fs_mgr_get_other_slot_suffix());
+ std::string avb_partition_name =
+ DeriveAvbPartitionName(fstab_entry, slot_suffix_, other_slot_suffix_);
auto avb_prop_name = "com.android.build." + avb_partition_name + ".security_patch";
return GetAvbPropertyDescriptor(avb_prop_name, vbmeta_images_);
}
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
index 4702e68..924ab24 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
@@ -83,8 +83,8 @@
// is verified and can be trusted.
//
// TODO(bowgotsai): remove Open() and switch to LoadAndVerifyVbmeta().
- static AvbUniquePtr Open(); // loads inline vbmeta, via libavb.
- static AvbUniquePtr LoadAndVerifyVbmeta(); // loads inline vbmeta.
+ static AvbUniquePtr Open(); // loads inline vbmeta, via libavb.
+ static AvbUniquePtr LoadAndVerifyVbmeta(const std::string& slot_suffix = {});
// The caller can specify optional preload_avb_key_blobs for public key matching.
// This is mostly for init to preload AVB keys before chroot into /system.
@@ -137,12 +137,14 @@
AvbHandle& operator=(AvbHandle&&) noexcept = delete; // no move assignment
private:
- AvbHandle() : status_(AvbHandleStatus::kUninitialized) {}
+ AvbHandle();
std::vector<VBMetaData> vbmeta_images_;
VBMetaInfo vbmeta_info_; // A summary info for vbmeta_images_.
AvbHandleStatus status_;
std::string avb_version_;
+ std::string slot_suffix_;
+ std::string other_slot_suffix_;
};
} // namespace fs_mgr
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 2e34920..ee83cda 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -655,10 +655,12 @@
" Partition Name: boot\n"
" Rollback Index Location: 1\n"
" Public key (sha1): cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+ " Flags: 0\n"
" Chain Partition descriptor:\n"
" Partition Name: system\n"
" Rollback Index Location: 2\n"
- " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n",
+ " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n"
+ " Flags: 0\n",
InfoImage("vbmeta.img"));
android::base::unique_fd fd(open(vbmeta_path.value().c_str(), O_RDONLY | O_CLOEXEC));
@@ -876,10 +878,12 @@
" Partition Name: boot\n"
" Rollback Index Location: 1\n"
" Public key (sha1): cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+ " Flags: 0\n"
" Chain Partition descriptor:\n"
" Partition Name: vbmeta_system\n"
" Rollback Index Location: 2\n"
- " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n",
+ " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n"
+ " Flags: 0\n",
InfoImage("vbmeta.img"));
bool fatal_error = false;
@@ -909,7 +913,8 @@
" Chain Partition descriptor:\n"
" Partition Name: system\n"
" Rollback Index Location: 3\n"
- " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n",
+ " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n"
+ " Flags: 0\n",
InfoImage("vbmeta_system.img"));
chained_descriptors = GetChainPartitionInfo(LoadVBMetaData("vbmeta_system.img"), &fatal_error);
diff --git a/fs_mgr/libfs_avb/tests/basic_test.cpp b/fs_mgr/libfs_avb/tests/basic_test.cpp
index 1c47c07..d49affb 100644
--- a/fs_mgr/libfs_avb/tests/basic_test.cpp
+++ b/fs_mgr/libfs_avb/tests/basic_test.cpp
@@ -268,10 +268,12 @@
" Partition Name: boot\n"
" Rollback Index Location: 1\n"
" Public key (sha1): cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+ " Flags: 0\n"
" Chain Partition descriptor:\n"
" Partition Name: system\n"
" Rollback Index Location: 2\n"
- " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n",
+ " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n"
+ " Flags: 0\n",
InfoImage("vbmeta.img"));
}
diff --git a/fs_mgr/libfstab/Android.bp b/fs_mgr/libfstab/Android.bp
new file mode 100644
index 0000000..df0269c
--- /dev/null
+++ b/fs_mgr/libfstab/Android.bp
@@ -0,0 +1,62 @@
+//
+// Copyright (C) 2023 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.
+//
+
+package {
+ default_applicable_licenses: [
+ "Android-Apache-2.0",
+ "system_core_fs_mgr_license",
+ ],
+}
+
+cc_library_static {
+ // Do not ever make this a shared library as long as it is vendor_available.
+ // It does not have a stable interface.
+ name: "libfstab",
+ vendor_available: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
+ recovery_available: true,
+ host_supported: true,
+ defaults: ["fs_mgr_defaults"],
+ export_include_dirs: ["include"],
+ header_libs: [
+ "libbase_headers",
+ "libgsi_headers",
+ ],
+ srcs: [
+ "fstab.cpp",
+ "boot_config.cpp",
+ "slotselect.cpp",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ vendor: {
+ cflags: [
+ // Skipping entries in fstab should only be done in a system
+ // process as the config file is in /system_ext.
+ // Remove the op from the vendor variant.
+ "-DNO_SKIP_MOUNT",
+ ],
+ },
+ },
+ apex_available: [
+ "//apex_available:anyapex",
+ "//apex_available:platform",
+ ],
+ min_sdk_version: "31",
+}
diff --git a/fs_mgr/libfstab/boot_config.cpp b/fs_mgr/libfstab/boot_config.cpp
new file mode 100644
index 0000000..b21495e
--- /dev/null
+++ b/fs_mgr/libfstab/boot_config.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2017 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 <algorithm>
+#include <iterator>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+
+#include "fstab_priv.h"
+#include "logging_macros.h"
+
+namespace android {
+namespace fs_mgr {
+
+const std::string& GetAndroidDtDir() {
+ // Set once and saves time for subsequent calls to this function
+ static const std::string kAndroidDtDir = [] {
+ std::string android_dt_dir;
+ if ((GetBootconfig("androidboot.android_dt_dir", &android_dt_dir) ||
+ GetKernelCmdline("androidboot.android_dt_dir", &android_dt_dir)) &&
+ !android_dt_dir.empty()) {
+ // Ensure the returned path ends with a /
+ if (android_dt_dir.back() != '/') {
+ android_dt_dir.push_back('/');
+ }
+ } else {
+ // Fall back to the standard procfs-based path
+ android_dt_dir = "/proc/device-tree/firmware/android/";
+ }
+ LINFO << "Using Android DT directory " << android_dt_dir;
+ return android_dt_dir;
+ }();
+ return kAndroidDtDir;
+}
+
+void ImportBootconfigFromString(const std::string& bootconfig,
+ const std::function<void(std::string, std::string)>& fn) {
+ for (std::string_view line : android::base::Split(bootconfig, "\n")) {
+ const auto equal_pos = line.find('=');
+ std::string key = android::base::Trim(line.substr(0, equal_pos));
+ if (key.empty()) {
+ continue;
+ }
+ std::string value;
+ if (equal_pos != line.npos) {
+ value = android::base::Trim(line.substr(equal_pos + 1));
+ // If the value is a comma-delimited list, the kernel would insert a space between the
+ // list elements when read from /proc/bootconfig.
+ // BoardConfig.mk:
+ // BOARD_BOOTCONFIG := key=value1,value2,value3
+ // /proc/bootconfig:
+ // key = "value1", "value2", "value3"
+ if (key == "androidboot.boot_device" || key == "androidboot.boot_devices") {
+ // boot_device[s] is a special case where a list element can contain comma and the
+ // caller expects a space-delimited list, so don't remove space here.
+ value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
+ } else {
+ // In order to not break the expectations of existing code, we modify the value to
+ // keep the format consistent with the kernel cmdline by removing quote and space.
+ std::string_view sv(value);
+ android::base::ConsumePrefix(&sv, "\"");
+ android::base::ConsumeSuffix(&sv, "\"");
+ value = android::base::StringReplace(sv, R"(", ")", ",", true);
+ }
+ }
+ // "key" and "key =" means empty value.
+ fn(std::move(key), std::move(value));
+ }
+}
+
+bool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,
+ std::string* out) {
+ bool found = false;
+ ImportBootconfigFromString(bootconfig, [&](std::string config_key, std::string value) {
+ if (!found && config_key == key) {
+ *out = std::move(value);
+ found = true;
+ }
+ });
+ return found;
+}
+
+void ImportBootconfig(const std::function<void(std::string, std::string)>& fn) {
+ std::string bootconfig;
+ android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
+ ImportBootconfigFromString(bootconfig, fn);
+}
+
+bool GetBootconfig(const std::string& key, std::string* out) {
+ std::string bootconfig;
+ android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
+ return GetBootconfigFromString(bootconfig, key, out);
+}
+
+void ImportKernelCmdlineFromString(const std::string& cmdline,
+ const std::function<void(std::string, std::string)>& fn) {
+ static constexpr char quote = '"';
+
+ size_t base = 0;
+ while (true) {
+ // skip quoted spans
+ auto found = base;
+ while (((found = cmdline.find_first_of(" \"", found)) != cmdline.npos) &&
+ (cmdline[found] == quote)) {
+ // unbalanced quote is ok
+ if ((found = cmdline.find(quote, found + 1)) == cmdline.npos) break;
+ ++found;
+ }
+ std::string piece = cmdline.substr(base, found - base);
+ piece.erase(std::remove(piece.begin(), piece.end(), quote), piece.end());
+ auto equal_sign = piece.find('=');
+ if (equal_sign == piece.npos) {
+ if (!piece.empty()) {
+ // no difference between <key> and <key>=
+ fn(std::move(piece), "");
+ }
+ } else {
+ std::string value = piece.substr(equal_sign + 1);
+ piece.resize(equal_sign);
+ fn(std::move(piece), std::move(value));
+ }
+ if (found == cmdline.npos) break;
+ base = found + 1;
+ }
+}
+
+bool GetKernelCmdlineFromString(const std::string& cmdline, const std::string& key,
+ std::string* out) {
+ bool found = false;
+ ImportKernelCmdlineFromString(cmdline, [&](std::string config_key, std::string value) {
+ if (!found && config_key == key) {
+ *out = std::move(value);
+ found = true;
+ }
+ });
+ return found;
+}
+
+void ImportKernelCmdline(const std::function<void(std::string, std::string)>& fn) {
+ std::string cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &cmdline);
+ ImportKernelCmdlineFromString(android::base::Trim(cmdline), fn);
+}
+
+bool GetKernelCmdline(const std::string& key, std::string* out) {
+ std::string cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &cmdline);
+ return GetKernelCmdlineFromString(android::base::Trim(cmdline), key, out);
+}
+
+} // namespace fs_mgr
+} // namespace android
+
+// Tries to get the boot config value in device tree, properties, kernel bootconfig and kernel
+// cmdline (in that order).
+// Returns 'true' if successfully found, 'false' otherwise.
+bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
+ FSTAB_CHECK(out_val != nullptr);
+
+ // firstly, check the device tree
+ if (is_dt_compatible()) {
+ std::string file_name = android::fs_mgr::GetAndroidDtDir() + key;
+ if (android::base::ReadFileToString(file_name, out_val)) {
+ if (!out_val->empty()) {
+ out_val->pop_back(); // Trims the trailing '\0' out.
+ return true;
+ }
+ }
+ }
+
+ // next, check if we have "ro.boot" property already
+ *out_val = android::base::GetProperty("ro.boot." + key, "");
+ if (!out_val->empty()) {
+ return true;
+ }
+
+ // next, check if we have the property in bootconfig
+ const std::string config_key = "androidboot." + key;
+ if (android::fs_mgr::GetBootconfig(config_key, out_val)) {
+ return true;
+ }
+
+ // finally, fallback to kernel cmdline, properties may not be ready yet
+ if (android::fs_mgr::GetKernelCmdline(config_key, out_val)) {
+ return true;
+ }
+
+ return false;
+}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/libfstab/fstab.cpp
similarity index 91%
rename from fs_mgr/fs_mgr_fstab.cpp
rename to fs_mgr/libfstab/fstab.cpp
index c85e831..32460b1 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/libfstab/fstab.cpp
@@ -36,7 +36,8 @@
#include <android-base/strings.h>
#include <libgsi/libgsi.h>
-#include "fs_mgr_priv.h"
+#include "fstab_priv.h"
+#include "logging_macros.h"
using android::base::EndsWith;
using android::base::ParseByteCount;
@@ -50,11 +51,10 @@
namespace fs_mgr {
namespace {
-constexpr char kDefaultAndroidDtDir[] = "/proc/device-tree/firmware/android";
constexpr char kProcMountsPath[] = "/proc/mounts";
struct FlagList {
- const char *name;
+ const char* name;
uint64_t flag;
};
@@ -80,7 +80,7 @@
off64_t CalculateZramSize(int percentage) {
off64_t total;
- total = sysconf(_SC_PHYS_PAGES);
+ total = sysconf(_SC_PHYS_PAGES);
total *= percentage;
total /= 100;
@@ -328,8 +328,7 @@
// some recovery fstabs still contain the FDE options since they didn't do
// anything in recovery mode anyway (except possibly to cause the
// reservation of a crypto footer) and thus never got removed.
- if (entry->fs_mgr_flags.crypt && !entry->fs_mgr_flags.vold_managed &&
- access("/system/bin/recovery", F_OK) != 0) {
+ if (entry->fs_mgr_flags.crypt && !entry->fs_mgr_flags.vold_managed && !InRecovery()) {
LERROR << "FDE is no longer supported; 'encryptable' can only be used for adoptable "
"storage";
return false;
@@ -337,25 +336,14 @@
return true;
}
-std::string InitAndroidDtDir() {
- std::string android_dt_dir;
- // The platform may specify a custom Android DT path in kernel cmdline
- if (!fs_mgr_get_boot_config_from_bootconfig_source("android_dt_dir", &android_dt_dir) &&
- !fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) {
- // Fall back to the standard procfs-based path
- android_dt_dir = kDefaultAndroidDtDir;
- }
- return android_dt_dir;
-}
-
bool IsDtFstabCompatible() {
std::string dt_value;
- std::string file_name = get_android_dt_dir() + "/fstab/compatible";
+ std::string file_name = GetAndroidDtDir() + "fstab/compatible";
if (ReadDtFile(file_name, &dt_value) && dt_value == "android,fstab") {
// If there's no status property or its set to "ok" or "okay", then we use the DT fstab.
std::string status_value;
- std::string status_file_name = get_android_dt_dir() + "/fstab/status";
+ std::string status_file_name = GetAndroidDtDir() + "fstab/status";
return !ReadDtFile(status_file_name, &status_value) || status_value == "ok" ||
status_value == "okay";
}
@@ -368,7 +356,7 @@
return {};
}
- std::string fstabdir_name = get_android_dt_dir() + "/fstab";
+ std::string fstabdir_name = GetAndroidDtDir() + "fstab";
std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
if (!fstabdir) return {};
@@ -401,7 +389,7 @@
std::string mount_point;
file_name =
- android::base::StringPrintf("%s/%s/mnt_point", fstabdir_name.c_str(), dp->d_name);
+ android::base::StringPrintf("%s/%s/mnt_point", fstabdir_name.c_str(), dp->d_name);
if (ReadDtFile(file_name, &value)) {
LINFO << "dt_fstab: Using a specified mount point " << value << " for " << dp->d_name;
mount_point = value;
@@ -417,14 +405,16 @@
}
fstab_entry.push_back(value);
- file_name = android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
+ file_name =
+ android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
if (!ReadDtFile(file_name, &value)) {
LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
return {};
}
fstab_entry.push_back(value);
- file_name = android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
+ file_name =
+ android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
if (!ReadDtFile(file_name, &value)) {
LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
return {};
@@ -520,6 +510,9 @@
// ramdisk's copy of the fstab had to be located in the root directory, but now
// the system/etc directory is supported too and is the preferred location.
std::string GetFstabPath() {
+ if (InRecovery()) {
+ return "/etc/recovery.fstab";
+ }
for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
std::string suffix;
@@ -835,19 +828,11 @@
fstab->clear();
ReadFstabFromDt(fstab, false /* verbose */);
- std::string default_fstab_path;
- // Use different fstab paths for normal boot and recovery boot, respectively
- if ((access("/sbin/recovery", F_OK) == 0) || (access("/system/bin/recovery", F_OK) == 0)) {
- default_fstab_path = "/etc/recovery.fstab";
- } else { // normal boot
- default_fstab_path = GetFstabPath();
- }
-
Fstab default_fstab;
+ const std::string default_fstab_path = GetFstabPath();
if (!default_fstab_path.empty() && ReadFstabFromFile(default_fstab_path, &default_fstab)) {
- for (auto&& entry : default_fstab) {
- fstab->emplace_back(std::move(entry));
- }
+ fstab->insert(fstab->end(), std::make_move_iterator(default_fstab.begin()),
+ std::make_move_iterator(default_fstab.end()));
} else {
LINFO << __FUNCTION__ << "(): failed to find device default fstab";
}
@@ -877,40 +862,33 @@
}
std::set<std::string> GetBootDevices() {
+ std::set<std::string> boot_devices;
// First check bootconfig, then kernel commandline, then the device tree
- std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
std::string value;
- if (fs_mgr_get_boot_config_from_bootconfig_source("boot_devices", &value) ||
- fs_mgr_get_boot_config_from_bootconfig_source("boot_device", &value)) {
- std::set<std::string> boot_devices;
- // remove quotes and split by spaces
- auto boot_device_strings = base::Split(base::StringReplace(value, "\"", "", true), " ");
- for (std::string_view device : boot_device_strings) {
- // trim the trailing comma, keep the rest.
+ if (GetBootconfig("androidboot.boot_devices", &value) ||
+ GetBootconfig("androidboot.boot_device", &value)) {
+ // split by spaces and trim the trailing comma.
+ for (std::string_view device : android::base::Split(value, " ")) {
base::ConsumeSuffix(&device, ",");
boot_devices.emplace(device);
}
return boot_devices;
}
- if (fs_mgr_get_boot_config_from_kernel_cmdline("boot_devices", &value) ||
- ReadDtFile(dt_file_name, &value)) {
- auto boot_devices = Split(value, ",");
- return std::set<std::string>(boot_devices.begin(), boot_devices.end());
+ const std::string dt_file_name = GetAndroidDtDir() + "boot_devices";
+ if (GetKernelCmdline("androidboot.boot_devices", &value) || ReadDtFile(dt_file_name, &value)) {
+ auto boot_devices_list = Split(value, ",");
+ return {std::make_move_iterator(boot_devices_list.begin()),
+ std::make_move_iterator(boot_devices_list.end())};
}
- std::string cmdline;
- if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
- std::set<std::string> boot_devices;
- const std::string cmdline_key = "androidboot.boot_device";
- for (const auto& [key, value] : fs_mgr_parse_cmdline(cmdline)) {
- if (key == cmdline_key) {
- boot_devices.emplace(value);
- }
+ ImportKernelCmdline([&](std::string key, std::string value) {
+ if (key == "androidboot.boot_device") {
+ boot_devices.emplace(std::move(value));
}
- if (!boot_devices.empty()) {
- return boot_devices;
- }
+ });
+ if (!boot_devices.empty()) {
+ return boot_devices;
}
// Fallback to extract boot devices from fstab.
@@ -936,18 +914,22 @@
return base_device + "-verity";
}
+bool InRecovery() {
+ // Check the existence of recovery binary instead of using the compile time
+ // __ANDROID_RECOVERY__ macro.
+ // If BOARD_USES_RECOVERY_AS_BOOT is true, both normal and recovery boot
+ // mode would use the same init binary, which would mean during normal boot
+ // the '/init' binary is actually a symlink pointing to
+ // init_second_stage.recovery, which would be compiled with
+ // __ANDROID_RECOVERY__ defined.
+ return access("/system/bin/recovery", F_OK) == 0 || access("/sbin/recovery", F_OK) == 0;
+}
+
} // namespace fs_mgr
} // namespace android
-// FIXME: The same logic is duplicated in system/core/init/
-const std::string& get_android_dt_dir() {
- // Set once and saves time for subsequent calls to this function
- static const std::string kAndroidDtDir = android::fs_mgr::InitAndroidDtDir();
- return kAndroidDtDir;
-}
-
bool is_dt_compatible() {
- std::string file_name = get_android_dt_dir() + "/compatible";
+ std::string file_name = android::fs_mgr::GetAndroidDtDir() + "compatible";
std::string dt_value;
if (android::fs_mgr::ReadDtFile(file_name, &dt_value)) {
if (dt_value == "android,firmware") {
diff --git a/fs_mgr/libfstab/fstab_priv.h b/fs_mgr/libfstab/fstab_priv.h
new file mode 100644
index 0000000..5105da0
--- /dev/null
+++ b/fs_mgr/libfstab/fstab_priv.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 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 <functional>
+#include <string>
+
+#include <fstab/fstab.h>
+
+// Do not include logging_macros.h here as this header is used by fs_mgr, too.
+
+bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
+
+bool fs_mgr_update_for_slotselect(android::fs_mgr::Fstab* fstab);
+bool is_dt_compatible();
+
+namespace android {
+namespace fs_mgr {
+
+bool InRecovery();
+bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out);
+bool SkipMountWithConfig(const std::string& skip_config, Fstab* fstab, bool verbose);
+std::string GetFstabPath();
+
+void ImportBootconfigFromString(const std::string& bootconfig,
+ const std::function<void(std::string, std::string)>& fn);
+
+bool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,
+ std::string* out);
+
+void ImportKernelCmdlineFromString(const std::string& cmdline,
+ const std::function<void(std::string, std::string)>& fn);
+
+bool GetKernelCmdlineFromString(const std::string& cmdline, const std::string& key,
+ std::string* out);
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/fuzz/Android.bp b/fs_mgr/libfstab/fuzz/Android.bp
similarity index 100%
rename from fs_mgr/fuzz/Android.bp
rename to fs_mgr/libfstab/fuzz/Android.bp
diff --git a/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp b/fs_mgr/libfstab/fuzz/fs_mgr_fstab_fuzzer.cpp
similarity index 97%
rename from fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
rename to fs_mgr/libfstab/fuzz/fs_mgr_fstab_fuzzer.cpp
index b5fdad4..b09b273 100644
--- a/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
+++ b/fs_mgr/libfstab/fuzz/fs_mgr_fstab_fuzzer.cpp
@@ -20,6 +20,8 @@
#include <fstab/fstab.h>
#include <fuzzer/FuzzedDataProvider.h>
+#include "../fstab_priv.h"
+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider fdp(data, size);
diff --git a/fs_mgr/fuzz/fstab.dict b/fs_mgr/libfstab/fuzz/fstab.dict
similarity index 100%
rename from fs_mgr/fuzz/fstab.dict
rename to fs_mgr/libfstab/fuzz/fstab.dict
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/libfstab/include/fstab/fstab.h
similarity index 79%
rename from fs_mgr/include_fstab/fstab/fstab.h
rename to fs_mgr/libfstab/include/fstab/fstab.h
index 9cb1546..09471f0 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/libfstab/include/fstab/fstab.h
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <functional>
#include <set>
#include <string>
#include <vector>
@@ -93,13 +94,6 @@
// Unless explicitly requested, a lookup on mount point should always return the 1st one.
using Fstab = std::vector<FstabEntry>;
-// Exported for testability. Regular users should use ReadFstabFromFile().
-bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out);
-// Exported for testability. Regular users should use ReadDefaultFstab().
-std::string GetFstabPath();
-// Exported for testability.
-bool SkipMountWithConfig(const std::string& skip_config, Fstab* fstab, bool verbose);
-
bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
bool ReadFstabFromProcMounts(Fstab* fstab);
bool ReadFstabFromDt(Fstab* fstab, bool verbose = true);
@@ -131,5 +125,28 @@
// expected name.
std::string GetVerityDeviceName(const FstabEntry& entry);
+// Returns the Android Device Tree directory as specified in the kernel bootconfig or cmdline.
+// If the platform does not configure a custom DT path, returns the standard one (based in procfs).
+const std::string& GetAndroidDtDir();
+
+// Import the kernel bootconfig by calling the callback |fn| with each key-value pair.
+void ImportBootconfig(const std::function<void(std::string, std::string)>& fn);
+
+// Get the kernel bootconfig value for |key|.
+// Returns true if |key| is found in bootconfig.
+// Otherwise returns false and |*out| is not modified.
+bool GetBootconfig(const std::string& key, std::string* out);
+
+// Import the kernel cmdline by calling the callback |fn| with each key-value pair.
+void ImportKernelCmdline(const std::function<void(std::string, std::string)>& fn);
+
+// Get the kernel cmdline value for |key|.
+// Returns true if |key| is found in the kernel cmdline.
+// Otherwise returns false and |*out| is not modified.
+bool GetKernelCmdline(const std::string& key, std::string* out);
+
+// Return the "other" slot for the given slot suffix.
+std::string OtherSlotSuffix(const std::string& suffix);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libfstab/logging_macros.h b/fs_mgr/libfstab/logging_macros.h
new file mode 100644
index 0000000..7ea1b77
--- /dev/null
+++ b/fs_mgr/libfstab/logging_macros.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 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 <android-base/logging.h>
+
+#define FSTAB_TAG "[libfstab] "
+
+/* The CHECK() in logging.h will use program invocation name as the tag.
+ * Thus, the log will have prefix "init: " when libfs_mgr is statically
+ * linked in the init process. This might be opaque when debugging.
+ * Append a library name tag at the end of the abort message to aid debugging.
+ */
+#define FSTAB_CHECK(x) CHECK(x) << "in " << FSTAB_TAG
+
+// Logs a message to kernel
+#define LINFO LOG(INFO) << FSTAB_TAG
+#define LWARNING LOG(WARNING) << FSTAB_TAG
+#define LERROR LOG(ERROR) << FSTAB_TAG
+#define LFATAL LOG(FATAL) << FSTAB_TAG
+
+// Logs a message with strerror(errno) at the end
+#define PINFO PLOG(INFO) << FSTAB_TAG
+#define PWARNING PLOG(WARNING) << FSTAB_TAG
+#define PERROR PLOG(ERROR) << FSTAB_TAG
+#define PFATAL PLOG(FATAL) << FSTAB_TAG
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/libfstab/slotselect.cpp
similarity index 91%
rename from fs_mgr/fs_mgr_slotselect.cpp
rename to fs_mgr/libfstab/slotselect.cpp
index 09c1b7e..db3f8da 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/libfstab/slotselect.cpp
@@ -18,8 +18,8 @@
#include <string>
-#include "fs_mgr.h"
-#include "fs_mgr_priv.h"
+#include "fstab_priv.h"
+#include "logging_macros.h"
// Realistically, this file should be part of the android::fs_mgr namespace;
using namespace android::fs_mgr;
@@ -74,3 +74,13 @@
}
return true;
}
+
+namespace android {
+namespace fs_mgr {
+
+std::string OtherSlotSuffix(const std::string& suffix) {
+ return other_suffix(suffix);
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/super_layout_builder.cpp b/fs_mgr/liblp/super_layout_builder.cpp
index 37f28e1..5349e41 100644
--- a/fs_mgr/liblp/super_layout_builder.cpp
+++ b/fs_mgr/liblp/super_layout_builder.cpp
@@ -46,21 +46,21 @@
bool SuperLayoutBuilder::Open(const LpMetadata& metadata) {
for (const auto& partition : metadata.partitions) {
if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {
- // Retrofit devices are not supported.
+ LOG(ERROR) << "Retrofit devices are not supported";
return false;
}
if (!(partition.attributes & LP_PARTITION_ATTR_READONLY)) {
- // Writable partitions are not supported.
+ LOG(ERROR) << "Writable partitions are not supported";
return false;
}
}
if (!metadata.extents.empty()) {
- // Partitions that already have extents are not supported (should
- // never be true of super_empty.img).
+ LOG(ERROR) << "Partitions that already have extents are not supported";
+ // should never be true of super_empty.img.
return false;
}
if (metadata.block_devices.size() != 1) {
- // Only one "super" is supported.
+ LOG(ERROR) << "Only one 'super' is supported";
return false;
}
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 8f35381..6fad662 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -44,7 +44,7 @@
"libext2_uuid",
"libext4_utils",
"libfstab",
- "libsnapshot_snapuserd",
+ "libsnapuserd_client",
"libz",
],
header_libs: [
@@ -101,7 +101,7 @@
}
cc_library_static {
- name: "libsnapshot",
+ name: "libsnapshot_static",
defaults: [
"libsnapshot_defaults",
"libsnapshot_hal_deps",
@@ -112,6 +112,25 @@
],
}
+cc_library {
+ name: "libsnapshot",
+ defaults: [
+ "libsnapshot_defaults",
+ "libsnapshot_cow_defaults",
+ "libsnapshot_hal_deps",
+ ],
+ srcs: [":libsnapshot_sources"],
+ shared_libs: [
+ "libfs_mgr_binder",
+ "liblp",
+ "libprotobuf-cpp-lite",
+ ],
+ static_libs: [
+ "libc++fs",
+ "libsnapshot_cow",
+ ]
+}
+
cc_library_static {
name: "libsnapshot_init",
native_coverage : true,
@@ -182,6 +201,7 @@
"libsnapshot_cow/snapshot_reader.cpp",
"libsnapshot_cow/writer_base.cpp",
"libsnapshot_cow/writer_v2.cpp",
+ "libsnapshot_cow/writer_v3.cpp",
],
export_include_dirs: ["include"],
host_supported: true,
@@ -247,7 +267,7 @@
"libgsi",
"libgmock",
"liblp",
- "libsnapshot",
+ "libsnapshot_static",
"libsnapshot_cow",
"libsnapshot_test_helpers",
"libsparse",
@@ -330,8 +350,6 @@
"libbrotli",
"libc++fs",
"libfstab",
- "libsnapshot",
- "libsnapshot_cow",
"libz",
"update_metadata-protos",
],
@@ -344,6 +362,7 @@
"liblog",
"liblp",
"libprotobuf-cpp-lite",
+ "libsnapshot",
"libstatslog",
"libutils",
],
@@ -374,6 +393,7 @@
srcs: [
"libsnapshot_cow/snapshot_reader_test.cpp",
"libsnapshot_cow/test_v2.cpp",
+ "libsnapshot_cow/test_v3.cpp",
],
cflags: [
"-D_FILE_OFFSET_BITS=64",
@@ -397,6 +417,9 @@
test_options: {
min_shipping_api_level: 30,
},
+ data: [
+ "tools/testdata/cow_v2",
+ ],
auto_gen_config: true,
require_root: false,
host_supported: true,
@@ -428,6 +451,54 @@
],
}
+cc_binary {
+ name: "create_snapshot",
+ host_supported: true,
+ device_supported: false,
+
+ srcs: ["libsnapshot_cow/create_cow.cpp"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ static_libs: [
+ "liblog",
+ "libbase",
+ "libext4_utils",
+ "libsnapshot_cow",
+ "libcrypto",
+ "libbrotli",
+ "libz",
+ "liblz4",
+ "libzstd",
+ "libgflags",
+ ],
+ shared_libs: [
+ ],
+
+ header_libs: [
+ "libstorage_literals_headers",
+ ],
+
+ dist: {
+ targets: [
+ "sdk",
+ "sdk-repo-platform-tools",
+ "sdk_repo",
+ ],
+ },
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: false,
+ },
+ },
+}
+
python_library_host {
name: "snapshot_proto_python",
srcs: [
diff --git a/fs_mgr/libsnapshot/OWNERS b/fs_mgr/libsnapshot/OWNERS
index 9d2b877..c8b1003 100644
--- a/fs_mgr/libsnapshot/OWNERS
+++ b/fs_mgr/libsnapshot/OWNERS
@@ -1,5 +1,6 @@
-# Bug component: 30545
+# Bug component: 1014951
balsini@google.com
dvander@google.com
elsk@google.com
akailash@google.com
+zhangkelvin@google.com
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
new file mode 100644
index 0000000..8191d61
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
@@ -0,0 +1,51 @@
+//
+// Copyright (C) 2023 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 <memory>
+#include "libsnapshot/cow_format.h"
+
+namespace android {
+namespace snapshot {
+
+class ICompressor {
+ public:
+ explicit ICompressor(uint32_t compression_level, uint32_t block_size)
+ : compression_level_(compression_level), block_size_(block_size) {}
+
+ virtual ~ICompressor() {}
+ // Factory methods for compression methods.
+ static std::unique_ptr<ICompressor> Gz(uint32_t compression_level, const int32_t block_size);
+ static std::unique_ptr<ICompressor> Brotli(uint32_t compression_level,
+ const int32_t block_size);
+ static std::unique_ptr<ICompressor> Lz4(uint32_t compression_level, const int32_t block_size);
+ static std::unique_ptr<ICompressor> Zstd(uint32_t compression_level, const int32_t block_size);
+
+ static std::unique_ptr<ICompressor> Create(CowCompression compression,
+ const int32_t block_size);
+
+ uint32_t GetCompressionLevel() const { return compression_level_; }
+ uint32_t GetBlockSize() const { return block_size_; }
+ [[nodiscard]] virtual std::basic_string<uint8_t> Compress(const void* data,
+ size_t length) const = 0;
+
+ private:
+ uint32_t compression_level_;
+ uint32_t block_size_;
+};
+} // namespace snapshot
+} // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 3a81f63..c9777a3 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -22,6 +22,9 @@
namespace android {
namespace snapshot {
+struct CowOperationV3;
+typedef CowOperationV3 CowOperation;
+
static constexpr uint64_t kCowMagicNumber = 0x436f77634f572121ULL;
static constexpr uint32_t kCowVersionMajor = 2;
static constexpr uint32_t kCowVersionMinor = 0;
@@ -49,7 +52,9 @@
// | Footer (fixed) |
// +-----------------------+
//
-// The operations begin immediately after the header, and the "raw data"
+// After the header is a 2mb scratch space that is used to read ahead data during merge operations
+//
+// The operations begin immediately after the scratch space, and the "raw data"
// immediately follows the operation which refers to it. While streaming
// an OTA, we can immediately write the op and data, syncing after each pair,
// while storing operation metadata in memory. At the end, we compute data and
@@ -86,6 +91,18 @@
// Scratch space used during merge
uint32_t buffer_size;
+
+} __attribute__((packed));
+
+struct CowHeaderV3 : public CowHeader {
+ // Location of sequence buffer in COW.
+ uint64_t sequence_buffer_offset;
+ // Size, in bytes, of the CowResumePoint buffer.
+ uint32_t resume_buffer_size;
+ // Size, in bytes, of the CowOperation buffer.
+ uint32_t op_buffer_size;
+ // Compression Algorithm
+ uint32_t compression_algorithm;
} __attribute__((packed));
// This structure is the same size of a normal Operation, but is repurposed for the footer.
@@ -107,9 +124,8 @@
uint64_t num_ops;
} __attribute__((packed));
-// Cow operations are currently fixed-size entries, but this may change if
-// needed.
-struct CowOperation {
+// V2 version of COW. On disk format for older devices
+struct CowOperationV2 {
// The operation code (see the constants and structures below).
uint8_t type;
@@ -143,7 +159,33 @@
uint64_t source;
} __attribute__((packed));
-static_assert(sizeof(CowOperation) == sizeof(CowFooterOperation));
+// The on disk format of cow (currently == CowOperation)
+struct CowOperationV3 {
+ // The operation code (see the constants and structures below).
+ uint8_t type;
+
+ // If this operation reads from the data section of the COW, this contains
+ // the length.
+ uint16_t data_length;
+
+ // The block of data in the new image that this operation modifies.
+ uint32_t new_block;
+
+ // The value of |source| depends on the operation code.
+ //
+ // CopyOp: a 32-bit block location in the source image.
+ // ReplaceOp: an absolute byte offset within the COW's data section.
+ // XorOp: an absolute byte offset in the source image.
+ // ZeroOp: unused
+ // LabelOp: a 64-bit opaque identifier.
+ //
+ // For ops other than Label:
+ // Bits 47-62 are reserved and must be zero.
+ // A block is compressed if it’s data is < block_sz
+ uint64_t source_info;
+} __attribute__((packed));
+
+static_assert(sizeof(CowOperationV2) == sizeof(CowFooterOperation));
static constexpr uint8_t kCowCopyOp = 1;
static constexpr uint8_t kCowReplaceOp = 2;
@@ -161,16 +203,19 @@
kCowCompressLz4 = 3,
kCowCompressZstd = 4,
};
+struct CowCompression {
+ CowCompressionAlgorithm algorithm = kCowCompressNone;
+ uint32_t compression_level = 0;
+};
static constexpr uint8_t kCowReadAheadNotStarted = 0;
static constexpr uint8_t kCowReadAheadInProgress = 1;
static constexpr uint8_t kCowReadAheadDone = 2;
-static inline uint64_t GetCowOpSourceInfoData(const CowOperation* op) {
- return op->source;
-}
-static inline bool GetCowOpSourceInfoCompression(const CowOperation* op) {
- return op->compression != kCowCompressNone;
+static constexpr uint64_t kCowOpSourceInfoDataMask = (1ULL << 48) - 1;
+
+static inline uint64_t GetCowOpSourceInfoData(const CowOperation& op) {
+ return op.source_info & kCowOpSourceInfoDataMask;
}
struct CowFooter {
@@ -194,10 +239,12 @@
// 2MB Scratch space used for read-ahead
static constexpr uint64_t BUFFER_REGION_DEFAULT_SIZE = (1ULL << 21);
+std::ostream& operator<<(std::ostream& os, CowOperationV2 const& arg);
+
std::ostream& operator<<(std::ostream& os, CowOperation const& arg);
-int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_size);
-int64_t GetNextDataOffset(const CowOperation& op, uint32_t cluster_size);
+int64_t GetNextOpOffset(const CowOperationV2& op, uint32_t cluster_size);
+int64_t GetNextDataOffset(const CowOperationV2& op, uint32_t cluster_size);
// Ops that are internal to the Cow Format and not OTA data
bool IsMetadataOp(const CowOperation& op);
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index f4ce52f..2721937 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -165,10 +165,10 @@
void UpdateMergeOpsCompleted(int num_merge_ops) { header_.num_merge_ops += num_merge_ops; }
private:
- bool ParseOps(std::optional<uint64_t> label);
+ bool ParseV2(android::base::borrowed_fd fd, std::optional<uint64_t> label);
bool PrepMergeOps();
uint64_t FindNumCopyops();
- uint8_t GetCompressionType(const CowOperation* op);
+ uint8_t GetCompressionType();
android::base::unique_fd owned_fd_;
android::base::borrowed_fd fd_;
@@ -185,7 +185,10 @@
std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
ReaderFlags reader_flag_;
bool is_merge_{};
+ uint8_t compression_type_ = kCowCompressNone;
};
+bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header);
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index d6194eb..3016e93 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -14,18 +14,17 @@
#pragma once
+#include <libsnapshot/cow_compress.h>
+
#include <stdint.h>
#include <condition_variable>
#include <cstdint>
-#include <future>
#include <memory>
#include <mutex>
#include <optional>
#include <queue>
#include <string>
-#include <thread>
-#include <utility>
#include <vector>
#include <android-base/unique_fd.h>
@@ -110,16 +109,15 @@
class CompressWorker {
public:
- CompressWorker(CowCompressionAlgorithm compression, uint32_t block_size);
+ CompressWorker(std::unique_ptr<ICompressor>&& compressor, uint32_t block_size);
bool RunThread();
void EnqueueCompressBlocks(const void* buffer, size_t num_blocks);
bool GetCompressedBuffers(std::vector<std::basic_string<uint8_t>>* compressed_buf);
void Finalize();
- static std::basic_string<uint8_t> Compress(CowCompressionAlgorithm compression,
- const void* data, size_t length);
+ static uint32_t GetDefaultCompressionLevel(CowCompressionAlgorithm compression);
- static bool CompressBlocks(CowCompressionAlgorithm compression, size_t block_size,
- const void* buffer, size_t num_blocks,
+ static bool CompressBlocks(ICompressor* compressor, size_t block_size, const void* buffer,
+ size_t num_blocks,
std::vector<std::basic_string<uint8_t>>* compressed_data);
private:
@@ -130,7 +128,7 @@
std::vector<std::basic_string<uint8_t>> compressed_data;
};
- CowCompressionAlgorithm compression_;
+ std::unique_ptr<ICompressor> compressor_;
uint32_t block_size_;
std::queue<CompressWork> work_queue_;
@@ -139,7 +137,6 @@
std::condition_variable cv_;
bool stopped_ = false;
- std::basic_string<uint8_t> Compress(const void* data, size_t length);
bool CompressBlocks(const void* buffer, size_t num_blocks,
std::vector<std::basic_string<uint8_t>>* compressed_data);
};
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index df532ee..e7b0020 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -73,6 +73,9 @@
class SnapshotMergeStats;
class SnapshotStatus;
+using std::chrono::duration_cast;
+using namespace std::chrono_literals;
+
static constexpr const std::string_view kCowGroupName = "cow";
static constexpr char kVirtualAbCompressionProp[] = "ro.virtual_ab.compression.enabled";
@@ -424,6 +427,7 @@
FRIEND_TEST(SnapshotTest, MergeFailureCode);
FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
+ FRIEND_TEST(SnapshotTest, BootSnapshotWithoutSlotSwitch);
FRIEND_TEST(SnapshotUpdateTest, AddPartition);
FRIEND_TEST(SnapshotUpdateTest, ConsistencyCheckResume);
FRIEND_TEST(SnapshotUpdateTest, DaemonTransition);
@@ -436,11 +440,13 @@
FRIEND_TEST(SnapshotUpdateTest, QueryStatusError);
FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
FRIEND_TEST(SnapshotUpdateTest, SpaceSwapUpdate);
+ FRIEND_TEST(SnapshotUpdateTest, MapAllSnapshotsWithoutSlotSwitch);
friend class SnapshotTest;
friend class SnapshotUpdateTest;
friend class FlashAfterUpdateTest;
friend class LockTestConsumer;
friend class SnapshotFuzzEnv;
+ friend class MapSnapshots;
friend struct AutoDeleteCowImage;
friend struct AutoDeleteSnapshot;
friend struct PartitionCowCreator;
@@ -455,7 +461,7 @@
bool EnsureImageManager();
// Ensure we're connected to snapuserd.
- bool EnsureSnapuserdConnected();
+ bool EnsureSnapuserdConnected(std::chrono::milliseconds timeout_ms = 10s);
// Helpers for first-stage init.
const std::unique_ptr<IDeviceInfo>& device() const { return device_; }
@@ -548,6 +554,16 @@
// Unmap and remove all known snapshots.
bool RemoveAllSnapshots(LockedFile* lock);
+ // Boot device off snapshots without slot switch
+ bool BootFromSnapshotsWithoutSlotSwitch();
+
+ // Remove kBootSnapshotsWithoutSlotSwitch so that device can boot
+ // without snapshots on the current slot
+ bool PrepareDeviceToBootWithoutSnapshot();
+
+ // Is the kBootSnapshotsWithoutSlotSwitch present
+ bool IsSnapshotWithoutSlotSwitch();
+
// List the known snapshot names.
bool ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots,
const std::string& suffix = "");
@@ -624,7 +640,7 @@
bool CollapseSnapshotDevice(LockedFile* lock, const std::string& name,
const SnapshotStatus& status);
- struct MergeResult {
+ struct [[nodiscard]] MergeResult {
explicit MergeResult(UpdateState state,
MergeFailureCode failure_code = MergeFailureCode::Ok)
: state(state), failure_code(failure_code) {}
@@ -662,6 +678,7 @@
std::string GetRollbackIndicatorPath();
std::string GetForwardMergeIndicatorPath();
std::string GetOldPartitionMetadataPath();
+ std::string GetBootSnapshotsWithoutSlotSwitchPath();
const LpMetadata* ReadOldPartitionMetadata(LockedFile* lock);
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
index a4a0ad6..abc7d33 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
@@ -18,12 +18,16 @@
#include <unistd.h>
#include <limits>
+#include <memory>
#include <queue>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <brotli/encode.h>
+#include <libsnapshot/cow_compress.h>
#include <libsnapshot/cow_format.h>
#include <libsnapshot/cow_reader.h>
#include <libsnapshot/cow_writer.h>
@@ -46,109 +50,175 @@
} else if (name == "none" || name.empty()) {
return {kCowCompressNone};
} else {
+ LOG(ERROR) << "unable to determine default compression algorithm for: " << name;
return {};
}
}
-std::basic_string<uint8_t> CompressWorker::Compress(const void* data, size_t length) {
- return Compress(compression_, data, length);
+std::unique_ptr<ICompressor> ICompressor::Create(CowCompression compression,
+ const int32_t block_size) {
+ switch (compression.algorithm) {
+ case kCowCompressLz4:
+ return ICompressor::Lz4(compression.compression_level, block_size);
+ case kCowCompressBrotli:
+ return ICompressor::Brotli(compression.compression_level, block_size);
+ case kCowCompressGz:
+ return ICompressor::Gz(compression.compression_level, block_size);
+ case kCowCompressZstd:
+ return ICompressor::Zstd(compression.compression_level, block_size);
+ case kCowCompressNone:
+ return nullptr;
+ }
+ return nullptr;
}
-std::basic_string<uint8_t> CompressWorker::Compress(CowCompressionAlgorithm compression,
- const void* data, size_t length) {
+// 1. Default compression level is determined by compression algorithm
+// 2. There might be compatibility issues if a value is changed here, as some older versions of
+// Android will assume a different compression level, causing cow_size estimation differences that
+// will lead to OTA failure. Ensure that the device and OTA package use the same compression level
+// for OTA to succeed.
+uint32_t CompressWorker::GetDefaultCompressionLevel(CowCompressionAlgorithm compression) {
switch (compression) {
case kCowCompressGz: {
- const auto bound = compressBound(length);
- std::basic_string<uint8_t> buffer(bound, '\0');
-
- uLongf dest_len = bound;
- auto rv = compress2(buffer.data(), &dest_len, reinterpret_cast<const Bytef*>(data),
- length, Z_BEST_COMPRESSION);
- if (rv != Z_OK) {
- LOG(ERROR) << "compress2 returned: " << rv;
- return {};
- }
- buffer.resize(dest_len);
- return buffer;
+ return Z_BEST_COMPRESSION;
}
case kCowCompressBrotli: {
- const auto bound = BrotliEncoderMaxCompressedSize(length);
- if (!bound) {
- LOG(ERROR) << "BrotliEncoderMaxCompressedSize returned 0";
- return {};
- }
- std::basic_string<uint8_t> buffer(bound, '\0');
-
- size_t encoded_size = bound;
- auto rv = BrotliEncoderCompress(
- BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, length,
- reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.data());
- if (!rv) {
- LOG(ERROR) << "BrotliEncoderCompress failed";
- return {};
- }
- buffer.resize(encoded_size);
- return buffer;
+ return BROTLI_DEFAULT_QUALITY;
}
case kCowCompressLz4: {
- const auto bound = LZ4_compressBound(length);
- if (!bound) {
- LOG(ERROR) << "LZ4_compressBound returned 0";
- return {};
- }
- std::basic_string<uint8_t> buffer(bound, '\0');
-
- const auto compressed_size = LZ4_compress_default(
- static_cast<const char*>(data), reinterpret_cast<char*>(buffer.data()), length,
- buffer.size());
- if (compressed_size <= 0) {
- LOG(ERROR) << "LZ4_compress_default failed, input size: " << length
- << ", compression bound: " << bound << ", ret: " << compressed_size;
- return {};
- }
- // Don't run compression if the compressed output is larger
- if (compressed_size >= length) {
- buffer.resize(length);
- memcpy(buffer.data(), data, length);
- } else {
- buffer.resize(compressed_size);
- }
- return buffer;
+ break;
}
case kCowCompressZstd: {
- std::basic_string<uint8_t> buffer(ZSTD_compressBound(length), '\0');
- const auto compressed_size =
- ZSTD_compress(buffer.data(), buffer.size(), data, length, 0);
- if (compressed_size <= 0) {
- LOG(ERROR) << "ZSTD compression failed " << compressed_size;
- return {};
- }
- // Don't run compression if the compressed output is larger
- if (compressed_size >= length) {
- buffer.resize(length);
- memcpy(buffer.data(), data, length);
- } else {
- buffer.resize(compressed_size);
- }
- return buffer;
+ return ZSTD_defaultCLevel();
}
- default:
- LOG(ERROR) << "unhandled compression type: " << compression;
+ case kCowCompressNone: {
break;
+ }
}
- return {};
-}
-bool CompressWorker::CompressBlocks(const void* buffer, size_t num_blocks,
- std::vector<std::basic_string<uint8_t>>* compressed_data) {
- return CompressBlocks(compression_, block_size_, buffer, num_blocks, compressed_data);
+ return 0;
}
-bool CompressWorker::CompressBlocks(CowCompressionAlgorithm compression, size_t block_size,
- const void* buffer, size_t num_blocks,
+class GzCompressor final : public ICompressor {
+ public:
+ GzCompressor(uint32_t compression_level, const uint32_t block_size)
+ : ICompressor(compression_level, block_size){};
+
+ std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
+ const auto bound = compressBound(length);
+ std::basic_string<uint8_t> buffer(bound, '\0');
+
+ uLongf dest_len = bound;
+ auto rv = compress2(buffer.data(), &dest_len, reinterpret_cast<const Bytef*>(data), length,
+ GetCompressionLevel());
+ if (rv != Z_OK) {
+ LOG(ERROR) << "compress2 returned: " << rv;
+ return {};
+ }
+ buffer.resize(dest_len);
+ return buffer;
+ };
+};
+
+class Lz4Compressor final : public ICompressor {
+ public:
+ Lz4Compressor(uint32_t compression_level, const uint32_t block_size)
+ : ICompressor(compression_level, block_size){};
+
+ std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
+ const auto bound = LZ4_compressBound(length);
+ if (!bound) {
+ LOG(ERROR) << "LZ4_compressBound returned 0";
+ return {};
+ }
+ std::basic_string<uint8_t> buffer(bound, '\0');
+
+ const auto compressed_size =
+ LZ4_compress_default(static_cast<const char*>(data),
+ reinterpret_cast<char*>(buffer.data()), length, buffer.size());
+ if (compressed_size <= 0) {
+ LOG(ERROR) << "LZ4_compress_default failed, input size: " << length
+ << ", compression bound: " << bound << ", ret: " << compressed_size;
+ return {};
+ }
+ // Don't run compression if the compressed output is larger
+ if (compressed_size >= length) {
+ buffer.resize(length);
+ memcpy(buffer.data(), data, length);
+ } else {
+ buffer.resize(compressed_size);
+ }
+ return buffer;
+ };
+};
+
+class BrotliCompressor final : public ICompressor {
+ public:
+ BrotliCompressor(uint32_t compression_level, const uint32_t block_size)
+ : ICompressor(compression_level, block_size){};
+
+ std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
+ const auto bound = BrotliEncoderMaxCompressedSize(length);
+ if (!bound) {
+ LOG(ERROR) << "BrotliEncoderMaxCompressedSize returned 0";
+ return {};
+ }
+ std::basic_string<uint8_t> buffer(bound, '\0');
+
+ size_t encoded_size = bound;
+ auto rv = BrotliEncoderCompress(
+ GetCompressionLevel(), BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, length,
+ reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.data());
+ if (!rv) {
+ LOG(ERROR) << "BrotliEncoderCompress failed";
+ return {};
+ }
+ buffer.resize(encoded_size);
+ return buffer;
+ };
+};
+
+class ZstdCompressor final : public ICompressor {
+ public:
+ ZstdCompressor(uint32_t compression_level, const uint32_t block_size)
+ : ICompressor(compression_level, block_size),
+ zstd_context_(ZSTD_createCCtx(), ZSTD_freeCCtx) {
+ ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_compressionLevel, compression_level);
+ ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_windowLog, log2(GetBlockSize()));
+ };
+
+ std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
+ std::basic_string<uint8_t> buffer(ZSTD_compressBound(length), '\0');
+ const auto compressed_size =
+ ZSTD_compress2(zstd_context_.get(), buffer.data(), buffer.size(), data, length);
+ if (compressed_size <= 0) {
+ LOG(ERROR) << "ZSTD compression failed " << compressed_size;
+ return {};
+ }
+ // Don't run compression if the compressed output is larger
+ if (compressed_size >= length) {
+ buffer.resize(length);
+ memcpy(buffer.data(), data, length);
+ } else {
+ buffer.resize(compressed_size);
+ }
+ return buffer;
+ };
+
+ private:
+ std::unique_ptr<ZSTD_CCtx, decltype(&ZSTD_freeCCtx)> zstd_context_;
+};
+
+bool CompressWorker::CompressBlocks(const void* buffer, size_t num_blocks,
+ std::vector<std::basic_string<uint8_t>>* compressed_data) {
+ return CompressBlocks(compressor_.get(), block_size_, buffer, num_blocks, compressed_data);
+}
+
+bool CompressWorker::CompressBlocks(ICompressor* compressor, size_t block_size, const void* buffer,
+ size_t num_blocks,
std::vector<std::basic_string<uint8_t>>* compressed_data) {
const uint8_t* iter = reinterpret_cast<const uint8_t*>(buffer);
while (num_blocks) {
- auto data = Compress(compression, iter, block_size);
+ auto data = compressor->Compress(iter, block_size);
if (data.empty()) {
PLOG(ERROR) << "CompressBlocks: Compression failed";
return false;
@@ -247,6 +317,25 @@
return true;
}
+std::unique_ptr<ICompressor> ICompressor::Brotli(uint32_t compression_level,
+ const int32_t block_size) {
+ return std::make_unique<BrotliCompressor>(compression_level, block_size);
+}
+
+std::unique_ptr<ICompressor> ICompressor::Gz(uint32_t compression_level, const int32_t block_size) {
+ return std::make_unique<GzCompressor>(compression_level, block_size);
+}
+
+std::unique_ptr<ICompressor> ICompressor::Lz4(uint32_t compression_level,
+ const int32_t block_size) {
+ return std::make_unique<Lz4Compressor>(compression_level, block_size);
+}
+
+std::unique_ptr<ICompressor> ICompressor::Zstd(uint32_t compression_level,
+ const int32_t block_size) {
+ return std::make_unique<ZstdCompressor>(compression_level, block_size);
+}
+
void CompressWorker::Finalize() {
{
std::unique_lock<std::mutex> lock(lock_);
@@ -255,8 +344,8 @@
cv_.notify_all();
}
-CompressWorker::CompressWorker(CowCompressionAlgorithm compression, uint32_t block_size)
- : compression_(compression), block_size_(block_size) {}
+CompressWorker::CompressWorker(std::unique_ptr<ICompressor>&& compressor, uint32_t block_size)
+ : compressor_(std::move(compressor)), block_size_(block_size) {}
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
index da90cc0..2aaf388 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
@@ -18,6 +18,7 @@
#include <array>
#include <cstring>
+#include <memory>
#include <utility>
#include <vector>
@@ -62,6 +63,8 @@
return IDecompressor::Brotli();
} else if (compressor == "gz") {
return IDecompressor::Gz();
+ } else if (compressor == "zstd") {
+ return IDecompressor::Zstd();
} else {
return nullptr;
}
@@ -211,10 +214,6 @@
return true;
}
-std::unique_ptr<IDecompressor> IDecompressor::Gz() {
- return std::unique_ptr<IDecompressor>(new GzDecompressor());
-}
-
class BrotliDecompressor final : public StreamDecompressor {
public:
~BrotliDecompressor();
@@ -275,10 +274,6 @@
return true;
}
-std::unique_ptr<IDecompressor> IDecompressor::Brotli() {
- return std::unique_ptr<IDecompressor>(new BrotliDecompressor());
-}
-
class Lz4Decompressor final : public IDecompressor {
public:
~Lz4Decompressor() override = default;
@@ -356,7 +351,7 @@
return decompressed_size;
}
std::vector<unsigned char> ignore_buf(decompressed_size);
- if (!Decompress(buffer, decompressed_size)) {
+ if (!Decompress(ignore_buf.data(), decompressed_size)) {
return -1;
}
memcpy(buffer, ignore_buf.data() + ignore_bytes, buffer_size);
@@ -382,6 +377,14 @@
}
};
+std::unique_ptr<IDecompressor> IDecompressor::Brotli() {
+ return std::make_unique<BrotliDecompressor>();
+}
+
+std::unique_ptr<IDecompressor> IDecompressor::Gz() {
+ return std::make_unique<GzDecompressor>();
+}
+
std::unique_ptr<IDecompressor> IDecompressor::Lz4() {
return std::make_unique<Lz4Decompressor>();
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
index ff3ccec..5ab4f7a 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
@@ -14,11 +14,14 @@
// limitations under the License.
//
+#include <inttypes.h>
#include <libsnapshot/cow_format.h>
#include <sys/types.h>
#include <unistd.h>
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <libsnapshot/cow_format.h>
#include "writer_v2.h"
namespace android {
@@ -26,45 +29,77 @@
using android::base::unique_fd;
-std::ostream& operator<<(std::ostream& os, CowOperation const& op) {
- os << "CowOperation(type:";
- if (op.type == kCowCopyOp)
- os << "kCowCopyOp, ";
- else if (op.type == kCowReplaceOp)
- os << "kCowReplaceOp, ";
- else if (op.type == kCowZeroOp)
- os << "kZeroOp, ";
- else if (op.type == kCowFooterOp)
- os << "kCowFooterOp, ";
- else if (op.type == kCowLabelOp)
- os << "kCowLabelOp, ";
- else if (op.type == kCowClusterOp)
- os << "kCowClusterOp ";
- else if (op.type == kCowXorOp)
- os << "kCowXorOp ";
- else if (op.type == kCowSequenceOp)
- os << "kCowSequenceOp ";
- else if (op.type == kCowFooterOp)
- os << "kCowFooterOp ";
- else
- os << (int)op.type << "?,";
- os << "compression:";
- if (op.compression == kCowCompressNone)
- os << "kCowCompressNone, ";
- else if (op.compression == kCowCompressGz)
- os << "kCowCompressGz, ";
- else if (op.compression == kCowCompressBrotli)
- os << "kCowCompressBrotli, ";
- else
- os << (int)op.compression << "?, ";
- os << "data_length:" << op.data_length << ",\t";
- os << "new_block:" << op.new_block << ",\t";
+std::ostream& EmitCowTypeString(std::ostream& os, uint8_t cow_type) {
+ switch (cow_type) {
+ case kCowCopyOp:
+ return os << "kCowCopyOp";
+ case kCowReplaceOp:
+ return os << "kCowReplaceOp";
+ case kCowZeroOp:
+ return os << "kZeroOp";
+ case kCowFooterOp:
+ return os << "kCowFooterOp";
+ case kCowLabelOp:
+ return os << "kCowLabelOp";
+ case kCowClusterOp:
+ return os << "kCowClusterOp";
+ case kCowXorOp:
+ return os << "kCowXorOp";
+ case kCowSequenceOp:
+ return os << "kCowSequenceOp";
+ default:
+ return os << (int)cow_type << "unknown";
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, CowOperationV2 const& op) {
+ os << "CowOperationV2(";
+ EmitCowTypeString(os, op.type) << ", ";
+ switch (op.compression) {
+ case kCowCompressNone:
+ os << "uncompressed, ";
+ break;
+ case kCowCompressGz:
+ os << "gz, ";
+ break;
+ case kCowCompressBrotli:
+ os << "brotli, ";
+ break;
+ case kCowCompressLz4:
+ os << "lz4, ";
+ break;
+ case kCowCompressZstd:
+ os << "zstd, ";
+ break;
+ }
+ os << "data_length:" << op.data_length << ", ";
+ os << "new_block:" << op.new_block << ", ";
os << "source:" << op.source;
os << ")";
return os;
}
-int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_ops) {
+std::ostream& operator<<(std::ostream& os, CowOperation const& op) {
+ os << "CowOperation(";
+ EmitCowTypeString(os, op.type);
+ if (op.type == kCowReplaceOp || op.type == kCowXorOp || op.type == kCowSequenceOp) {
+ os << ", data_length:" << op.data_length;
+ }
+ if (op.type != kCowClusterOp && op.type != kCowSequenceOp && op.type != kCowLabelOp) {
+ os << ", new_block:" << op.new_block;
+ }
+ if (op.type == kCowXorOp || op.type == kCowReplaceOp || op.type == kCowCopyOp) {
+ os << ", source:" << (op.source_info & kCowOpSourceInfoDataMask);
+ } else if (op.type == kCowClusterOp) {
+ os << ", cluster_data:" << (op.source_info & kCowOpSourceInfoDataMask);
+ } else {
+ os << ", label:0x" << android::base::StringPrintf("%" PRIx64, op.source_info);
+ }
+ os << ")";
+ return os;
+}
+
+int64_t GetNextOpOffset(const CowOperationV2& op, uint32_t cluster_ops) {
if (op.type == kCowClusterOp) {
return op.source;
} else if ((op.type == kCowReplaceOp || op.type == kCowXorOp) && cluster_ops == 0) {
@@ -74,11 +109,11 @@
}
}
-int64_t GetNextDataOffset(const CowOperation& op, uint32_t cluster_ops) {
+int64_t GetNextDataOffset(const CowOperationV2& op, uint32_t cluster_ops) {
if (op.type == kCowClusterOp) {
- return cluster_ops * sizeof(CowOperation);
+ return cluster_ops * sizeof(CowOperationV2);
} else if (cluster_ops == 0) {
- return sizeof(CowOperation);
+ return sizeof(CowOperationV2);
} else {
return 0;
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index 489669a..607d610 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -17,9 +17,7 @@
#include <sys/types.h>
#include <unistd.h>
-#include <limits>
#include <optional>
-#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>
@@ -35,6 +33,35 @@
namespace android {
namespace snapshot {
+bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header) {
+ if (lseek(fd.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek header failed";
+ return false;
+ }
+
+ memset(header, 0, sizeof(*header));
+
+ if (!android::base::ReadFully(fd, &header->prefix, sizeof(header->prefix))) {
+ return false;
+ }
+ if (header->prefix.magic != kCowMagicNumber) {
+ LOG(ERROR) << "Header Magic corrupted. Magic: " << header->prefix.magic
+ << "Expected: " << kCowMagicNumber;
+ return false;
+ }
+ if (header->prefix.header_size > sizeof(CowHeader)) {
+ LOG(ERROR) << "Unknown CowHeader size (got " << header->prefix.header_size
+ << " bytes, expected at most " << sizeof(CowHeader) << " bytes)";
+ return false;
+ }
+
+ if (lseek(fd.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek header failed";
+ return false;
+ }
+ return android::base::ReadFully(fd, header, header->prefix.header_size);
+}
+
CowReader::CowReader(ReaderFlags reader_flag, bool is_merge)
: fd_(-1),
header_(),
@@ -57,6 +84,7 @@
cow->data_loc_ = data_loc_;
cow->block_pos_index_ = block_pos_index_;
cow->is_merge_ = is_merge_;
+ cow->compression_type_ = compression_type_;
return cow;
}
@@ -103,8 +131,43 @@
footer_ = parser.footer();
fd_size_ = parser.fd_size();
last_label_ = parser.last_label();
- ops_ = std::move(parser.ops());
data_loc_ = parser.data_loc();
+ ops_ = std::make_shared<std::vector<CowOperation>>(parser.ops()->size());
+
+ // Translate the operation buffer from on disk to in memory
+ for (size_t i = 0; i < parser.ops()->size(); i++) {
+ const auto& v2_op = parser.ops()->at(i);
+
+ auto& new_op = ops_->at(i);
+ new_op.type = v2_op.type;
+ new_op.data_length = v2_op.data_length;
+
+ if (v2_op.new_block > std::numeric_limits<uint32_t>::max()) {
+ LOG(ERROR) << "Out-of-range new block in COW op: " << v2_op;
+ return false;
+ }
+ new_op.new_block = v2_op.new_block;
+
+ uint64_t source_info = v2_op.source;
+ if (new_op.type != kCowLabelOp) {
+ source_info &= kCowOpSourceInfoDataMask;
+ if (source_info != v2_op.source) {
+ LOG(ERROR) << "Out-of-range source value in COW op: " << v2_op;
+ return false;
+ }
+ }
+ if (v2_op.compression != kCowCompressNone) {
+ if (compression_type_ == kCowCompressNone) {
+ compression_type_ = v2_op.compression;
+ } else if (compression_type_ != v2_op.compression) {
+ LOG(ERROR) << "COW has mixed compression types which is not supported;"
+ << " previously saw " << compression_type_ << ", got "
+ << v2_op.compression << ", op: " << v2_op;
+ return false;
+ }
+ }
+ new_op.source_info = source_info;
+ }
// If we're resuming a write, we're not ready to merge
if (label.has_value()) return true;
@@ -310,9 +373,34 @@
bool CowReader::VerifyMergeOps() {
auto itr = GetMergeOpIter(true);
std::unordered_map<uint64_t, const CowOperation*> overwritten_blocks;
+ bool non_ordered_op_found = false;
+
while (!itr->AtEnd()) {
const auto& op = itr->Get();
uint64_t offset;
+
+ // Op should not be a metadata
+ if (IsMetadataOp(*op)) {
+ LOG(ERROR) << "Metadata op: " << op << " found during merge sequence";
+ return false;
+ }
+
+ // Sequence ops should contain all the ordered ops followed
+ // by Replace and Zero ops. If we find the first op which
+ // is not ordered, that means all ordered ops processing
+ // has been completed.
+ if (!IsOrderedOp(*op)) {
+ non_ordered_op_found = true;
+ }
+
+ // Since, all ordered ops processing has been completed,
+ // check that the subsequent ops are not ordered.
+ if (non_ordered_op_found && IsOrderedOp(*op)) {
+ LOG(ERROR) << "Invalid sequence - non-ordered and ordered ops"
+ << " cannot be mixed during sequence generation";
+ return false;
+ }
+
if (!GetSourceOffset(op, &offset)) {
itr->Next();
continue;
@@ -517,7 +605,7 @@
case kCowSequenceOp:
case kCowReplaceOp:
case kCowXorOp:
- return GetRawBytes(GetCowOpSourceInfoData(op), buffer, len, read);
+ return GetRawBytes(GetCowOpSourceInfoData(*op), buffer, len, read);
default:
LOG(ERROR) << "Cannot get raw bytes of non-data op: " << *op;
return false;
@@ -574,14 +662,14 @@
size_t remaining_;
};
-uint8_t CowReader::GetCompressionType(const CowOperation* op) {
- return op->compression;
+uint8_t CowReader::GetCompressionType() {
+ return compression_type_;
}
ssize_t CowReader::ReadData(const CowOperation* op, void* buffer, size_t buffer_size,
size_t ignore_bytes) {
std::unique_ptr<IDecompressor> decompressor;
- switch (GetCompressionType(op)) {
+ switch (GetCompressionType()) {
case kCowCompressNone:
break;
case kCowCompressGz:
@@ -601,7 +689,7 @@
}
break;
default:
- LOG(ERROR) << "Unknown compression type: " << GetCompressionType(op);
+ LOG(ERROR) << "Unknown compression type: " << GetCompressionType();
return -1;
}
@@ -609,7 +697,7 @@
if (op->type == kCowXorOp) {
offset = data_loc_->at(op->new_block);
} else {
- offset = GetCowOpSourceInfoData(op);
+ offset = GetCowOpSourceInfoData(*op);
}
if (!decompressor) {
@@ -625,10 +713,10 @@
bool CowReader::GetSourceOffset(const CowOperation* op, uint64_t* source_offset) {
switch (op->type) {
case kCowCopyOp:
- *source_offset = GetCowOpSourceInfoData(op) * header_.block_size;
+ *source_offset = GetCowOpSourceInfoData(*op) * header_.block_size;
return true;
case kCowXorOp:
- *source_offset = GetCowOpSourceInfoData(op);
+ *source_offset = GetCowOpSourceInfoData(*op);
return true;
default:
return false;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/create_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/create_cow.cpp
new file mode 100644
index 0000000..efb1035
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/create_cow.cpp
@@ -0,0 +1,361 @@
+#include <linux/types.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <condition_variable>
+#include <cstring>
+#include <future>
+#include <iostream>
+#include <limits>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
+#include <storage_literals/storage_literals.h>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/scopeguard.h>
+#include <android-base/strings.h>
+
+#include <gflags/gflags.h>
+#include <libsnapshot/cow_writer.h>
+
+#include <openssl/sha.h>
+
+DEFINE_string(source, "", "Source partition image");
+DEFINE_string(target, "", "Target partition image");
+DEFINE_string(compression, "lz4",
+ "Compression algorithm. Default is set to lz4. Available options: lz4, zstd, gz");
+
+namespace android {
+namespace snapshot {
+
+using namespace android::storage_literals;
+using namespace android;
+using android::base::unique_fd;
+
+using android::snapshot::CreateCowWriter;
+using android::snapshot::ICowWriter;
+
+class CreateSnapshot {
+ public:
+ CreateSnapshot(const std::string& src_file, const std::string& target_file,
+ const std::string& patch_file, const std::string& compression);
+ bool CreateSnapshotPatch();
+
+ private:
+ /* source.img */
+ std::string src_file_;
+ /* target.img */
+ std::string target_file_;
+ /* snapshot-patch generated */
+ std::string patch_file_;
+
+ /*
+ * Active file which is being parsed by this instance.
+ * It will either be source.img or target.img.
+ */
+ std::string parsing_file_;
+ bool create_snapshot_patch_ = false;
+
+ const int kNumThreads = 6;
+ const size_t kBlockSizeToRead = 1_MiB;
+
+ std::unordered_map<std::string, int> source_block_hash_;
+ std::mutex source_block_hash_lock_;
+
+ std::unique_ptr<ICowWriter> writer_;
+ std::mutex write_lock_;
+
+ std::unique_ptr<uint8_t[]> zblock_;
+
+ std::string compression_ = "lz4";
+ unique_fd fd_;
+
+ const int BLOCK_SZ = 4_KiB;
+ void SHA256(const void* data, size_t length, uint8_t out[32]);
+ bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
+ bool ReadBlocks(off_t offset, const int skip_blocks, const uint64_t dev_sz);
+ std::string ToHexString(const uint8_t* buf, size_t len);
+
+ bool CreateSnapshotFile();
+ bool FindSourceBlockHash();
+ bool PrepareParse(std::string& parsing_file, const bool createSnapshot);
+ bool ParsePartition();
+ bool WriteSnapshot(const void* buffer, uint64_t block, std::string& block_hash);
+};
+
+void CreateSnapshotLogger(android::base::LogId, android::base::LogSeverity severity, const char*,
+ const char*, unsigned int, const char* message) {
+ if (severity == android::base::ERROR) {
+ fprintf(stderr, "%s\n", message);
+ } else {
+ fprintf(stdout, "%s\n", message);
+ }
+}
+
+CreateSnapshot::CreateSnapshot(const std::string& src_file, const std::string& target_file,
+ const std::string& patch_file, const std::string& compression)
+ : src_file_(src_file), target_file_(target_file), patch_file_(patch_file) {
+ if (!compression.empty()) {
+ compression_ = compression;
+ }
+}
+
+bool CreateSnapshot::PrepareParse(std::string& parsing_file, const bool createSnapshot) {
+ parsing_file_ = parsing_file;
+ create_snapshot_patch_ = createSnapshot;
+
+ if (createSnapshot) {
+ fd_.reset(open(patch_file_.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666));
+ if (fd_ < 0) {
+ PLOG(ERROR) << "Failed to open the snapshot-patch file: " << patch_file_;
+ return false;
+ }
+
+ zblock_ = std::make_unique<uint8_t[]>(BLOCK_SZ);
+ std::memset(zblock_.get(), 0, BLOCK_SZ);
+
+ CowOptions options;
+ options.compression = compression_;
+ options.num_compress_threads = 2;
+ options.batch_write = true;
+ options.cluster_ops = 600;
+ writer_ = CreateCowWriter(2, options, std::move(fd_));
+ }
+ return true;
+}
+
+/*
+ * Create per-block sha256 hash of source partition
+ */
+bool CreateSnapshot::FindSourceBlockHash() {
+ if (!PrepareParse(src_file_, false)) {
+ return false;
+ }
+ return ParsePartition();
+}
+
+/*
+ * Create snapshot file by comparing sha256 per block
+ * of target.img with the constructed per-block sha256 hash
+ * of source partition.
+ */
+bool CreateSnapshot::CreateSnapshotFile() {
+ if (!PrepareParse(target_file_, true)) {
+ return false;
+ }
+ return ParsePartition();
+}
+
+/*
+ * Creates snapshot patch file by comparing source.img and target.img
+ */
+bool CreateSnapshot::CreateSnapshotPatch() {
+ if (!FindSourceBlockHash()) {
+ return false;
+ }
+ return CreateSnapshotFile();
+}
+
+void CreateSnapshot::SHA256(const void* data, size_t length, uint8_t out[32]) {
+ SHA256_CTX c;
+ SHA256_Init(&c);
+ SHA256_Update(&c, data, length);
+ SHA256_Final(out, &c);
+}
+
+std::string CreateSnapshot::ToHexString(const uint8_t* buf, size_t len) {
+ char lookup[] = "0123456789abcdef";
+ std::string out(len * 2 + 1, '\0');
+ char* outp = out.data();
+ for (; len > 0; len--, buf++) {
+ *outp++ = (char)lookup[*buf >> 4];
+ *outp++ = (char)lookup[*buf & 0xf];
+ }
+ return out;
+}
+
+bool CreateSnapshot::WriteSnapshot(const void* buffer, uint64_t block, std::string& block_hash) {
+ if (std::memcmp(zblock_.get(), buffer, BLOCK_SZ) == 0) {
+ std::lock_guard<std::mutex> lock(write_lock_);
+ return writer_->AddZeroBlocks(block, 1);
+ }
+
+ auto iter = source_block_hash_.find(block_hash);
+ if (iter != source_block_hash_.end()) {
+ std::lock_guard<std::mutex> lock(write_lock_);
+ return writer_->AddCopy(block, iter->second, 1);
+ }
+ std::lock_guard<std::mutex> lock(write_lock_);
+ return writer_->AddRawBlocks(block, buffer, BLOCK_SZ);
+}
+
+bool CreateSnapshot::ReadBlocks(off_t offset, const int skip_blocks, const uint64_t dev_sz) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file_.c_str(), O_RDONLY)));
+ if (fd < 0) {
+ LOG(ERROR) << "open failed: " << parsing_file_;
+ return false;
+ }
+
+ loff_t file_offset = offset;
+ const uint64_t read_sz = kBlockSizeToRead;
+ std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(read_sz);
+
+ while (true) {
+ size_t to_read = std::min((dev_sz - file_offset), read_sz);
+
+ if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {
+ LOG(ERROR) << "Failed to read block from block device: " << parsing_file_
+ << " at offset: " << file_offset << " read-size: " << to_read
+ << " block-size: " << dev_sz;
+ return false;
+ }
+
+ if (!IsBlockAligned(to_read)) {
+ LOG(ERROR) << "unable to parse the un-aligned request: " << to_read;
+ return false;
+ }
+
+ size_t num_blocks = to_read / BLOCK_SZ;
+ uint64_t buffer_offset = 0;
+ off_t foffset = file_offset;
+
+ while (num_blocks) {
+ const void* bufptr = (char*)buffer.get() + buffer_offset;
+ uint64_t blkindex = foffset / BLOCK_SZ;
+
+ uint8_t checksum[32];
+ SHA256(bufptr, BLOCK_SZ, checksum);
+ std::string hash = ToHexString(checksum, sizeof(checksum));
+
+ if (create_snapshot_patch_) {
+ if (!WriteSnapshot(bufptr, blkindex, hash)) {
+ LOG(ERROR) << "WriteSnapshot failed for block: " << blkindex;
+ return false;
+ }
+ } else {
+ std::lock_guard<std::mutex> lock(source_block_hash_lock_);
+ {
+ if (source_block_hash_.count(hash) == 0) {
+ source_block_hash_[hash] = blkindex;
+ }
+ }
+ }
+ buffer_offset += BLOCK_SZ;
+ foffset += BLOCK_SZ;
+ num_blocks -= 1;
+ }
+
+ file_offset += (skip_blocks * to_read);
+ if (file_offset >= dev_sz) {
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool CreateSnapshot::ParsePartition() {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file_.c_str(), O_RDONLY)));
+ if (fd < 0) {
+ LOG(ERROR) << "open failed: " << parsing_file_;
+ return false;
+ }
+
+ uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
+ if (!dev_sz) {
+ LOG(ERROR) << "Could not determine block device size: " << parsing_file_;
+ return false;
+ }
+
+ if (!IsBlockAligned(dev_sz)) {
+ LOG(ERROR) << "dev_sz: " << dev_sz << " is not block aligned";
+ return false;
+ }
+
+ int num_threads = kNumThreads;
+
+ std::vector<std::future<bool>> threads;
+ off_t start_offset = 0;
+ const int skip_blocks = num_threads;
+
+ while (num_threads) {
+ threads.emplace_back(std::async(std::launch::async, &CreateSnapshot::ReadBlocks, this,
+ start_offset, skip_blocks, dev_sz));
+ start_offset += kBlockSizeToRead;
+ num_threads -= 1;
+ if (start_offset >= dev_sz) {
+ break;
+ }
+ }
+
+ bool ret = true;
+ for (auto& t : threads) {
+ ret = t.get() && ret;
+ }
+
+ if (ret && create_snapshot_patch_ && !writer_->Finalize()) {
+ LOG(ERROR) << "Finzalize failed";
+ return false;
+ }
+
+ return ret;
+}
+
+} // namespace snapshot
+} // namespace android
+
+constexpr char kUsage[] = R"(
+NAME
+ create_snapshot - Create snapshot patches by comparing two partition images
+
+SYNOPSIS
+ create_snapshot --source=<source.img> --target=<target.img> --compression="<compression-algorithm"
+
+ source.img -> Source partition image
+ target.img -> Target partition image
+ compressoin -> compression algorithm. Default set to lz4. Supported types are gz, lz4, zstd.
+
+EXAMPLES
+
+ $ create_snapshot $SOURCE_BUILD/system.img $TARGET_BUILD/system.img
+ $ create_snapshot $SOURCE_BUILD/product.img $TARGET_BUILD/product.img --compression="zstd"
+
+)";
+
+int main(int argc, char* argv[]) {
+ android::base::InitLogging(argv, &android::snapshot::CreateSnapshotLogger);
+ ::gflags::SetUsageMessage(kUsage);
+ ::gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+ if (FLAGS_source.empty() || FLAGS_target.empty()) {
+ LOG(INFO) << kUsage;
+ return 0;
+ }
+
+ std::string fname = android::base::Basename(FLAGS_target.c_str());
+ auto parts = android::base::Split(fname, ".");
+ std::string snapshotfile = parts[0] + ".patch";
+ android::snapshot::CreateSnapshot snapshot(FLAGS_source, FLAGS_target, snapshotfile,
+ FLAGS_compression);
+
+ if (!snapshot.CreateSnapshotPatch()) {
+ LOG(ERROR) << "Snapshot creation failed";
+ return -1;
+ }
+
+ LOG(INFO) << "Snapshot patch: " << snapshotfile << " created successfully";
+ return 0;
+}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
index a6dee4f..83b5a12 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
@@ -22,6 +22,7 @@
#include <string>
#include <vector>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <gflags/gflags.h>
@@ -38,11 +39,13 @@
DEFINE_bool(verify_merge_sequence, false, "Verify merge order sequencing");
DEFINE_bool(show_merge_sequence, false, "Show merge order sequence");
DEFINE_bool(show_raw_ops, false, "Show raw ops directly from the underlying parser");
+DEFINE_string(extract_to, "", "Extract the COW contents to the given file");
namespace android {
namespace snapshot {
using android::base::borrowed_fd;
+using android::base::unique_fd;
void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
unsigned int, const char* message) {
@@ -53,7 +56,7 @@
}
}
-static void ShowBad(CowReader& reader, const struct CowOperation* op) {
+static void ShowBad(CowReader& reader, const CowOperation* op) {
size_t count;
auto buffer = std::make_unique<uint8_t[]>(op->data_length);
@@ -104,12 +107,21 @@
}
static bool Inspect(const std::string& path) {
- android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
+ unique_fd fd(open(path.c_str(), O_RDONLY));
if (fd < 0) {
PLOG(ERROR) << "open failed: " << path;
return false;
}
+ unique_fd extract_to;
+ if (!FLAGS_extract_to.empty()) {
+ extract_to.reset(open(FLAGS_extract_to.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0664));
+ if (extract_to < 0) {
+ PLOG(ERROR) << "could not open " << FLAGS_extract_to << " for writing";
+ return false;
+ }
+ }
+
CowReader reader;
auto start_time = std::chrono::steady_clock::now();
@@ -186,12 +198,23 @@
if (!FLAGS_silent && FLAGS_show_ops) std::cout << *op << "\n";
- if (FLAGS_decompress && op->type == kCowReplaceOp && op->compression != kCowCompressNone) {
+ if ((FLAGS_decompress || extract_to >= 0) && op->type == kCowReplaceOp) {
if (reader.ReadData(op, buffer.data(), buffer.size()) < 0) {
std::cerr << "Failed to decompress for :" << *op << "\n";
success = false;
if (FLAGS_show_bad_data) ShowBad(reader, op);
}
+ if (extract_to >= 0) {
+ off_t offset = uint64_t(op->new_block) * header.block_size;
+ if (!android::base::WriteFullyAtOffset(extract_to, buffer.data(), buffer.size(),
+ offset)) {
+ PLOG(ERROR) << "failed to write block " << op->new_block;
+ return false;
+ }
+ }
+ } else if (extract_to >= 0 && !IsMetadataOp(*op) && op->type != kCowZeroOp) {
+ PLOG(ERROR) << "Cannot extract op yet: " << *op;
+ return false;
}
if (op->type == kCowSequenceOp && FLAGS_show_merge_sequence) {
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
index fdb5c59..a7307bf 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
@@ -23,35 +23,6 @@
using android::base::borrowed_fd;
-bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header) {
- if (lseek(fd.get(), 0, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek header failed";
- return false;
- }
-
- memset(header, 0, sizeof(*header));
-
- if (!android::base::ReadFully(fd, &header->prefix, sizeof(header->prefix))) {
- return false;
- }
- if (header->prefix.magic != kCowMagicNumber) {
- LOG(ERROR) << "Header Magic corrupted. Magic: " << header->prefix.magic
- << "Expected: " << kCowMagicNumber;
- return false;
- }
- if (header->prefix.header_size > sizeof(CowHeader)) {
- LOG(ERROR) << "Unknown CowHeader size (got " << header->prefix.header_size
- << " bytes, expected at most " << sizeof(CowHeader) << " bytes)";
- return false;
- }
-
- if (lseek(fd.get(), 0, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek header failed";
- return false;
- }
- return android::base::ReadFully(fd, header, header->prefix.header_size);
-}
-
bool CowParserV2::Parse(borrowed_fd fd, const CowHeader& header, std::optional<uint64_t> label) {
auto pos = lseek(fd.get(), 0, SEEK_END);
if (pos < 0) {
@@ -66,18 +37,18 @@
<< sizeof(CowFooter);
return false;
}
- if (header_.op_size != sizeof(CowOperation)) {
+ if (header_.op_size != sizeof(CowOperationV2)) {
LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
- << sizeof(CowOperation);
+ << sizeof(CowOperationV2);
return false;
}
if (header_.cluster_ops == 1) {
LOG(ERROR) << "Clusters must contain at least two operations to function.";
return false;
}
- if (header_.op_size != sizeof(CowOperation)) {
+ if (header_.op_size != sizeof(CowOperationV2)) {
LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
- << sizeof(CowOperation);
+ << sizeof(CowOperationV2);
return false;
}
if (header_.cluster_ops == 1) {
@@ -123,23 +94,23 @@
uint64_t data_pos = 0;
if (header_.cluster_ops) {
- data_pos = pos + header_.cluster_ops * sizeof(CowOperation);
+ data_pos = pos + header_.cluster_ops * sizeof(CowOperationV2);
} else {
- data_pos = pos + sizeof(CowOperation);
+ data_pos = pos + sizeof(CowOperationV2);
}
- auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
+ auto ops_buffer = std::make_shared<std::vector<CowOperationV2>>();
uint64_t current_op_num = 0;
uint64_t cluster_ops = header_.cluster_ops ?: 1;
bool done = false;
// Alternating op clusters and data
while (!done) {
- uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperation));
+ uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperationV2));
if (to_add == 0) break;
ops_buffer->resize(current_op_num + to_add);
if (!android::base::ReadFully(fd, &ops_buffer->data()[current_op_num],
- to_add * sizeof(CowOperation))) {
+ to_add * sizeof(CowOperationV2))) {
PLOG(ERROR) << "read op failed";
return false;
}
@@ -150,7 +121,7 @@
if (current_op.type == kCowXorOp) {
data_loc->insert({current_op.new_block, data_pos});
}
- pos += sizeof(CowOperation) + GetNextOpOffset(current_op, header_.cluster_ops);
+ pos += sizeof(CowOperationV2) + GetNextOpOffset(current_op, header_.cluster_ops);
data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);
if (current_op.type == kCowClusterOp) {
@@ -222,7 +193,7 @@
<< ops_buffer->size();
return false;
}
- if (ops_buffer->size() * sizeof(CowOperation) != footer_->op.ops_size) {
+ if (ops_buffer->size() * sizeof(CowOperationV2) != footer_->op.ops_size) {
LOG(ERROR) << "ops size does not match ";
return false;
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
index afcf687..f51ff88 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
@@ -33,7 +33,7 @@
const CowHeader& header() const { return header_; }
const std::optional<CowFooter>& footer() const { return footer_; }
- std::shared_ptr<std::vector<CowOperation>> ops() { return ops_; }
+ std::shared_ptr<std::vector<CowOperationV2>> ops() { return ops_; }
std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc() const { return data_loc_; }
uint64_t fd_size() const { return fd_size_; }
const std::optional<uint64_t>& last_label() const { return last_label_; }
@@ -43,13 +43,11 @@
CowHeader header_ = {};
std::optional<CowFooter> footer_;
- std::shared_ptr<std::vector<CowOperation>> ops_;
+ std::shared_ptr<std::vector<CowOperationV2>> ops_;
std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
uint64_t fd_size_;
std::optional<uint64_t> last_label_;
};
-bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header);
-
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
index 31b9a58..35d74ba 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
@@ -86,10 +86,9 @@
while (!iter->AtEnd()) {
auto op = iter->Get();
ASSERT_EQ(op->type, kCowCopyOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 10 + i);
- ASSERT_EQ(op->source, 1000 + i);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 1000 + i);
iter->Next();
i += 1;
}
@@ -133,10 +132,9 @@
auto op = iter->Get();
ASSERT_EQ(op->type, kCowCopyOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 10);
- ASSERT_EQ(op->source, 20);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 20);
std::string sink(data.size(), '\0');
@@ -145,7 +143,6 @@
op = iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_FALSE(GetCowOpSourceInfoCompression(op));
ASSERT_EQ(op->data_length, 4096);
ASSERT_EQ(op->new_block, 50);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -157,20 +154,18 @@
// Note: the zero operation gets split into two blocks.
ASSERT_EQ(op->type, kCowZeroOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 51);
- ASSERT_EQ(op->source, 0);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 52);
- ASSERT_EQ(op->source, 0);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
iter->Next();
ASSERT_TRUE(iter->AtEnd());
@@ -212,10 +207,9 @@
auto op = iter->Get();
ASSERT_EQ(op->type, kCowCopyOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 10);
- ASSERT_EQ(op->source, 20);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 20);
std::string sink(data.size(), '\0');
@@ -224,10 +218,9 @@
op = iter->Get();
ASSERT_EQ(op->type, kCowXorOp);
- ASSERT_FALSE(GetCowOpSourceInfoCompression(op));
ASSERT_EQ(op->data_length, 4096);
ASSERT_EQ(op->new_block, 50);
- ASSERT_EQ(GetCowOpSourceInfoData(op), 98314); // 4096 * 24 + 10
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 98314); // 4096 * 24 + 10
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data);
@@ -237,20 +230,18 @@
// Note: the zero operation gets split into two blocks.
ASSERT_EQ(op->type, kCowZeroOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 51);
- ASSERT_EQ(op->source, 0);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
ASSERT_EQ(op->type, kCowZeroOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 52);
- ASSERT_EQ(op->source, 0);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
iter->Next();
ASSERT_TRUE(iter->AtEnd());
@@ -283,7 +274,6 @@
std::string sink(data.size(), '\0');
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(GetCowOpSourceInfoCompression(op));
ASSERT_EQ(op->data_length, 56); // compressed!
ASSERT_EQ(op->new_block, 50);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -339,7 +329,7 @@
total_blocks += 1;
std::string sink(xor_data.size(), '\0');
ASSERT_EQ(op->new_block, 50);
- ASSERT_EQ(GetCowOpSourceInfoData(op), 98314); // 4096 * 24 + 10
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 98314); // 4096 * 24 + 10
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, xor_data);
}
@@ -472,14 +462,16 @@
if (strcmp(GetParam(), "none") == 0) {
GTEST_SKIP();
}
-
+ CowCompression compression;
auto algorithm = CompressionAlgorithmFromString(GetParam());
ASSERT_TRUE(algorithm.has_value());
+ compression.algorithm = algorithm.value();
std::string expected = "The quick brown fox jumps over the lazy dog.";
expected.resize(4096, '\0');
- auto result = CompressWorker::Compress(*algorithm, expected.data(), expected.size());
+ std::unique_ptr<ICompressor> compressor = ICompressor::Create(compression, 4096);
+ auto result = compressor->Compress(expected.data(), expected.size());
ASSERT_FALSE(result.empty());
HorribleStream<uint8_t> stream(result);
@@ -528,7 +520,6 @@
std::string sink(data.size(), '\0');
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(GetCowOpSourceInfoCompression(op));
ASSERT_EQ(op->data_length, 56); // compressed!
ASSERT_EQ(op->new_block, 50);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -546,7 +537,6 @@
sink = {};
sink.resize(data2.size(), '\0');
- ASSERT_TRUE(GetCowOpSourceInfoCompression(op));
ASSERT_EQ(op->data_length, 41); // compressed!
ASSERT_EQ(op->new_block, 51);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -591,7 +581,6 @@
auto op = iter->Get();
ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(GetCowOpSourceInfoCompression(op));
ASSERT_EQ(op->new_block, 51);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
}
@@ -675,7 +664,7 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 3);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 3);
iter->Next();
@@ -728,7 +717,7 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 0);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
iter->Next();
@@ -786,7 +775,7 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 5);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 5);
iter->Next();
ASSERT_TRUE(iter->AtEnd());
@@ -855,7 +844,7 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 4);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 4);
iter->Next();
@@ -873,7 +862,7 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 5);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 5);
iter->Next();
@@ -926,7 +915,7 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 4);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 4);
iter->Next();
@@ -951,7 +940,7 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 5);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 5);
iter->Next();
@@ -970,7 +959,7 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 6);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 6);
iter->Next();
@@ -1017,7 +1006,7 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 50);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 50);
iter->Next();
@@ -1408,6 +1397,18 @@
ASSERT_TRUE(iter->AtEnd());
}
+TEST_F(CowTest, ParseOptionsTest) {
+ CowOptions options;
+ std::vector<std::pair<std::string, bool>> testcases = {
+ {"gz,4", true}, {"gz,4,4", false}, {"lz4,4", true}, {"brotli,4", true},
+ {"zstd,4", true}, {"zstd,x", false}, {"zs,4", false}, {"zstd.4", false}};
+ for (size_t i = 0; i < testcases.size(); i++) {
+ options.compression = testcases[i].first;
+ CowWriterV2 writer(options, GetCowFd());
+ ASSERT_EQ(writer.Initialize(), testcases[i].second);
+ }
+}
+
TEST_F(CowTest, LegacyRevMergeOpItrTest) {
CowOptions options;
options.cluster_ops = 5;
@@ -1490,6 +1491,37 @@
ASSERT_FALSE(reader.VerifyMergeOps());
}
+unique_fd OpenTestFile(const std::string& file, int flags) {
+ std::string path = "tools/testdata/" + file;
+
+ unique_fd fd(open(path.c_str(), flags));
+ if (fd >= 0) {
+ return fd;
+ }
+
+ path = android::base::GetExecutableDirectory() + "/" + path;
+ return unique_fd{open(path.c_str(), flags)};
+}
+
+TEST_F(CowTest, CompatibilityTest) {
+ std::string filename = "cow_v2";
+ auto fd = OpenTestFile(filename, O_RDONLY);
+ if (fd.get() == -1) {
+ LOG(ERROR) << filename << " not found";
+ GTEST_SKIP();
+ }
+ CowReader reader;
+ reader.Parse(fd);
+
+ const auto& header = reader.GetHeader();
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);
+ ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+
+ CowFooter footer;
+ ASSERT_TRUE(reader.GetFooter(&footer));
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
new file mode 100644
index 0000000..2373d4d
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
@@ -0,0 +1,53 @@
+// Copyright (C) 2023 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/stat.h>
+
+#include <cstdio>
+#include <iostream>
+#include <memory>
+#include <string_view>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <libsnapshot/cow_reader.h>
+#include <libsnapshot/cow_writer.h>
+#include "cow_decompress.h"
+#include "libsnapshot/cow_format.h"
+#include "writer_v3.h"
+using android::base::unique_fd;
+using testing::AssertionFailure;
+using testing::AssertionResult;
+using testing::AssertionSuccess;
+
+namespace android {
+namespace snapshot {
+
+class CowOperationV3Test : public ::testing::Test {
+ protected:
+ virtual void SetUp() override {
+ cow_ = std::make_unique<TemporaryFile>();
+ ASSERT_GE(cow_->fd, 0) << strerror(errno);
+ }
+
+ virtual void TearDown() override { cow_ = nullptr; }
+
+ unique_fd GetCowFd() { return unique_fd{dup(cow_->fd)}; }
+
+ std::unique_ptr<TemporaryFile> cow_;
+};
+
+} // namespace snapshot
+} // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp
index ff34c59..2ffc37b 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp
@@ -191,5 +191,16 @@
block_dev_size);
}
+bool CowWriterBase::Sync() {
+ if (is_dev_null_) {
+ return true;
+ }
+ if (fsync(fd_.get()) < 0) {
+ PLOG(ERROR) << "fsync failed";
+ return false;
+ }
+ return true;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
index c8b4772..709b248 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
@@ -34,6 +34,7 @@
// If the given label is not found, Initialize will fail.
virtual bool Initialize(std::optional<uint64_t> label = {}) = 0;
+ bool Sync();
bool AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
@@ -62,7 +63,6 @@
bool ValidateNewBlock(uint64_t new_block);
CowOptions options_;
- CowHeader header_{};
android::base::unique_fd fd_;
bool is_dev_null_ = false;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
index c549969..a6f449f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
@@ -20,8 +20,8 @@
#include <sys/uio.h>
#include <unistd.h>
+#include <future>
#include <limits>
-#include <queue>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -39,6 +39,8 @@
#include <sys/ioctl.h>
#include <unistd.h>
+#include "android-base/parseint.h"
+#include "android-base/strings.h"
#include "parser_v2.h"
// The info messages here are spammy, but as useful for update_engine. Disable
@@ -108,7 +110,7 @@
header_.prefix.minor_version = kCowVersionMinor;
header_.prefix.header_size = sizeof(CowHeader);
header_.footer_size = sizeof(CowFooter);
- header_.op_size = sizeof(CowOperation);
+ header_.op_size = sizeof(CowOperationV2);
header_.block_size = options_.block_size;
header_.num_merge_ops = options_.num_merge_ops;
header_.cluster_ops = options_.cluster_ops;
@@ -119,12 +121,29 @@
}
bool CowWriterV2::ParseOptions() {
- auto algorithm = CompressionAlgorithmFromString(options_.compression);
+ auto parts = android::base::Split(options_.compression, ",");
+
+ if (parts.size() > 2) {
+ LOG(ERROR) << "failed to parse compression parameters: invalid argument count: "
+ << parts.size() << " " << options_.compression;
+ return false;
+ }
+ auto algorithm = CompressionAlgorithmFromString(parts[0]);
if (!algorithm) {
LOG(ERROR) << "unrecognized compression: " << options_.compression;
return false;
}
- compression_ = *algorithm;
+ if (parts.size() > 1) {
+ if (!android::base::ParseUint(parts[1], &compression_.compression_level)) {
+ LOG(ERROR) << "failed to parse compression level invalid type: " << parts[1];
+ return false;
+ }
+ } else {
+ compression_.compression_level =
+ CompressWorker::GetDefaultCompressionLevel(algorithm.value());
+ }
+
+ compression_.algorithm = *algorithm;
if (options_.cluster_ops == 1) {
LOG(ERROR) << "Clusters must contain at least two operations to function.";
@@ -140,9 +159,9 @@
struct iovec* cowop_ptr = cowop_vec_.get();
struct iovec* data_ptr = data_vec_.get();
for (size_t i = 0; i < header_.cluster_ops; i++) {
- std::unique_ptr<CowOperation> op = std::make_unique<CowOperation>();
+ std::unique_ptr<CowOperationV2> op = std::make_unique<CowOperationV2>();
cowop_ptr[i].iov_base = op.get();
- cowop_ptr[i].iov_len = sizeof(CowOperation);
+ cowop_ptr[i].iov_len = sizeof(CowOperationV2);
opbuffer_vec_.push_back(std::move(op));
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(header_.block_size * 2);
@@ -155,8 +174,7 @@
current_data_pos_ = next_data_pos_;
}
- std::string batch_write = batch_write_ ? "enabled" : "disabled";
- LOG_INFO << "Batch writes: " << batch_write;
+ LOG_INFO << "Batch writes: " << (batch_write_ ? "enabled" : "disabled");
}
void CowWriterV2::InitWorkers() {
@@ -165,7 +183,9 @@
return;
}
for (int i = 0; i < num_compress_threads_; i++) {
- auto wt = std::make_unique<CompressWorker>(compression_, header_.block_size);
+ std::unique_ptr<ICompressor> compressor =
+ ICompressor::Create(compression_, header_.block_size);
+ auto wt = std::make_unique<CompressWorker>(std::move(compressor), header_.block_size);
threads_.emplace_back(std::async(std::launch::async, &CompressWorker::RunThread, wt.get()));
compress_threads_.push_back(std::move(wt));
}
@@ -194,19 +214,19 @@
}
void CowWriterV2::InitPos() {
- next_op_pos_ = sizeof(header_) + header_.buffer_size;
- cluster_size_ = header_.cluster_ops * sizeof(CowOperation);
+ next_op_pos_ = sizeof(CowHeader) + header_.buffer_size;
+ cluster_size_ = header_.cluster_ops * sizeof(CowOperationV2);
if (header_.cluster_ops) {
next_data_pos_ = next_op_pos_ + cluster_size_;
} else {
- next_data_pos_ = next_op_pos_ + sizeof(CowOperation);
+ next_data_pos_ = next_op_pos_ + sizeof(CowOperationV2);
}
current_cluster_size_ = 0;
current_data_size_ = 0;
}
bool CowWriterV2::OpenForWrite() {
- // This limitation is tied to the data field size in CowOperation.
+ // This limitation is tied to the data field size in CowOperationV2.
if (header_.block_size > std::numeric_limits<uint16_t>::max()) {
LOG(ERROR) << "Block size is too large";
return false;
@@ -223,7 +243,7 @@
// Headers are not complete, but this ensures the file is at the right
// position.
- if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
+ if (!android::base::WriteFully(fd_, &header_, sizeof(CowHeader))) {
PLOG(ERROR) << "write failed";
return false;
}
@@ -242,7 +262,7 @@
return false;
}
- if (lseek(fd_.get(), sizeof(header_) + header_.buffer_size, SEEK_SET) < 0) {
+ if (lseek(fd_.get(), sizeof(CowHeader) + header_.buffer_size, SEEK_SET) < 0) {
PLOG(ERROR) << "lseek failed";
return false;
}
@@ -293,7 +313,7 @@
CHECK(!merge_in_progress_);
for (size_t i = 0; i < num_blocks; i++) {
- CowOperation op = {};
+ CowOperationV2 op = {};
op.type = kCowCopyOp;
op.new_block = new_block + i;
op.source = old_block + i;
@@ -320,10 +340,12 @@
const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
compressed_buf_.clear();
if (num_threads <= 1) {
- return CompressWorker::CompressBlocks(compression_, options_.block_size, data, num_blocks,
- &compressed_buf_);
+ if (!compressor_) {
+ compressor_ = ICompressor::Create(compression_, header_.block_size);
+ }
+ return CompressWorker::CompressBlocks(compressor_.get(), options_.block_size, data,
+ num_blocks, &compressed_buf_);
}
-
// Submit the blocks per thread. The retrieval of
// compressed buffers has to be done in the same order.
// We should not poll for completed buffers in a different order as the
@@ -366,7 +388,7 @@
while (num_blocks) {
size_t pending_blocks = (std::min(kProcessingBlocks, num_blocks));
- if (compression_ && num_compress_threads_ > 1) {
+ if (compression_.algorithm && num_compress_threads_ > 1) {
if (!CompressBlocks(pending_blocks, iter)) {
return false;
}
@@ -377,7 +399,7 @@
num_blocks -= pending_blocks;
while (i < size / header_.block_size && pending_blocks) {
- CowOperation op = {};
+ CowOperationV2 op = {};
op.new_block = new_block_start + i;
op.type = type;
if (type == kCowXorOp) {
@@ -386,19 +408,22 @@
op.source = next_data_pos_;
}
- if (compression_) {
+ if (compression_.algorithm) {
auto data = [&, this]() {
if (num_compress_threads_ > 1) {
auto data = std::move(*buf_iter_);
buf_iter_++;
return data;
} else {
- auto data =
- CompressWorker::Compress(compression_, iter, header_.block_size);
+ if (!compressor_) {
+ compressor_ = ICompressor::Create(compression_, header_.block_size);
+ }
+
+ auto data = compressor_->Compress(iter, header_.block_size);
return data;
}
}();
- op.compression = compression_;
+ op.compression = compression_.algorithm;
op.data_length = static_cast<uint16_t>(data.size());
if (!WriteOperation(op, data.data(), data.size())) {
@@ -426,7 +451,7 @@
bool CowWriterV2::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
CHECK(!merge_in_progress_);
for (uint64_t i = 0; i < num_blocks; i++) {
- CowOperation op = {};
+ CowOperationV2 op = {};
op.type = kCowZeroOp;
op.new_block = new_block_start + i;
op.source = 0;
@@ -437,7 +462,7 @@
bool CowWriterV2::EmitLabel(uint64_t label) {
CHECK(!merge_in_progress_);
- CowOperation op = {};
+ CowOperationV2 op = {};
op.type = kCowLabelOp;
op.source = label;
return WriteOperation(op) && Sync();
@@ -448,7 +473,7 @@
size_t to_add = 0;
size_t max_ops = (header_.block_size * 2) / sizeof(uint32_t);
while (num_ops > 0) {
- CowOperation op = {};
+ CowOperationV2 op = {};
op.type = kCowSequenceOp;
op.source = next_data_pos_;
to_add = std::min(num_ops, max_ops);
@@ -464,16 +489,16 @@
}
bool CowWriterV2::EmitCluster() {
- CowOperation op = {};
+ CowOperationV2 op = {};
op.type = kCowClusterOp;
// Next cluster starts after remainder of current cluster and the next data block.
- op.source = current_data_size_ + cluster_size_ - current_cluster_size_ - sizeof(CowOperation);
+ op.source = current_data_size_ + cluster_size_ - current_cluster_size_ - sizeof(CowOperationV2);
return WriteOperation(op);
}
bool CowWriterV2::EmitClusterIfNeeded() {
// If there isn't room for another op and the cluster end op, end the current cluster
- if (cluster_size_ && cluster_size_ < current_cluster_size_ + 2 * sizeof(CowOperation)) {
+ if (cluster_size_ && cluster_size_ < current_cluster_size_ + 2 * sizeof(CowOperationV2)) {
if (!EmitCluster()) return false;
}
return true;
@@ -507,14 +532,14 @@
}
}
- // Footer should be at the end of a file, so if there is data after the current block, end it
- // and start a new cluster.
+ // Footer should be at the end of a file, so if there is data after the current block, end
+ // it and start a new cluster.
if (cluster_size_ && current_data_size_ > 0) {
EmitCluster();
extra_cluster = true;
}
- footer_.op.ops_size = footer_.op.num_ops * sizeof(CowOperation);
+ footer_.op.ops_size = footer_.op.num_ops * sizeof(CowOperationV2);
if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
PLOG(ERROR) << "Failed to seek to footer position.";
return false;
@@ -586,9 +611,9 @@
if (op_vec_index_) {
ret = pwritev(fd_.get(), cowop_vec_.get(), op_vec_index_, current_op_pos_);
- if (ret != (op_vec_index_ * sizeof(CowOperation))) {
- PLOG(ERROR) << "pwritev failed for CowOperation. Expected: "
- << (op_vec_index_ * sizeof(CowOperation));
+ if (ret != (op_vec_index_ * sizeof(CowOperationV2))) {
+ PLOG(ERROR) << "pwritev failed for CowOperationV2. Expected: "
+ << (op_vec_index_ * sizeof(CowOperationV2));
return false;
}
}
@@ -610,17 +635,16 @@
return true;
}
-bool CowWriterV2::WriteOperation(const CowOperation& op, const void* data, size_t size) {
- if (!EnsureSpaceAvailable(next_op_pos_ + sizeof(op))) {
- return false;
- }
- if (!EnsureSpaceAvailable(next_data_pos_ + size)) {
+bool CowWriterV2::WriteOperation(const CowOperationV2& op, const void* data, size_t size) {
+ if (!EnsureSpaceAvailable(next_op_pos_ + sizeof(op)) ||
+ !EnsureSpaceAvailable(next_data_pos_ + size)) {
return false;
}
if (batch_write_) {
- CowOperation* cow_op = reinterpret_cast<CowOperation*>(cowop_vec_[op_vec_index_].iov_base);
- std::memcpy(cow_op, &op, sizeof(CowOperation));
+ CowOperationV2* cow_op =
+ reinterpret_cast<CowOperationV2*>(cowop_vec_[op_vec_index_].iov_base);
+ std::memcpy(cow_op, &op, sizeof(CowOperationV2));
op_vec_index_ += 1;
if (data != nullptr && size > 0) {
@@ -658,7 +682,7 @@
return EmitClusterIfNeeded();
}
-void CowWriterV2::AddOperation(const CowOperation& op) {
+void CowWriterV2::AddOperation(const CowOperationV2& op) {
footer_.op.num_ops++;
if (op.type == kCowClusterOp) {
@@ -670,7 +694,7 @@
}
next_data_pos_ += op.data_length + GetNextDataOffset(op, header_.cluster_ops);
- next_op_pos_ += sizeof(CowOperation) + GetNextOpOffset(op, header_.cluster_ops);
+ next_op_pos_ += sizeof(CowOperationV2) + GetNextOpOffset(op, header_.cluster_ops);
}
bool CowWriterV2::WriteRawData(const void* data, const size_t size) {
@@ -680,17 +704,6 @@
return true;
}
-bool CowWriterV2::Sync() {
- if (is_dev_null_) {
- return true;
- }
- if (fsync(fd_.get()) < 0) {
- PLOG(ERROR) << "fsync failed";
- return false;
- }
- return true;
-}
-
bool CowWriterV2::Truncate(off_t length) {
if (is_dev_null_ || is_block_device_) {
return true;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
index 809ae57..24170eb 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
@@ -14,6 +14,7 @@
#pragma once
+#include <future>
#include "writer_base.h"
namespace android {
@@ -49,21 +50,24 @@
bool OpenForAppend(uint64_t label);
bool GetDataPos(uint64_t* pos);
bool WriteRawData(const void* data, size_t size);
- bool WriteOperation(const CowOperation& op, const void* data = nullptr, size_t size = 0);
- void AddOperation(const CowOperation& op);
+ bool WriteOperation(const CowOperationV2& op, const void* data = nullptr, size_t size = 0);
+ void AddOperation(const CowOperationV2& op);
void InitPos();
void InitBatchWrites();
void InitWorkers();
bool FlushCluster();
bool CompressBlocks(size_t num_blocks, const void* data);
- bool Sync();
bool Truncate(off_t length);
bool EnsureSpaceAvailable(const uint64_t bytes_needed) const;
private:
CowFooter footer_{};
- CowCompressionAlgorithm compression_ = kCowCompressNone;
+ CowHeader header_{};
+ CowCompression compression_;
+ // in the case that we are using one thread for compression, we can store and re-use the same
+ // compressor
+ std::unique_ptr<ICompressor> compressor_;
uint64_t current_op_pos_ = 0;
uint64_t next_op_pos_ = 0;
uint64_t next_data_pos_ = 0;
@@ -80,7 +84,7 @@
std::vector<std::basic_string<uint8_t>> compressed_buf_;
std::vector<std::basic_string<uint8_t>>::iterator buf_iter_;
- std::vector<std::unique_ptr<CowOperation>> opbuffer_vec_;
+ std::vector<std::unique_ptr<CowOperationV2>> opbuffer_vec_;
std::vector<std::unique_ptr<uint8_t[]>> databuffer_vec_;
std::unique_ptr<struct iovec[]> cowop_vec_;
int op_vec_index_ = 0;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
new file mode 100644
index 0000000..2b9867e
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
@@ -0,0 +1,117 @@
+//
+// Copyright (C) 2020 The Android Open Source_info 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 "writer_v3.h"
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <brotli/encode.h>
+#include <libsnapshot/cow_format.h>
+#include <libsnapshot/cow_reader.h>
+#include <libsnapshot/cow_writer.h>
+#include <lz4.h>
+#include <zlib.h>
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+// The info messages here are spammy, but as useful for update_engine. Disable
+// them when running on the host.
+#ifdef __ANDROID__
+#define LOG_INFO LOG(INFO)
+#else
+#define LOG_INFO LOG(VERBOSE)
+#endif
+
+namespace android {
+namespace snapshot {
+
+static_assert(sizeof(off_t) == sizeof(uint64_t));
+
+using android::base::unique_fd;
+
+CowWriterV3::CowWriterV3(const CowOptions& options, unique_fd&& fd)
+ : CowWriterBase(options, std::move(fd)) {}
+
+CowWriterV3::~CowWriterV3() {}
+
+bool CowWriterV3::Initialize(std::optional<uint64_t> label) {
+ LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
+ if (label) return false;
+ return false;
+}
+
+bool CowWriterV3::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
+ LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
+ if (new_block || old_block || num_blocks) return false;
+ return false;
+}
+
+bool CowWriterV3::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+ LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
+
+ if (new_block_start || data || size) return false;
+ return false;
+}
+
+bool CowWriterV3::EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) {
+ LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
+ if (new_block_start || old_block || offset || data || size) return false;
+ return false;
+}
+
+bool CowWriterV3::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+ LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
+ if (new_block_start && num_blocks) return false;
+ return false;
+}
+
+bool CowWriterV3::EmitLabel(uint64_t label) {
+ LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
+ if (label) return false;
+ return false;
+}
+
+bool CowWriterV3::EmitSequenceData(size_t num_ops, const uint32_t* data) {
+ LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
+ if (num_ops && data) return false;
+ return false;
+}
+
+bool CowWriterV3::Finalize() {
+ LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
+ return false;
+}
+
+uint64_t CowWriterV3::GetCowSize() {
+ LOG(ERROR) << __LINE__ << " " << __FILE__
+ << " <- Get Cow Size function here should never be called";
+ return 0;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
new file mode 100644
index 0000000..ddd7287
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 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 <future>
+#include "writer_base.h"
+
+namespace android {
+namespace snapshot {
+
+class CowWriterV3 : public CowWriterBase {
+ public:
+ explicit CowWriterV3(const CowOptions& options, android::base::unique_fd&& fd);
+ ~CowWriterV3() override;
+
+ bool Initialize(std::optional<uint64_t> label = {}) override;
+ bool Finalize() override;
+ uint64_t GetCowSize() override;
+
+ protected:
+ virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
+ virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+ virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) override;
+ virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+ virtual bool EmitLabel(uint64_t label) override;
+ virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index 7057223..5bc7e65 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -131,15 +131,28 @@
return is_optimized;
}
-void WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Extent& de,
+bool WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Extent& de,
unsigned int sectors_per_block) {
const auto block_boundary = de.start_block() + de.num_blocks();
for (auto b = de.start_block(); b < block_boundary; ++b) {
for (unsigned int s = 0; s < sectors_per_block; ++s) {
- const auto sector_id = b * sectors_per_block + s;
+ // sector_id = b * sectors_per_block + s;
+ uint64_t block_start_sector_id;
+ if (__builtin_mul_overflow(b, sectors_per_block, &block_start_sector_id)) {
+ LOG(ERROR) << "Integer overflow when calculating sector id (" << b << " * "
+ << sectors_per_block << ")";
+ return false;
+ }
+ uint64_t sector_id;
+ if (__builtin_add_overflow(block_start_sector_id, s, §or_id)) {
+ LOG(ERROR) << "Integer overflow when calculating sector id ("
+ << block_start_sector_id << " + " << s << ")";
+ return false;
+ }
sc->WriteSector(sector_id);
}
}
+ return true;
}
std::optional<uint64_t> PartitionCowCreator::GetCowSize() {
@@ -167,7 +180,7 @@
// Allocate space for extra extents (if any). These extents are those that can be
// used for error corrections or to store verity hash trees.
for (const auto& de : extra_extents) {
- WriteExtent(&sc, de, sectors_per_block);
+ if (!WriteExtent(&sc, de, sectors_per_block)) return std::nullopt;
}
if (update == nullptr) return sc.cow_size_bytes();
@@ -182,7 +195,7 @@
}
for (const auto& de : written_op->dst_extents()) {
- WriteExtent(&sc, de, sectors_per_block);
+ if (!WriteExtent(&sc, de, sectors_per_block)) return std::nullopt;
}
}
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index fbea79b..4743a42 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -83,6 +83,8 @@
using namespace std::chrono_literals;
using namespace std::string_literals;
+static constexpr char kBootSnapshotsWithoutSlotSwitch[] =
+ "/metadata/ota/snapshot-boot-without-slot-switch";
static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
static constexpr char kRollbackIndicatorPath[] = "/metadata/ota/rollback-indicator";
static constexpr auto kUpdateStateCheckInterval = 2s;
@@ -217,6 +219,12 @@
auto file = LockExclusive();
if (!file) return false;
+ if (IsSnapshotWithoutSlotSwitch()) {
+ LOG(ERROR) << "Cannot cancel the snapshots as partitions are mounted off the snapshots on "
+ "current slot.";
+ return false;
+ }
+
UpdateState state = ReadUpdateState(file.get());
if (state == UpdateState::None) {
RemoveInvalidSnapshots(file.get());
@@ -299,10 +307,9 @@
// - For ForwardMerge, FinishedSnapshotWrites asserts that the existence of the indicator
// matches the incoming update.
std::vector<std::string> files = {
- GetSnapshotBootIndicatorPath(),
- GetRollbackIndicatorPath(),
- GetForwardMergeIndicatorPath(),
- GetOldPartitionMetadataPath(),
+ GetSnapshotBootIndicatorPath(), GetRollbackIndicatorPath(),
+ GetForwardMergeIndicatorPath(), GetOldPartitionMetadataPath(),
+ GetBootSnapshotsWithoutSlotSwitchPath(),
};
for (const auto& file : files) {
RemoveFileIfExists(file);
@@ -483,6 +490,32 @@
LOG(ERROR) << "Failed to retrieve base_sectors from Snapuserd";
return false;
}
+ } else if (IsSnapshotWithoutSlotSwitch()) {
+ // When snapshots are on current slot, we determine the size
+ // of block device based on the number of COW operations. We cannot
+ // use base device as it will be from older image.
+ size_t num_ops = 0;
+ uint64_t dev_sz = 0;
+ unique_fd fd(open(cow_file.c_str(), O_RDONLY | O_CLOEXEC));
+ if (fd < 0) {
+ PLOG(ERROR) << "Failed to open " << cow_file;
+ return false;
+ }
+
+ CowReader reader;
+ if (!reader.Parse(std::move(fd))) {
+ LOG(ERROR) << "Failed to parse cow " << cow_file;
+ return false;
+ }
+
+ const auto& header = reader.GetHeader();
+ if (header.prefix.major_version > 2) {
+ LOG(ERROR) << "COW format not supported";
+ return false;
+ }
+ num_ops = reader.get_num_total_data_ops();
+ dev_sz = (num_ops * header.block_size);
+ base_sectors = dev_sz >> 9;
} else {
// For userspace snapshots, the size of the base device is taken as the
// size of the dm-user block device. Since there is no pseudo mapping
@@ -729,6 +762,14 @@
LOG(ERROR) << "Failed to remove status file " << file_path << ": " << error;
return false;
}
+
+ // This path may never exist. If it is present, then it's a stale
+ // snapshot status file. Just remove the file and log the message.
+ const std::string tmp_path = file_path + ".tmp";
+ if (!android::base::RemoveFileIfExists(tmp_path, &error)) {
+ LOG(ERROR) << "Failed to remove stale snapshot file " << tmp_path;
+ }
+
return true;
}
@@ -754,10 +795,10 @@
return false;
}
- auto other_suffix = device_->GetOtherSlotSuffix();
+ auto current_slot_suffix = device_->GetSlotSuffix();
for (const auto& snapshot : snapshots) {
- if (android::base::EndsWith(snapshot, other_suffix)) {
+ if (!android::base::EndsWith(snapshot, current_slot_suffix)) {
// Allow the merge to continue, but log this unexpected case.
LOG(ERROR) << "Unexpected snapshot found during merge: " << snapshot;
continue;
@@ -1123,7 +1164,7 @@
return MergeResult(UpdateState::MergeFailed, MergeFailureCode::ListSnapshots);
}
- auto other_suffix = device_->GetOtherSlotSuffix();
+ auto current_slot_suffix = device_->GetSlotSuffix();
bool cancelled = false;
bool merging = false;
@@ -1131,9 +1172,9 @@
bool wrong_phase = false;
MergeFailureCode failure_code = MergeFailureCode::Ok;
for (const auto& snapshot : snapshots) {
- if (android::base::EndsWith(snapshot, other_suffix)) {
+ if (!android::base::EndsWith(snapshot, current_slot_suffix)) {
// This will have triggered an error message in InitiateMerge already.
- LOG(INFO) << "Skipping merge validation of unexpected snapshot: " << snapshot;
+ LOG(ERROR) << "Skipping merge validation of unexpected snapshot: " << snapshot;
continue;
}
@@ -1248,14 +1289,12 @@
GetMetadataPartitionState(*current_metadata, name) == MetadataPartitionState::Updated);
if (UpdateUsesUserSnapshots(lock)) {
- std::string merge_status;
- if (EnsureSnapuserdConnected()) {
- // Query the snapshot status from the daemon
- merge_status = snapuserd_client_->QuerySnapshotStatus(name);
- } else {
- MergeResult(UpdateState::MergeFailed, MergeFailureCode::QuerySnapshotStatus);
+ if (!EnsureSnapuserdConnected()) {
+ return MergeResult(UpdateState::MergeFailed, MergeFailureCode::QuerySnapshotStatus);
}
+ // Query the snapshot status from the daemon
+ const auto merge_status = snapuserd_client_->QuerySnapshotStatus(name);
if (merge_status == "snapshot-merge-failed") {
return MergeResult(UpdateState::MergeFailed, MergeFailureCode::UnknownTargetType);
}
@@ -1473,6 +1512,10 @@
return result;
}
+std::string SnapshotManager::GetBootSnapshotsWithoutSlotSwitchPath() {
+ return metadata_dir_ + "/" + android::base::Basename(kBootSnapshotsWithoutSlotSwitch);
+}
+
std::string SnapshotManager::GetSnapshotBootIndicatorPath() {
return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath);
}
@@ -2114,6 +2157,10 @@
return state;
}
+bool SnapshotManager::IsSnapshotWithoutSlotSwitch() {
+ return (access(GetBootSnapshotsWithoutSlotSwitchPath().c_str(), F_OK) == 0);
+}
+
bool SnapshotManager::UpdateUsesCompression() {
auto lock = LockShared();
if (!lock) return false;
@@ -2206,6 +2253,13 @@
}
bool SnapshotManager::NeedSnapshotsInFirstStageMount() {
+ if (IsSnapshotWithoutSlotSwitch()) {
+ if (GetCurrentSlot() != Slot::Source) {
+ LOG(ERROR) << "Snapshots marked to boot without slot switch; but slot is wrong";
+ return false;
+ }
+ return true;
+ }
// If we fail to read, we'll wind up using CreateLogicalPartitions, which
// will create devices that look like the old slot, except with extra
// content at the end of each device. This will confuse dm-verity, and
@@ -2341,7 +2395,8 @@
// completed, live_snapshot_status is set to nullopt.
std::optional<SnapshotStatus> live_snapshot_status;
do {
- if (!(params.partition->attributes & LP_PARTITION_ATTR_UPDATED)) {
+ if (!IsSnapshotWithoutSlotSwitch() &&
+ !(params.partition->attributes & LP_PARTITION_ATTR_UPDATED)) {
LOG(INFO) << "Detected re-flashing of partition, will skip snapshot: "
<< params.GetPartitionName();
break;
@@ -2490,9 +2545,6 @@
}
created_devices.EmplaceBack<AutoUnmapDevice>(&dm_, name);
- remaining_time = GetRemainingTime(params.timeout_ms, begin);
- if (remaining_time.count() < 0) return false;
-
cow_device = new_cow_device;
}
@@ -2507,6 +2559,9 @@
// the user-space will not start the merge. We have to explicitly inform the
// daemon to resume the merge. Check ProcessUpdateState() call stack.
if (!UpdateUsesUserSnapshots(lock)) {
+ remaining_time = GetRemainingTime(params.timeout_ms, begin);
+ if (remaining_time.count() < 0) return false;
+
std::string path;
if (!MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time,
&path)) {
@@ -2697,7 +2752,7 @@
// to unmap; hence, we can't be deleting the device
// as the table would be mounted off partitions and will fail.
if (snapshot_status.state() != SnapshotState::MERGE_COMPLETED) {
- if (!DeleteDeviceIfExists(dm_user_name)) {
+ if (!DeleteDeviceIfExists(dm_user_name, 4000ms)) {
LOG(ERROR) << "Cannot unmap " << dm_user_name;
return false;
}
@@ -3092,7 +3147,7 @@
return true;
}
-bool SnapshotManager::EnsureSnapuserdConnected() {
+bool SnapshotManager::EnsureSnapuserdConnected(std::chrono::milliseconds timeout_ms) {
if (snapuserd_client_) {
return true;
}
@@ -3101,7 +3156,7 @@
return false;
}
- snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
+ snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, timeout_ms);
if (!snapuserd_client_) {
LOG(ERROR) << "Unable to connect to snapuserd";
return false;
@@ -4366,13 +4421,70 @@
bool SnapshotManager::IsUserspaceSnapshotUpdateInProgress() {
auto slot = GetCurrentSlot();
if (slot == Slot::Target) {
+ // Merge in-progress
if (IsSnapuserdRequired()) {
return true;
}
}
+ // Let's check more deeper to see if snapshots are mounted
+ auto lock = LockExclusive();
+ if (!lock) {
+ return false;
+ }
+
+ std::vector<std::string> snapshots;
+ if (!ListSnapshots(lock.get(), &snapshots)) {
+ return false;
+ }
+
+ for (const auto& snapshot : snapshots) {
+ // Active snapshot and daemon is alive
+ if (IsSnapshotDevice(snapshot) && EnsureSnapuserdConnected(2s)) {
+ return true;
+ }
+ }
+
return false;
}
+bool SnapshotManager::BootFromSnapshotsWithoutSlotSwitch() {
+ auto lock = LockExclusive();
+ if (!lock) return false;
+
+ auto contents = device_->GetSlotSuffix();
+ // This is the indicator which tells first-stage init
+ // to boot from snapshots even though there was no slot-switch
+ auto boot_file = GetBootSnapshotsWithoutSlotSwitchPath();
+ if (!WriteStringToFileAtomic(contents, boot_file)) {
+ PLOG(ERROR) << "write failed: " << boot_file;
+ return false;
+ }
+
+ SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
+ update_status.set_state(UpdateState::Initiated);
+ update_status.set_userspace_snapshots(true);
+ update_status.set_using_snapuserd(true);
+ if (!WriteSnapshotUpdateStatus(lock.get(), update_status)) {
+ return false;
+ }
+ return true;
+}
+
+bool SnapshotManager::PrepareDeviceToBootWithoutSnapshot() {
+ auto lock = LockExclusive();
+ if (!lock) return false;
+
+ android::base::RemoveFileIfExists(GetSnapshotBootIndicatorPath());
+ android::base::RemoveFileIfExists(GetBootSnapshotsWithoutSlotSwitchPath());
+
+ SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
+ update_status.set_state(UpdateState::Cancelled);
+ if (!WriteSnapshotUpdateStatus(lock.get(), update_status)) {
+ return false;
+ }
+ return true;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 0a85489..e506110 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -685,6 +685,17 @@
}
ASSERT_TRUE(sm->InitiateMerge());
+ // Create stale files in snapshot directory. Merge should skip these files
+ // as the suffix doesn't match the current slot.
+ auto tmp_path = test_device->GetMetadataDir() + "/snapshots/test_partition_b.tmp";
+ auto other_slot = test_device->GetMetadataDir() + "/snapshots/test_partition_a";
+
+ unique_fd fd(open(tmp_path.c_str(), O_RDWR | O_CLOEXEC | O_CREAT, 0644));
+ ASSERT_GE(fd, 0);
+
+ fd.reset(open(other_slot.c_str(), O_RDWR | O_CLOEXEC | O_CREAT, 0644));
+ ASSERT_GE(fd, 0);
+
// The device should have been switched to a snapshot-merge target.
DeviceMapper::TargetInfo target;
ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
@@ -700,13 +711,23 @@
ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::MergeCompleted);
ASSERT_EQ(sm->GetUpdateState(), UpdateState::None);
+ // Make sure that snapshot states are cleared and all stale files
+ // are deleted
+ {
+ ASSERT_TRUE(AcquireLock());
+ auto local_lock = std::move(lock_);
+ std::vector<std::string> snapshots;
+ ASSERT_TRUE(sm->ListSnapshots(local_lock.get(), &snapshots));
+ ASSERT_TRUE(snapshots.empty());
+ }
+
// The device should no longer be a snapshot or snapshot-merge.
ASSERT_FALSE(sm->IsSnapshotDevice("test_partition_b"));
// Test that we can read back the string we wrote to the snapshot. Note
// that the base device is gone now. |snap_device| contains the correct
// partition.
- unique_fd fd(open("/dev/block/mapper/test_partition_b", O_RDONLY | O_CLOEXEC));
+ fd.reset(open("/dev/block/mapper/test_partition_b", O_RDONLY | O_CLOEXEC));
ASSERT_GE(fd, 0);
std::string buffer(test_string.size(), '\0');
@@ -2538,6 +2559,56 @@
}
}
+TEST_F(SnapshotUpdateTest, MapAllSnapshotsWithoutSlotSwitch) {
+ MountMetadata();
+ AddOperationForPartitions();
+ // Execute the update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+ if (!sm->UpdateUsesUserSnapshots()) {
+ GTEST_SKIP() << "Test does not apply as UserSnapshots aren't enabled.";
+ }
+
+ ASSERT_TRUE(WriteSnapshots());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+ if (ShouldSkipLegacyMerging()) {
+ GTEST_SKIP() << "Skipping legacy merge test";
+ }
+ // Mark the indicator
+ ASSERT_TRUE(sm->BootFromSnapshotsWithoutSlotSwitch());
+
+ ASSERT_TRUE(sm->EnsureSnapuserdConnected());
+ sm->set_use_first_stage_snapuserd(true);
+
+ ASSERT_TRUE(sm->NeedSnapshotsInFirstStageMount());
+
+ // Map snapshots
+ ASSERT_TRUE(sm->MapAllSnapshots(10s));
+
+ // New updates should fail
+ ASSERT_FALSE(sm->BeginUpdate());
+
+ // Snapshots cannot be cancelled
+ ASSERT_FALSE(sm->CancelUpdate());
+
+ // Merge cannot start
+ ASSERT_FALSE(sm->InitiateMerge());
+
+ // Read bytes back and verify they match the cache.
+ ASSERT_TRUE(IsPartitionUnchanged("sys_b"));
+
+ // Remove the indicators
+ ASSERT_TRUE(sm->PrepareDeviceToBootWithoutSnapshot());
+
+ // Ensure snapshots are still mounted
+ ASSERT_TRUE(sm->IsUserspaceSnapshotUpdateInProgress());
+
+ // Cleanup snapshots
+ ASSERT_TRUE(sm->UnmapAllSnapshots());
+}
+
TEST_F(SnapshotUpdateTest, MapAllSnapshots) {
AddOperationForPartitions();
// Execute the update.
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 38eb719..ebaca2d 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -17,14 +17,25 @@
#include <sysexits.h>
#include <chrono>
+#include <filesystem>
+#include <fstream>
+#include <future>
#include <iostream>
#include <map>
#include <sstream>
+#include <thread>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
+#include <android-base/chrono_utils.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
#include <fstab/fstab.h>
@@ -33,6 +44,8 @@
#include <libsnapshot/snapshot.h>
#include <storage_literals/storage_literals.h>
+#include "partition_cow_creator.h"
+
#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
#include <BootControlClient.h>
#endif
@@ -56,13 +69,211 @@
" merge\n"
" Deprecated.\n"
" map\n"
- " Map all partitions at /dev/block/mapper\n";
+ " Map all partitions at /dev/block/mapper\n"
+ " map-snapshots <directory where snapshot patches are present>\n"
+ " Map all snapshots based on patches present in the directory\n"
+ " unmap-snapshots\n"
+ " Unmap all pre-created snapshots\n"
+ " delete-snapshots\n"
+ " Delete all pre-created snapshots\n"
+ " revert-snapshots\n"
+ " Prepares devices to boot without snapshots on next boot.\n"
+ " This does not delete the snapshot. It only removes the indicators\n"
+ " so that first stage init will not mount from snapshots.\n";
return EX_USAGE;
}
namespace android {
namespace snapshot {
+class MapSnapshots {
+ public:
+ MapSnapshots(std::string path = "");
+ bool CreateSnapshotDevice(std::string& partition_name, std::string& patch);
+ bool InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch);
+ bool FinishSnapshotWrites();
+ bool UnmapCowImagePath(std::string& name);
+ bool DeleteSnapshots();
+ bool CleanupSnapshot() { return sm_->PrepareDeviceToBootWithoutSnapshot(); }
+ bool BeginUpdate();
+
+ private:
+ std::optional<std::string> GetCowImagePath(std::string& name);
+ bool WriteSnapshotPatch(std::string cow_device, std::string patch);
+ std::unique_ptr<SnapshotManager::LockedFile> lock_;
+ std::unique_ptr<SnapshotManager> sm_;
+ std::vector<std::future<bool>> threads_;
+ std::string snapshot_dir_path_;
+};
+
+MapSnapshots::MapSnapshots(std::string path) {
+ sm_ = SnapshotManager::New();
+ if (!sm_) {
+ std::cout << "Failed to create snapshotmanager";
+ exit(1);
+ }
+ snapshot_dir_path_ = path + "/";
+}
+
+bool MapSnapshots::BeginUpdate() {
+ lock_ = sm_->LockExclusive();
+ std::vector<std::string> snapshots;
+ sm_->ListSnapshots(lock_.get(), &snapshots);
+ if (!snapshots.empty()) {
+ // Snapshots are already present.
+ return true;
+ }
+
+ lock_ = nullptr;
+ if (!sm_->BeginUpdate()) {
+ LOG(ERROR) << "BeginUpdate failed";
+ return false;
+ }
+ lock_ = sm_->LockExclusive();
+ return true;
+}
+
+bool MapSnapshots::CreateSnapshotDevice(std::string& partition_name, std::string& patchfile) {
+ std::string parsing_file = snapshot_dir_path_ + patchfile;
+
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file.c_str(), O_RDONLY)));
+ if (fd < 0) {
+ LOG(ERROR) << "Failed to open file: " << parsing_file;
+ return false;
+ }
+
+ uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
+ if (!dev_sz) {
+ LOG(ERROR) << "Could not determine block device size: " << parsing_file;
+ return false;
+ }
+
+ const int block_sz = 4_KiB;
+ dev_sz += block_sz - 1;
+ dev_sz &= ~(block_sz - 1);
+
+ SnapshotStatus status;
+ status.set_state(SnapshotState::CREATED);
+ status.set_using_snapuserd(true);
+ status.set_old_partition_size(0);
+ status.set_name(partition_name);
+ status.set_cow_file_size(dev_sz);
+ status.set_cow_partition_size(0);
+
+ PartitionCowCreator cow_creator;
+ cow_creator.using_snapuserd = true;
+
+ if (!sm_->CreateSnapshot(lock_.get(), &cow_creator, &status)) {
+ LOG(ERROR) << "CreateSnapshot failed";
+ return false;
+ }
+
+ if (!sm_->CreateCowImage(lock_.get(), partition_name)) {
+ LOG(ERROR) << "CreateCowImage failed";
+ return false;
+ }
+
+ return true;
+}
+
+std::optional<std::string> MapSnapshots::GetCowImagePath(std::string& name) {
+ auto cow_dev = sm_->MapCowImage(name, 5s);
+ if (!cow_dev.has_value()) {
+ LOG(ERROR) << "Failed to get COW device path";
+ return std::nullopt;
+ }
+
+ LOG(INFO) << "COW Device path: " << cow_dev.value();
+ return cow_dev;
+}
+
+bool MapSnapshots::WriteSnapshotPatch(std::string cow_device, std::string patch) {
+ std::string patch_file = snapshot_dir_path_ + patch;
+
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(patch_file.c_str(), O_RDONLY)));
+ if (fd < 0) {
+ LOG(ERROR) << "Failed to open file: " << patch_file;
+ return false;
+ }
+
+ uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
+ if (!dev_sz) {
+ std::cout << "Could not determine block device size: " << patch_file;
+ return false;
+ }
+
+ android::base::unique_fd cfd(TEMP_FAILURE_RETRY(open(cow_device.c_str(), O_RDWR)));
+ if (cfd < 0) {
+ LOG(ERROR) << "Failed to open file: " << cow_device;
+ return false;
+ }
+
+ const uint64_t read_sz = 1_MiB;
+ std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(read_sz);
+ off_t file_offset = 0;
+
+ while (true) {
+ size_t to_read = std::min((dev_sz - file_offset), read_sz);
+ if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {
+ PLOG(ERROR) << "ReadFullyAtOffset failed";
+ return false;
+ }
+
+ if (!android::base::WriteFullyAtOffset(cfd, buffer.get(), to_read, file_offset)) {
+ PLOG(ERROR) << "WriteFullyAtOffset failed";
+ return false;
+ }
+ file_offset += to_read;
+ if (file_offset >= dev_sz) {
+ break;
+ }
+ }
+ fsync(cfd.get());
+ return true;
+}
+
+bool MapSnapshots::InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch) {
+ auto path = GetCowImagePath(pname);
+ if (!path.has_value()) {
+ return false;
+ }
+ threads_.emplace_back(std::async(std::launch::async, &MapSnapshots::WriteSnapshotPatch, this,
+ path.value(), snapshot_patch));
+ return true;
+}
+
+bool MapSnapshots::FinishSnapshotWrites() {
+ bool ret = true;
+ for (auto& t : threads_) {
+ ret = t.get() && ret;
+ }
+
+ lock_ = nullptr;
+ if (ret) {
+ LOG(INFO) << "Pre-created snapshots successfully copied";
+ if (!sm_->FinishedSnapshotWrites(false)) {
+ return false;
+ }
+ return sm_->BootFromSnapshotsWithoutSlotSwitch();
+ }
+
+ LOG(ERROR) << "Snapshot copy failed";
+ return false;
+}
+
+bool MapSnapshots::UnmapCowImagePath(std::string& name) {
+ return sm_->UnmapCowImage(name);
+}
+
+bool MapSnapshots::DeleteSnapshots() {
+ lock_ = sm_->LockExclusive();
+ if (!sm_->RemoveAllUpdateState(lock_.get())) {
+ LOG(ERROR) << "Remove All Update State failed";
+ return false;
+ }
+ return true;
+}
+
bool DumpCmdHandler(int /*argc*/, char** argv) {
android::base::InitLogging(argv, &android::base::StderrLogger);
return SnapshotManager::New()->Dump(std::cout);
@@ -85,6 +296,134 @@
return false;
}
+bool GetVerityPartitions(std::vector<std::string>& partitions) {
+ auto& dm = android::dm::DeviceMapper::Instance();
+ auto dm_block_devices = dm.FindDmPartitions();
+ if (dm_block_devices.empty()) {
+ LOG(ERROR) << "No dm-enabled block device is found.";
+ return false;
+ }
+
+ for (auto& block_device : dm_block_devices) {
+ std::string dm_block_name = block_device.first;
+ std::string slot_suffix = fs_mgr_get_slot_suffix();
+ std::string partition = dm_block_name + slot_suffix;
+ partitions.push_back(partition);
+ }
+ return true;
+}
+
+bool UnMapPrecreatedSnapshots(int, char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+ // Make sure we are root.
+ if (::getuid() != 0) {
+ LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+ return EXIT_FAILURE;
+ }
+
+ std::vector<std::string> partitions;
+ if (!GetVerityPartitions(partitions)) {
+ return false;
+ }
+
+ MapSnapshots snapshot;
+ for (auto partition : partitions) {
+ if (!snapshot.UnmapCowImagePath(partition)) {
+ LOG(ERROR) << "UnmapCowImagePath failed: " << partition;
+ }
+ }
+ return true;
+}
+
+bool RemovePrecreatedSnapshots(int, char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+ // Make sure we are root.
+ if (::getuid() != 0) {
+ LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+ return false;
+ }
+
+ MapSnapshots snapshot;
+ if (!snapshot.CleanupSnapshot()) {
+ LOG(ERROR) << "CleanupSnapshot failed";
+ return false;
+ }
+ return true;
+}
+
+bool DeletePrecreatedSnapshots(int, char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+ // Make sure we are root.
+ if (::getuid() != 0) {
+ LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+ return EXIT_FAILURE;
+ }
+
+ MapSnapshots snapshot;
+ return snapshot.DeleteSnapshots();
+}
+
+bool MapPrecreatedSnapshots(int argc, char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+
+ // Make sure we are root.
+ if (::getuid() != 0) {
+ LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+ return EXIT_FAILURE;
+ }
+
+ if (argc < 3) {
+ std::cerr << " map-snapshots <directory location where snapshot patches are present>"
+ " Map all snapshots based on patches present in the directory\n";
+ return false;
+ }
+
+ std::string path = std::string(argv[2]);
+ std::vector<std::string> patchfiles;
+
+ for (const auto& entry : std::filesystem::directory_iterator(path)) {
+ if (android::base::EndsWith(entry.path().generic_string(), ".patch")) {
+ patchfiles.push_back(android::base::Basename(entry.path().generic_string()));
+ }
+ }
+ auto& dm = android::dm::DeviceMapper::Instance();
+ auto dm_block_devices = dm.FindDmPartitions();
+ if (dm_block_devices.empty()) {
+ LOG(ERROR) << "No dm-enabled block device is found.";
+ return false;
+ }
+
+ std::vector<std::pair<std::string, std::string>> partitions;
+ for (auto& patchfile : patchfiles) {
+ auto npos = patchfile.rfind(".patch");
+ auto dm_block_name = patchfile.substr(0, npos);
+ if (dm_block_devices.find(dm_block_name) != dm_block_devices.end()) {
+ std::string slot_suffix = fs_mgr_get_slot_suffix();
+ std::string partition = dm_block_name + slot_suffix;
+ partitions.push_back(std::make_pair(partition, patchfile));
+ }
+ }
+
+ MapSnapshots cow(path);
+ if (!cow.BeginUpdate()) {
+ LOG(ERROR) << "BeginUpdate failed";
+ return false;
+ }
+
+ for (auto& pair : partitions) {
+ if (!cow.CreateSnapshotDevice(pair.first, pair.second)) {
+ LOG(ERROR) << "CreateSnapshotDevice failed for: " << pair.first;
+ return false;
+ }
+ if (!cow.InitiateThreadedSnapshotWrite(pair.first, pair.second)) {
+ LOG(ERROR) << "InitiateThreadedSnapshotWrite failed for: " << pair.first;
+ return false;
+ }
+ }
+
+ return cow.FinishSnapshotWrites();
+}
+
#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
bool CreateTestUpdate(SnapshotManager* sm) {
chromeos_update_engine::DeltaArchiveManifest manifest;
@@ -137,8 +476,8 @@
.block_device = fs_mgr_get_super_partition_name(target_slot_number),
.metadata_slot = {target_slot_number},
.partition_name = system_target_name,
- .partition_opener = &opener,
.timeout_ms = 10s,
+ .partition_opener = &opener,
};
auto writer = sm->OpenSnapshotWriter(clpp, std::nullopt);
if (!writer) {
@@ -211,6 +550,10 @@
{"test-blank-ota", TestOtaHandler},
#endif
{"unmap", UnmapCmdHandler},
+ {"map-snapshots", MapPrecreatedSnapshots},
+ {"unmap-snapshots", UnMapPrecreatedSnapshots},
+ {"delete-snapshots", DeletePrecreatedSnapshots},
+ {"revert-snapshots", RemovePrecreatedSnapshots},
// clang-format on
};
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index 9fe567a..1b0c563 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -19,7 +19,7 @@
}
cc_defaults {
- name: "libsnapshot_snapuserd_defaults",
+ name: "libsnapuserd_client_defaults",
defaults: [
"fs_mgr_defaults",
],
@@ -33,15 +33,15 @@
}
cc_library_static {
- name: "libsnapshot_snapuserd",
+ name: "libsnapuserd_client",
defaults: [
"fs_mgr_defaults",
- "libsnapshot_snapuserd_defaults",
+ "libsnapuserd_client_defaults",
],
recovery_available: true,
static_libs: [
"libcutils_sockets",
- "libfs_mgr",
+ "libfs_mgr_file_wait",
],
shared_libs: [
"libbase",
@@ -57,33 +57,45 @@
defaults: [
"fs_mgr_defaults",
],
+ local_include_dirs: ["include/"],
srcs: [
"dm-snapshot-merge/snapuserd.cpp",
- "dm-snapshot-merge/snapuserd_worker.cpp",
"dm-snapshot-merge/snapuserd_readahead.cpp",
+ "dm-snapshot-merge/snapuserd_worker.cpp",
+ "dm_user_block_server.cpp",
"snapuserd_buffer.cpp",
"user-space-merge/handler_manager.cpp",
+ "user-space-merge/merge_worker.cpp",
+ "user-space-merge/read_worker.cpp",
"user-space-merge/snapuserd_core.cpp",
- "user-space-merge/snapuserd_dm_user.cpp",
- "user-space-merge/snapuserd_merge.cpp",
"user-space-merge/snapuserd_readahead.cpp",
"user-space-merge/snapuserd_transitions.cpp",
"user-space-merge/snapuserd_verify.cpp",
+ "user-space-merge/worker.cpp",
+ "utility.cpp",
+ ],
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
],
static_libs: [
"libbase",
"libdm",
+ "libext2_uuid",
"libext4_utils",
"libsnapshot_cow",
"liburing",
],
include_dirs: ["bionic/libc/kernel"],
+ export_include_dirs: ["include"],
header_libs: [
"libstorage_literals_headers",
],
ramdisk_available: true,
vendor_ramdisk_available: true,
recovery_available: true,
+ host_supported: true,
}
cc_defaults {
@@ -97,17 +109,24 @@
"user-space-merge/snapuserd_server.cpp",
],
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
+ ],
+
static_libs: [
"libbase",
"libbrotli",
"libcutils_sockets",
"libdm",
- "libfs_mgr",
+ "libext2_uuid",
+ "libfs_mgr_file_wait",
"libgflags",
"liblog",
"libsnapshot_cow",
- "libsnapshot_snapuserd",
"libsnapuserd",
+ "libsnapuserd_client",
"libz",
"liblz4",
"libext4_utils",
@@ -142,6 +161,9 @@
init_rc: [
"snapuserd.rc",
],
+ static_libs: [
+ "libsnapuserd_client",
+ ],
ramdisk_available: false,
vendor_ramdisk_available: true,
}
@@ -184,12 +206,13 @@
"libbrotli",
"libgtest",
"libsnapshot_cow",
- "libsnapshot_snapuserd",
+ "libsnapuserd_client",
"libcutils_sockets",
"libz",
- "libfs_mgr",
"libdm",
+ "libext2_uuid",
"libext4_utils",
+ "libfs_mgr_file_wait",
],
header_libs: [
"libstorage_literals_headers",
@@ -202,15 +225,23 @@
require_root: false,
}
-cc_test {
- name: "snapuserd_test",
+cc_defaults {
+ name: "snapuserd_test_defaults",
defaults: [
"fs_mgr_defaults",
"libsnapshot_cow_defaults",
],
srcs: [
+ "testing/dm_user_harness.cpp",
+ "testing/harness.cpp",
+ "testing/host_harness.cpp",
"user-space-merge/snapuserd_test.cpp",
],
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libbase",
"liblog",
@@ -219,17 +250,20 @@
"libbrotli",
"libcutils_sockets",
"libdm",
+ "libext2_uuid",
"libext4_utils",
- "libfs_mgr",
+ "libfs_mgr_file_wait",
"libgflags",
"libgtest",
"libsnapshot_cow",
- "libsnapshot_snapuserd",
"libsnapuserd",
"liburing",
"libz",
],
- include_dirs: ["bionic/libc/kernel"],
+ include_dirs: [
+ "bionic/libc/kernel",
+ ".",
+ ],
header_libs: [
"libstorage_literals_headers",
"libfiemap_headers",
@@ -238,5 +272,69 @@
min_shipping_api_level: 30,
},
auto_gen_config: true,
- require_root: false,
+ require_root: true,
+ compile_multilib: "first",
+}
+
+cc_test {
+ name: "snapuserd_test",
+ defaults: ["snapuserd_test_defaults"],
+ host_supported: true,
+ test_suites: [
+ "device-tests",
+ ],
+}
+
+// vts tests cannot be host_supported.
+cc_test {
+ name: "vts_snapuserd_test",
+ defaults: ["snapuserd_test_defaults"],
+ test_suites: [
+ "vts",
+ ],
+}
+
+cc_binary_host {
+ name: "snapuserd_extractor",
+ defaults: [
+ "fs_mgr_defaults",
+ "libsnapshot_cow_defaults",
+ ],
+ srcs: [
+ "testing/dm_user_harness.cpp",
+ "testing/harness.cpp",
+ "testing/host_harness.cpp",
+ "user-space-merge/extractor.cpp",
+ "snapuserd_extractor.cpp",
+ ],
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ static_libs: [
+ "libbrotli",
+ "libcutils_sockets",
+ "libdm",
+ "libext2_uuid",
+ "libext4_utils",
+ "libfs_mgr_file_wait",
+ "libgflags",
+ "libsnapshot_cow",
+ "libsnapuserd",
+ "liburing",
+ "libz",
+ ],
+ include_dirs: [
+ "bionic/libc/kernel",
+ ".",
+ ],
+ header_libs: [
+ "libstorage_literals_headers",
+ "libfiemap_headers",
+ ],
}
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
index 978a7f2..6dc082e 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
@@ -508,7 +508,7 @@
// the merge of operations are done based on the ops present
// in the file.
//===========================================================
- uint64_t block_source = GetCowOpSourceInfoData(cow_op);
+ uint64_t block_source = GetCowOpSourceInfoData(*cow_op);
if (prev_id.has_value()) {
if (dest_blocks.count(cow_op->new_block) || source_blocks.count(block_source)) {
break;
@@ -734,8 +734,8 @@
off_t offset = 0;
for (int i = 0; i < num_threads; i++) {
- std::async(std::launch::async, &Snapuserd::ReadBlocksToCache, this, dm_block_device,
- partition_name, offset, read_sz_per_thread);
+ (void)std::async(std::launch::async, &Snapuserd::ReadBlocksToCache, this, dm_block_device,
+ partition_name, offset, read_sz_per_thread);
offset += read_sz_per_thread;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
index d5fbe91..ab0b309 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
@@ -172,7 +172,7 @@
}
void ReadAheadThread::CheckOverlap(const CowOperation* cow_op) {
- uint64_t source_block = GetCowOpSourceInfoData(cow_op);
+ uint64_t source_block = GetCowOpSourceInfoData(*cow_op);
if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block)) {
overlap_ = true;
}
@@ -191,7 +191,7 @@
// Get the first block with offset
const CowOperation* cow_op = GetRAOpIter();
CHECK_NE(cow_op, nullptr);
- *source_offset = GetCowOpSourceInfoData(cow_op);
+ *source_offset = GetCowOpSourceInfoData(*cow_op);
if (cow_op->type == kCowCopyOp) {
*source_offset *= BLOCK_SZ;
}
@@ -210,7 +210,7 @@
while (!RAIterDone() && num_ops) {
const CowOperation* op = GetRAOpIter();
CHECK_NE(op, nullptr);
- uint64_t next_offset = GetCowOpSourceInfoData(op);
+ uint64_t next_offset = GetCowOpSourceInfoData(*op);
if (op->type == kCowCopyOp) {
next_offset *= BLOCK_SZ;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
index 559dcc7..571b352 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
@@ -103,7 +103,7 @@
ssize_t rv = reader_->ReadData(cow_op, buffer, BLOCK_SZ);
if (rv != BLOCK_SZ) {
SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block
- << ", return = " << rv;
+ << ", return = " << rv << ", COW operation = " << *cow_op;
return false;
}
return true;
diff --git a/fs_mgr/libsnapshot/snapuserd/dm_user_block_server.cpp b/fs_mgr/libsnapshot/snapuserd/dm_user_block_server.cpp
new file mode 100644
index 0000000..e988335
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/dm_user_block_server.cpp
@@ -0,0 +1,152 @@
+// Copyright (C) 2023 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 <snapuserd/dm_user_block_server.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <snapuserd/snapuserd_kernel.h>
+#include "snapuserd_logging.h"
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+
+DmUserBlockServer::DmUserBlockServer(const std::string& misc_name, unique_fd&& ctrl_fd,
+ Delegate* delegate, size_t buffer_size)
+ : misc_name_(misc_name), ctrl_fd_(std::move(ctrl_fd)), delegate_(delegate) {
+ buffer_.Initialize(buffer_size);
+}
+
+bool DmUserBlockServer::ProcessRequests() {
+ struct dm_user_header* header = buffer_.GetHeaderPtr();
+ if (!android::base::ReadFully(ctrl_fd_, header, sizeof(*header))) {
+ if (errno != ENOTBLK) {
+ SNAP_PLOG(ERROR) << "Control-read failed";
+ }
+
+ SNAP_PLOG(DEBUG) << "ReadDmUserHeader failed....";
+ return false;
+ }
+
+ SNAP_LOG(DEBUG) << "Daemon: msg->seq: " << std::dec << header->seq;
+ SNAP_LOG(DEBUG) << "Daemon: msg->len: " << std::dec << header->len;
+ SNAP_LOG(DEBUG) << "Daemon: msg->sector: " << std::dec << header->sector;
+ SNAP_LOG(DEBUG) << "Daemon: msg->type: " << std::dec << header->type;
+ SNAP_LOG(DEBUG) << "Daemon: msg->flags: " << std::dec << header->flags;
+
+ if (!ProcessRequest(header)) {
+ if (header->type != DM_USER_RESP_ERROR) {
+ SendError();
+ }
+ return false;
+ }
+ return true;
+}
+
+bool DmUserBlockServer::ProcessRequest(dm_user_header* header) {
+ // Use the same header buffer as the response header.
+ int request_type = header->type;
+ header->type = DM_USER_RESP_SUCCESS;
+ header_response_ = true;
+
+ // Reset the output buffer.
+ buffer_.ResetBufferOffset();
+
+ switch (request_type) {
+ case DM_USER_REQ_MAP_READ:
+ return delegate_->RequestSectors(header->sector, header->len);
+
+ case DM_USER_REQ_MAP_WRITE:
+ // We should not get any write request to dm-user as we mount all
+ // partitions as read-only.
+ SNAP_LOG(ERROR) << "Unexpected write request from dm-user";
+ return false;
+
+ default:
+ SNAP_LOG(ERROR) << "Unexpected request from dm-user: " << request_type;
+ return false;
+ }
+}
+
+void* DmUserBlockServer::GetResponseBuffer(size_t size, size_t to_write) {
+ return buffer_.AcquireBuffer(size, to_write);
+}
+
+bool DmUserBlockServer::SendBufferedIo() {
+ return WriteDmUserPayload(buffer_.GetPayloadBytesWritten());
+}
+
+void DmUserBlockServer::SendError() {
+ struct dm_user_header* header = buffer_.GetHeaderPtr();
+ header->type = DM_USER_RESP_ERROR;
+ // This is an issue with the dm-user interface. There
+ // is no way to propagate the I/O error back to dm-user
+ // if we have already communicated the header back. Header
+ // is responded once at the beginning; however I/O can
+ // be processed in chunks. If we encounter an I/O error
+ // somewhere in the middle of the processing, we can't communicate
+ // this back to dm-user.
+ //
+ // TODO: Fix the interface
+ CHECK(header_response_);
+
+ WriteDmUserPayload(0);
+}
+
+bool DmUserBlockServer::WriteDmUserPayload(size_t size) {
+ size_t payload_size = size;
+ void* buf = buffer_.GetPayloadBufPtr();
+ if (header_response_) {
+ payload_size += sizeof(struct dm_user_header);
+ buf = buffer_.GetBufPtr();
+ }
+
+ if (!android::base::WriteFully(ctrl_fd_, buf, payload_size)) {
+ SNAP_PLOG(ERROR) << "Write to dm-user failed size: " << payload_size;
+ return false;
+ }
+
+ // After the first header is sent in response to a request, we cannot
+ // send any additional headers.
+ header_response_ = false;
+
+ // Reset the buffer for use by the next request.
+ buffer_.ResetBufferOffset();
+ return true;
+}
+
+DmUserBlockServerOpener::DmUserBlockServerOpener(const std::string& misc_name,
+ const std::string& dm_user_path)
+ : misc_name_(misc_name), dm_user_path_(dm_user_path) {}
+
+std::unique_ptr<IBlockServer> DmUserBlockServerOpener::Open(IBlockServer::Delegate* delegate,
+ size_t buffer_size) {
+ unique_fd fd(open(dm_user_path_.c_str(), O_RDWR | O_CLOEXEC));
+ if (fd < 0) {
+ SNAP_PLOG(ERROR) << "Could not open dm-user path: " << dm_user_path_;
+ return nullptr;
+ }
+ return std::make_unique<DmUserBlockServer>(misc_name_, std::move(fd), delegate, buffer_size);
+}
+
+std::shared_ptr<IBlockServerOpener> DmUserBlockServerFactory::CreateOpener(
+ const std::string& misc_name) {
+ auto dm_path = "/dev/dm-user/" + misc_name;
+ return std::make_shared<DmUserBlockServerOpener>(misc_name, dm_path);
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/block_server.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/block_server.h
new file mode 100644
index 0000000..406bf11
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/block_server.h
@@ -0,0 +1,95 @@
+// Copyright (C) 2023 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 <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+namespace android {
+namespace snapshot {
+
+// These interfaces model the block device driver component of snapuserd (eg,
+// dm-user).
+
+// An open connection to a userspace block device control
+class IBlockServer {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Respond to a request for reading a contiguous run of sectors. This
+ // call should be followed by calls to GetResponseBuffer/CommitBuffer
+ // until the |size| is fulfilled.
+ //
+ // If false is returned, an error will be automatically reported unless
+ // SendError was called.
+ virtual bool RequestSectors(uint64_t sector, uint64_t size) = 0;
+ };
+
+ virtual ~IBlockServer() {}
+
+ // Process I/O requests. This can block the worker thread until either a
+ // request is available or the underlying connection has been destroyed.
+ //
+ // True indicates that one or more requests was processed. False indicates
+ // an unrecoverable condition and processing should stop.
+ virtual bool ProcessRequests() = 0;
+
+ // Return a buffer for fulfilling a RequestSectors request. This buffer
+ // is valid until calling SendBufferedIo. This cannot be called outside
+ // of RequestSectors().
+ //
+ // "to_write" must be <= "size". If it is < size, the excess bytes are
+ // available for writing, but will not be send via SendBufferedIo, and
+ // may be reallocated in the next call to GetResponseBuffer.
+ //
+ // All buffers returned are invalidated after SendBufferedIo or returning
+ // control from RequestSectors.
+ virtual void* GetResponseBuffer(size_t size, size_t to_write) = 0;
+
+ // Send all outstanding buffers to the driver, in order. This should
+ // be called at least once in response to RequestSectors. This returns
+ // ownership of any buffers returned by GetResponseBuffer.
+ //
+ // If false is returned, an error is automatically reported to the driver.
+ virtual bool SendBufferedIo() = 0;
+
+ void* GetResponseBuffer(size_t size) { return GetResponseBuffer(size, size); }
+};
+
+class IBlockServerOpener {
+ public:
+ virtual ~IBlockServerOpener() = default;
+
+ // Open a connection to the service. This is called on the daemon thread.
+ //
+ // buffer_size is the maximum amount of buffered I/O to use.
+ virtual std::unique_ptr<IBlockServer> Open(IBlockServer::Delegate* delegate,
+ size_t buffer_size) = 0;
+};
+
+class IBlockServerFactory {
+ public:
+ virtual ~IBlockServerFactory() {}
+
+ // Return a new IBlockServerOpener given a unique device name.
+ virtual std::shared_ptr<IBlockServerOpener> CreateOpener(const std::string& misc_name) = 0;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/dm_user_block_server.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/dm_user_block_server.h
new file mode 100644
index 0000000..f1f8da1
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/dm_user_block_server.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 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 <android-base/unique_fd.h>
+
+#include <string>
+
+#include <snapuserd/block_server.h>
+#include <snapuserd/snapuserd_buffer.h>
+
+namespace android {
+namespace snapshot {
+
+class DmUserBlockServer : public IBlockServer {
+ public:
+ DmUserBlockServer(const std::string& misc_name, android::base::unique_fd&& ctrl_fd,
+ Delegate* delegate, size_t buffer_size);
+
+ bool ProcessRequests() override;
+ void* GetResponseBuffer(size_t size, size_t to_write) override;
+ bool SendBufferedIo() override;
+ void SendError();
+
+ private:
+ bool ProcessRequest(dm_user_header* header);
+ bool WriteDmUserPayload(size_t size);
+
+ std::string misc_name_;
+ android::base::unique_fd ctrl_fd_;
+ Delegate* delegate_;
+
+ // Per-request state.
+ BufferSink buffer_;
+ bool header_response_ = false;
+};
+
+class DmUserBlockServerOpener : public IBlockServerOpener {
+ public:
+ DmUserBlockServerOpener(const std::string& misc_name, const std::string& dm_user_path);
+
+ std::unique_ptr<IBlockServer> Open(IBlockServer::Delegate* delegate,
+ size_t buffer_size) override;
+
+ private:
+ std::string misc_name_;
+ std::string dm_user_path_;
+};
+
+class DmUserBlockServerFactory : public IBlockServerFactory {
+ public:
+ std::shared_ptr<IBlockServerOpener> CreateOpener(const std::string& misc_name) override;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
index a6b6a7f..c5ca2b1 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
@@ -36,6 +36,21 @@
struct dm_user_header* GetHeaderPtr();
void ResetBufferOffset() { buffer_offset_ = 0; }
void* GetPayloadBufPtr();
+ loff_t GetPayloadBytesWritten() { return buffer_offset_; }
+
+ // Same as calling GetPayloadBuffer and then UpdateBufferOffset.
+ //
+ // This is preferred over GetPayloadBuffer as it does not require a
+ // separate call to UpdateBufferOffset.
+ void* AcquireBuffer(size_t size) { return AcquireBuffer(size, size); }
+
+ // Same as AcquireBuffer, but separates the requested size from the buffer
+ // offset. This is useful for a situation where a full run of data will be
+ // read, but only a partial amount will be returned.
+ //
+ // If size != to_write, the excess bytes may be reallocated by the next
+ // call to AcquireBuffer.
+ void* AcquireBuffer(size_t size, size_t to_write);
private:
std::unique_ptr<uint8_t[]> buffer_;
@@ -43,19 +58,5 @@
size_t buffer_size_;
};
-class XorSink final {
- public:
- void Initialize(BufferSink* sink, size_t size);
- void Reset();
- void* GetBuffer(size_t requested, size_t* actual);
- bool ReturnData(void* buffer, size_t len);
-
- private:
- BufferSink* bufsink_;
- std::unique_ptr<uint8_t[]> buffer_;
- size_t buffer_size_;
- size_t returned_;
-};
-
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
index 46952c4..7ab75dc 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
@@ -14,6 +14,8 @@
#pragma once
+#include <linux/types.h>
+
namespace android {
namespace snapshot {
@@ -40,6 +42,7 @@
* multiple of 512 bytes. Hence these two constants.
*/
static constexpr uint32_t SECTOR_SHIFT = 9;
+static constexpr uint64_t SECTOR_SIZE = (1ULL << SECTOR_SHIFT);
static constexpr size_t BLOCK_SZ = 4096;
static constexpr size_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SZ) - 1);
@@ -69,7 +72,7 @@
/* In sectors */
uint32_t chunk_size;
-} __packed;
+} __attribute__((packed));
// A disk exception is a mapping of old_chunk to new_chunk
// old_chunk is the chunk ID of a dm-snapshot device.
@@ -77,7 +80,7 @@
struct disk_exception {
uint64_t old_chunk;
uint64_t new_chunk;
-} __packed;
+} __attribute__((packed));
// Control structures to communicate with dm-user
// It comprises of header and a payload
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
index ab763ab..490c0e6 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
@@ -15,22 +15,36 @@
*/
#include <snapuserd/snapuserd_buffer.h>
+
+#include <android-base/logging.h>
#include <snapuserd/snapuserd_kernel.h>
namespace android {
namespace snapshot {
void BufferSink::Initialize(size_t size) {
- buffer_size_ = size;
+ buffer_size_ = size + sizeof(struct dm_user_header);
buffer_offset_ = 0;
- buffer_ = std::make_unique<uint8_t[]>(size);
+ buffer_ = std::make_unique<uint8_t[]>(buffer_size_);
+}
+
+void* BufferSink::AcquireBuffer(size_t size, size_t to_write) {
+ CHECK(to_write <= size);
+
+ void* ptr = GetPayloadBuffer(size);
+ if (!ptr) {
+ return nullptr;
+ }
+ UpdateBufferOffset(to_write);
+ return ptr;
}
void* BufferSink::GetPayloadBuffer(size_t size) {
- if ((buffer_size_ - buffer_offset_) < size) return nullptr;
-
char* buffer = reinterpret_cast<char*>(GetBufPtr());
struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
+ if ((buffer_size_ - buffer_offset_ - sizeof(msg->header)) < size) {
+ return nullptr;
+ }
return (char*)msg->payload.buf + buffer_offset_;
}
@@ -59,38 +73,5 @@
return msg->payload.buf;
}
-void XorSink::Initialize(BufferSink* sink, size_t size) {
- bufsink_ = sink;
- buffer_size_ = size;
- returned_ = 0;
- buffer_ = std::make_unique<uint8_t[]>(size);
-}
-
-void XorSink::Reset() {
- returned_ = 0;
-}
-
-void* XorSink::GetBuffer(size_t requested, size_t* actual) {
- if (requested > buffer_size_) {
- *actual = buffer_size_;
- } else {
- *actual = requested;
- }
- return buffer_.get();
-}
-
-bool XorSink::ReturnData(void* buffer, size_t len) {
- uint8_t* xor_data = reinterpret_cast<uint8_t*>(buffer);
- uint8_t* buff = reinterpret_cast<uint8_t*>(bufsink_->GetPayloadBuffer(len + returned_));
- if (buff == nullptr) {
- return false;
- }
- for (size_t i = 0; i < len; i++) {
- buff[returned_ + i] ^= xor_data[i];
- }
- returned_ += len;
- return true;
-}
-
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_extractor.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_extractor.cpp
new file mode 100644
index 0000000..f46cd5b
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_extractor.cpp
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 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 <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gflags/gflags.h>
+#include "user-space-merge/extractor.h"
+
+using namespace std::string_literals;
+
+DEFINE_string(base, "", "Base device/image");
+DEFINE_string(cow, "", "COW device/image");
+DEFINE_string(out, "", "Output path");
+DEFINE_int32(num_sectors, 0, "Number of sectors to read");
+
+int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+ android::base::InitLogging(argv);
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+ if (FLAGS_out.empty()) {
+ LOG(ERROR) << "Missing -out argument.";
+ return 1;
+ }
+ if (FLAGS_base.empty()) {
+ LOG(ERROR) << "Missing -base argument.";
+ return 1;
+ }
+ if (FLAGS_cow.empty()) {
+ LOG(ERROR) << "missing -out argument.";
+ return 1;
+ }
+ if (!FLAGS_num_sectors) {
+ LOG(ERROR) << "missing -num_sectors argument.";
+ return 1;
+ }
+
+ android::snapshot::Extractor extractor(FLAGS_base, FLAGS_cow);
+ if (!extractor.Init()) {
+ return 1;
+ }
+ if (!extractor.Extract(FLAGS_num_sectors, FLAGS_out)) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_logging.h b/fs_mgr/libsnapshot/snapuserd/snapuserd_logging.h
new file mode 100644
index 0000000..bc470ce
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_logging.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 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 <android-base/logging.h>
+
+#define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
+#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.cpp b/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.cpp
new file mode 100644
index 0000000..7cadf25
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.cpp
@@ -0,0 +1,67 @@
+// Copyright (C) 2023 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 "dm_user_harness.h"
+
+#include <fcntl.h>
+
+#include <android-base/file.h>
+#include <fs_mgr/file_wait.h>
+#include <libdm/dm.h>
+#include <snapuserd/dm_user_block_server.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace std::chrono_literals;
+using android::base::unique_fd;
+
+DmUserDevice::DmUserDevice(std::unique_ptr<Tempdevice>&& dev) : dev_(std::move(dev)) {}
+
+const std::string& DmUserDevice::GetPath() {
+ return dev_->path();
+}
+
+bool DmUserDevice::Destroy() {
+ return dev_->Destroy();
+}
+
+DmUserTestHarness::DmUserTestHarness() {
+ block_server_factory_ = std::make_unique<DmUserBlockServerFactory>();
+}
+
+std::unique_ptr<IUserDevice> DmUserTestHarness::CreateUserDevice(const std::string& dev_name,
+ const std::string& misc_name,
+ uint64_t num_sectors) {
+ android::dm::DmTable dmuser_table;
+ dmuser_table.Emplace<android::dm::DmTargetUser>(0, num_sectors, misc_name);
+ auto dev = std::make_unique<Tempdevice>(dev_name, dmuser_table);
+ if (!dev->valid()) {
+ return nullptr;
+ }
+
+ auto misc_device = "/dev/dm-user/" + misc_name;
+ if (!android::fs_mgr::WaitForFile(misc_device, 10s)) {
+ return nullptr;
+ }
+
+ return std::make_unique<DmUserDevice>(std::move(dev));
+}
+
+IBlockServerFactory* DmUserTestHarness::GetBlockServerFactory() {
+ return block_server_factory_.get();
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.h b/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.h
new file mode 100644
index 0000000..cf26bed
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2023 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 <android-base/unique_fd.h>
+
+#include "harness.h"
+#include "temp_device.h"
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+
+class DmUserBlockServerFactory;
+
+class DmUserDevice final : public IUserDevice {
+ public:
+ explicit DmUserDevice(std::unique_ptr<Tempdevice>&& dev);
+ const std::string& GetPath() override;
+ bool Destroy() override;
+
+ private:
+ std::unique_ptr<Tempdevice> dev_;
+};
+
+class DmUserTestHarness final : public ITestHarness {
+ public:
+ DmUserTestHarness();
+
+ std::unique_ptr<IUserDevice> CreateUserDevice(const std::string& dev_name,
+ const std::string& misc_name,
+ uint64_t num_sectors) override;
+ IBlockServerFactory* GetBlockServerFactory() override;
+ bool HasUserDevice() override { return true; }
+
+ private:
+ std::unique_ptr<DmUserBlockServerFactory> block_server_factory_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/harness.cpp b/fs_mgr/libsnapshot/snapuserd/testing/harness.cpp
new file mode 100644
index 0000000..02ae549
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/harness.cpp
@@ -0,0 +1,116 @@
+// Copyright (C) 2023 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 "harness.h"
+
+#ifdef __ANDROID__
+#include <linux/memfd.h>
+#endif
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <ext4_utils/ext4_utils.h>
+#include <libdm/loop_control.h>
+#include "snapuserd_logging.h"
+
+namespace android {
+namespace snapshot {
+
+using namespace std::chrono_literals;
+using android::base::unique_fd;
+using android::dm::LoopDevice;
+
+#ifdef __ANDROID__
+// Prefer this on device since it is a real block device, which is more similar
+// to how we use snapuserd.
+class MemoryBackedDevice final : public IBackingDevice {
+ public:
+ bool Init(uint64_t size) {
+ memfd_.reset(memfd_create("snapuserd_test", MFD_ALLOW_SEALING));
+ if (memfd_ < 0) {
+ PLOG(ERROR) << "memfd_create failed";
+ return false;
+ }
+ if (ftruncate(memfd_.get(), size) < 0) {
+ PLOG(ERROR) << "ftruncate failed";
+ return false;
+ }
+ if (fcntl(memfd_.get(), F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
+ PLOG(ERROR) << "fcntl seal failed";
+ return false;
+ }
+ dev_ = std::make_unique<LoopDevice>(memfd_, 10s);
+ return dev_->valid();
+ }
+ const std::string& GetPath() override { return dev_->device(); }
+ uint64_t GetSize() override {
+ unique_fd fd(open(GetPath().c_str(), O_RDONLY | O_CLOEXEC));
+ if (fd < 0) {
+ PLOG(ERROR) << "open failed: " << GetPath();
+ return 0;
+ }
+ return get_block_device_size(fd.get());
+ }
+
+ private:
+ unique_fd memfd_;
+ std::unique_ptr<LoopDevice> dev_;
+};
+#endif
+
+class FileBackedDevice final : public IBackingDevice {
+ public:
+ bool Init(uint64_t size) {
+ if (temp_.fd < 0) {
+ return false;
+ }
+ if (ftruncate(temp_.fd, size) < 0) {
+ PLOG(ERROR) << "ftruncate failed: " << temp_.path;
+ return false;
+ }
+ path_ = temp_.path;
+ return true;
+ }
+
+ const std::string& GetPath() override { return path_; }
+ uint64_t GetSize() override {
+ off_t off = lseek(temp_.fd, 0, SEEK_END);
+ if (off < 0) {
+ PLOG(ERROR) << "lseek failed: " << temp_.path;
+ return 0;
+ }
+ return off;
+ }
+
+ private:
+ TemporaryFile temp_;
+ std::string path_;
+};
+
+std::unique_ptr<IBackingDevice> ITestHarness::CreateBackingDevice(uint64_t size) {
+#ifdef __ANDROID__
+ auto dev = std::make_unique<MemoryBackedDevice>();
+#else
+ auto dev = std::make_unique<FileBackedDevice>();
+#endif
+ if (!dev->Init(size)) {
+ return nullptr;
+ }
+ return dev;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/harness.h b/fs_mgr/libsnapshot/snapuserd/testing/harness.h
new file mode 100644
index 0000000..ffe9f7b
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/harness.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2023 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 <stddef.h>
+#include <sys/types.h>
+
+#include <memory>
+
+#include <android-base/unique_fd.h>
+#include <snapuserd/block_server.h>
+
+namespace android {
+namespace snapshot {
+
+// Interface for a "block driver in userspace" device.
+class IUserDevice {
+ public:
+ virtual ~IUserDevice() {}
+ virtual const std::string& GetPath() = 0;
+ virtual bool Destroy() = 0;
+};
+
+// Interface for an fd/temp file that is a block device when possible.
+class IBackingDevice {
+ public:
+ virtual ~IBackingDevice() {}
+ virtual const std::string& GetPath() = 0;
+ virtual uint64_t GetSize() = 0;
+};
+
+class ITestHarness {
+ public:
+ virtual ~ITestHarness() {}
+ virtual std::unique_ptr<IUserDevice> CreateUserDevice(const std::string& dev_name,
+ const std::string& misc_name,
+ uint64_t num_sectors) = 0;
+ virtual IBlockServerFactory* GetBlockServerFactory() = 0;
+ virtual bool HasUserDevice() = 0;
+ virtual std::unique_ptr<IBackingDevice> CreateBackingDevice(uint64_t size);
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/host_harness.cpp b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.cpp
new file mode 100644
index 0000000..0d230ad
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.cpp
@@ -0,0 +1,112 @@
+// Copyright (C) 2023 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 "host_harness.h"
+
+#include "snapuserd_logging.h"
+
+namespace android {
+namespace snapshot {
+
+void TestBlockServerQueue::WaitForShutdown() {
+ std::unique_lock lock(m_);
+ if (shutdown_) {
+ return;
+ }
+ cv_.wait(lock, [this]() -> bool { return shutdown_; });
+}
+
+void TestBlockServerQueue::Shutdown() {
+ std::unique_lock lock(m_);
+ shutdown_ = true;
+ cv_.notify_all();
+}
+
+TestBlockServer::TestBlockServer(std::shared_ptr<TestBlockServerQueue> queue,
+ const std::string& misc_name)
+ : queue_(queue), misc_name_(misc_name) {}
+
+bool TestBlockServer::ProcessRequests() {
+ queue_->WaitForShutdown();
+ return false;
+}
+
+void* TestBlockServer::GetResponseBuffer(size_t size, size_t to_write) {
+ std::string buffer(size, '\0');
+ buffered_.emplace_back(std::move(buffer), to_write);
+ return buffered_.back().first.data();
+}
+
+bool TestBlockServer::SendBufferedIo() {
+ for (const auto& [data, to_write] : buffered_) {
+ sent_io_ += data.substr(0, to_write);
+ }
+ buffered_.clear();
+ return true;
+}
+
+TestBlockServerOpener::TestBlockServerOpener(std::shared_ptr<TestBlockServerQueue> queue,
+ const std::string& misc_name)
+ : queue_(queue), misc_name_(misc_name) {}
+
+std::unique_ptr<IBlockServer> TestBlockServerOpener::Open(IBlockServer::Delegate*, size_t) {
+ return std::make_unique<TestBlockServer>(queue_, misc_name_);
+}
+
+std::shared_ptr<TestBlockServerOpener> TestBlockServerFactory::CreateTestOpener(
+ const std::string& misc_name) {
+ if (queues_.count(misc_name)) {
+ LOG(ERROR) << "Cannot create opener for " << misc_name << ", already exists";
+ return nullptr;
+ }
+ auto queue = std::make_shared<TestBlockServerQueue>();
+ queues_.emplace(misc_name, queue);
+ return std::make_shared<TestBlockServerOpener>(queue, misc_name);
+}
+
+std::shared_ptr<IBlockServerOpener> TestBlockServerFactory::CreateOpener(
+ const std::string& misc_name) {
+ return CreateTestOpener(misc_name);
+}
+
+bool TestBlockServerFactory::DeleteQueue(const std::string& misc_name) {
+ auto iter = queues_.find(misc_name);
+ if (iter == queues_.end()) {
+ LOG(ERROR) << "Cannot delete queue " << misc_name << ", not found";
+ return false;
+ }
+ iter->second->Shutdown();
+ queues_.erase(iter);
+ return true;
+}
+
+HostUserDevice::HostUserDevice(TestBlockServerFactory* factory, const std::string& misc_name)
+ : factory_(factory), misc_name_(misc_name) {}
+
+bool HostUserDevice::Destroy() {
+ return factory_->DeleteQueue(misc_name_);
+}
+
+std::unique_ptr<IUserDevice> HostTestHarness::CreateUserDevice(const std::string&,
+ const std::string& misc_name,
+ uint64_t) {
+ return std::make_unique<HostUserDevice>(&factory_, misc_name);
+}
+
+IBlockServerFactory* HostTestHarness::GetBlockServerFactory() {
+ return &factory_;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/host_harness.h b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.h
new file mode 100644
index 0000000..ec0bd29
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.h
@@ -0,0 +1,105 @@
+// Copyright (C) 2023 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 <condition_variable>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "harness.h"
+
+namespace android {
+namespace snapshot {
+
+class TestBlockServerQueue final {
+ public:
+ void WaitForShutdown();
+ void Shutdown();
+
+ private:
+ std::mutex m_;
+ std::condition_variable cv_;
+ bool shutdown_ = false;
+};
+
+class TestBlockServer final : public IBlockServer {
+ public:
+ TestBlockServer(std::shared_ptr<TestBlockServerQueue> queue, const std::string& misc_name);
+ bool ProcessRequests() override;
+ void* GetResponseBuffer(size_t size, size_t to_write) override;
+ bool SendBufferedIo() override;
+
+ std::string&& sent_io() { return std::move(sent_io_); }
+
+ private:
+ std::shared_ptr<TestBlockServerQueue> queue_;
+ std::string misc_name_;
+ std::string sent_io_;
+ std::vector<std::pair<std::string, size_t>> buffered_;
+};
+
+class TestBlockServerOpener final : public IBlockServerOpener {
+ public:
+ TestBlockServerOpener(std::shared_ptr<TestBlockServerQueue> queue,
+ const std::string& misc_name);
+ std::unique_ptr<IBlockServer> Open(IBlockServer::Delegate* delegate,
+ size_t buffer_size) override;
+
+ private:
+ std::shared_ptr<TestBlockServerQueue> queue_;
+ std::string misc_name_;
+};
+
+class TestBlockServerFactory final : public IBlockServerFactory {
+ public:
+ std::shared_ptr<IBlockServerOpener> CreateOpener(const std::string& misc_name) override;
+ std::shared_ptr<TestBlockServerOpener> CreateTestOpener(const std::string& misc_name);
+ bool DeleteQueue(const std::string& misc_name);
+
+ private:
+ std::unordered_map<std::string, std::shared_ptr<TestBlockServerQueue>> queues_;
+};
+
+class TestBlockServerFactory;
+
+class HostUserDevice final : public IUserDevice {
+ public:
+ HostUserDevice(TestBlockServerFactory* factory, const std::string& misc_name);
+ const std::string& GetPath() override { return empty_path_; }
+ bool Destroy();
+
+ private:
+ TestBlockServerFactory* factory_;
+ std::string misc_name_;
+ std::string empty_path_;
+};
+
+class HostTestHarness final : public ITestHarness {
+ public:
+ std::unique_ptr<IUserDevice> CreateUserDevice(const std::string& dev_name,
+ const std::string& misc_name,
+ uint64_t num_sectors) override;
+ IBlockServerFactory* GetBlockServerFactory() override;
+ bool HasUserDevice() override { return false; }
+
+ private:
+ TestBlockServerFactory factory_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/temp_device.h b/fs_mgr/libsnapshot/snapuserd/testing/temp_device.h
new file mode 100644
index 0000000..2a6d3ea
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/temp_device.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2023 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 <chrono>
+#include <string>
+
+#include <libdm/dm.h>
+
+namespace android {
+namespace snapshot {
+
+using android::dm::DeviceMapper;
+using android::dm::DmTable;
+
+class Tempdevice {
+ public:
+ Tempdevice(const std::string& name, const DmTable& table)
+ : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
+ valid_ = dm_.CreateDevice(name, table, &path_, std::chrono::seconds(5));
+ }
+ Tempdevice(Tempdevice&& other) noexcept
+ : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
+ other.valid_ = false;
+ }
+ ~Tempdevice() {
+ if (valid_) {
+ dm_.DeleteDeviceIfExists(name_);
+ }
+ }
+ bool Destroy() {
+ if (!valid_) {
+ return true;
+ }
+ valid_ = false;
+ return dm_.DeleteDeviceIfExists(name_);
+ }
+ const std::string& path() const { return path_; }
+ const std::string& name() const { return name_; }
+ bool valid() const { return valid_; }
+
+ Tempdevice(const Tempdevice&) = delete;
+ Tempdevice& operator=(const Tempdevice&) = delete;
+
+ Tempdevice& operator=(Tempdevice&& other) noexcept {
+ name_ = other.name_;
+ valid_ = other.valid_;
+ other.valid_ = false;
+ return *this;
+ }
+
+ private:
+ DeviceMapper& dm_;
+ std::string name_;
+ std::string path_;
+ bool valid_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
new file mode 100644
index 0000000..c5718d5
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
@@ -0,0 +1,90 @@
+// Copyright (C) 2023 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 "extractor.h"
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+using namespace std::string_literals;
+
+namespace android {
+namespace snapshot {
+
+Extractor::Extractor(const std::string& base_path, const std::string& cow_path)
+ : base_path_(base_path), cow_path_(cow_path), control_name_("test") {}
+
+bool Extractor::Init() {
+ auto opener = factory_.CreateTestOpener(control_name_);
+ handler_ = std::make_shared<SnapshotHandler>(control_name_, cow_path_, base_path_, base_path_,
+ opener, 1, false, false);
+ if (!handler_->InitCowDevice()) {
+ return false;
+ }
+ if (!handler_->InitializeWorkers()) {
+ return false;
+ }
+
+ read_worker_ = std::make_unique<ReadWorker>(cow_path_, base_path_, control_name_, base_path_,
+ handler_->GetSharedPtr(), opener);
+ if (!read_worker_->Init()) {
+ return false;
+ }
+ block_server_ = static_cast<TestBlockServer*>(read_worker_->block_server());
+
+ handler_thread_ = std::async(std::launch::async, &SnapshotHandler::Start, handler_.get());
+ return true;
+}
+
+Extractor::~Extractor() {
+ factory_.DeleteQueue(control_name_);
+}
+
+bool Extractor::Extract(off_t num_sectors, const std::string& out_path) {
+ unique_fd out_fd(open(out_path.c_str(), O_RDWR | O_CLOEXEC | O_TRUNC | O_CREAT, 0664));
+ if (out_fd < 0) {
+ PLOG(ERROR) << "Could not open for writing: " << out_path;
+ return false;
+ }
+
+ for (off_t i = 0; i < num_sectors; i++) {
+ if (!read_worker_->RequestSectors(i, 512)) {
+ LOG(ERROR) << "Read sector " << i << " failed.";
+ return false;
+ }
+ std::string result = std::move(block_server_->sent_io());
+ off_t offset = i * 512;
+ if (!android::base::WriteFullyAtOffset(out_fd, result.data(), result.size(), offset)) {
+ PLOG(ERROR) << "write failed";
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.h
new file mode 100644
index 0000000..65285b1
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 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 <string>
+#include <thread>
+
+#include <android-base/unique_fd.h>
+#include "merge_worker.h"
+#include "read_worker.h"
+#include "snapuserd_core.h"
+#include "testing/host_harness.h"
+
+namespace android {
+namespace snapshot {
+
+class Extractor final {
+ public:
+ Extractor(const std::string& base_path, const std::string& cow_path);
+ ~Extractor();
+
+ bool Init();
+ bool Extract(off_t num_sectors, const std::string& out_path);
+
+ private:
+ std::string base_path_;
+ std::string cow_path_;
+
+ TestBlockServerFactory factory_;
+ HostTestHarness harness_;
+ std::string control_name_;
+ std::shared_ptr<SnapshotHandler> handler_;
+ std::unique_ptr<ReadWorker> read_worker_;
+ std::future<bool> handler_thread_;
+ TestBlockServer* block_server_ = nullptr;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
index bdba5c0..d979e20 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
@@ -14,10 +14,13 @@
#include "handler_manager.h"
+#include <pthread.h>
#include <sys/eventfd.h>
#include <android-base/logging.h>
+#include "merge_worker.h"
+#include "read_worker.h"
#include "snapuserd_core.h"
namespace android {
@@ -48,9 +51,10 @@
std::shared_ptr<HandlerThread> SnapshotHandlerManager::AddHandler(
const std::string& misc_name, const std::string& cow_device_path,
const std::string& backing_device, const std::string& base_path_merge,
- int num_worker_threads, bool use_iouring, bool perform_verification) {
+ std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads, bool use_iouring,
+ bool perform_verification) {
auto snapuserd = std::make_shared<SnapshotHandler>(misc_name, cow_device_path, backing_device,
- base_path_merge, num_worker_threads,
+ base_path_merge, opener, num_worker_threads,
use_iouring, perform_verification);
if (!snapuserd->InitCowDevice()) {
LOG(ERROR) << "Failed to initialize Snapuserd";
@@ -129,6 +133,8 @@
void SnapshotHandlerManager::RunThread(std::shared_ptr<HandlerThread> handler) {
LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
+ pthread_setname_np(pthread_self(), "Handler");
+
if (!handler->snapuserd()->Start()) {
LOG(ERROR) << " Failed to launch all worker threads";
}
@@ -198,9 +204,8 @@
handler->snapuserd()->MonitorMerge();
- if (!is_merge_monitor_started_) {
- std::thread(&SnapshotHandlerManager::MonitorMerge, this).detach();
- is_merge_monitor_started_ = true;
+ if (!merge_monitor_.joinable()) {
+ merge_monitor_ = std::thread(&SnapshotHandlerManager::MonitorMerge, this);
}
merge_handlers_.push(handler);
@@ -217,6 +222,7 @@
}
void SnapshotHandlerManager::MonitorMerge() {
+ pthread_setname_np(pthread_self(), "Merge Monitor");
while (!stop_monitor_merge_thread_) {
uint64_t testVal;
ssize_t ret =
@@ -354,8 +360,12 @@
if (th.joinable()) th.join();
}
- stop_monitor_merge_thread_ = true;
- WakeupMonitorMergeThread();
+ if (merge_monitor_.joinable()) {
+ stop_monitor_merge_thread_ = true;
+ WakeupMonitorMergeThread();
+
+ merge_monitor_.join();
+ }
}
auto SnapshotHandlerManager::FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
index b7ddac1..b1605f0 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
@@ -21,6 +21,7 @@
#include <vector>
#include <android-base/unique_fd.h>
+#include <snapuserd/block_server.h>
namespace android {
namespace snapshot {
@@ -55,6 +56,7 @@
const std::string& cow_device_path,
const std::string& backing_device,
const std::string& base_path_merge,
+ std::shared_ptr<IBlockServerOpener> opener,
int num_worker_threads, bool use_iouring,
bool perform_verification) = 0;
@@ -91,6 +93,7 @@
const std::string& cow_device_path,
const std::string& backing_device,
const std::string& base_path_merge,
+ std::shared_ptr<IBlockServerOpener> opener,
int num_worker_threads, bool use_iouring,
bool perform_verification) override;
bool StartHandler(const std::string& misc_name) override;
@@ -119,9 +122,9 @@
std::mutex lock_;
HandlerList dm_users_;
- bool is_merge_monitor_started_ = false;
bool stop_monitor_merge_thread_ = false;
int active_merge_threads_ = 0;
+ std::thread merge_monitor_;
int num_partitions_merge_complete_ = 0;
std::queue<std::shared_ptr<HandlerThread>> merge_handlers_;
android::base::unique_fd monitor_merge_event_fd_;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
similarity index 91%
rename from fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
rename to fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
index ce95b76..11b8d7c 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
@@ -13,8 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include "merge_worker.h"
+
+#include <pthread.h>
#include "snapuserd_core.h"
+#include "utility.h"
namespace android {
namespace snapshot {
@@ -23,8 +27,13 @@
using namespace android::dm;
using android::base::unique_fd;
-int Worker::PrepareMerge(uint64_t* source_offset, int* pending_ops,
- std::vector<const CowOperation*>* replace_zero_vec) {
+MergeWorker::MergeWorker(const std::string& cow_device, const std::string& misc_name,
+ const std::string& base_path_merge,
+ std::shared_ptr<SnapshotHandler> snapuserd)
+ : Worker(cow_device, misc_name, base_path_merge, snapuserd) {}
+
+int MergeWorker::PrepareMerge(uint64_t* source_offset, int* pending_ops,
+ std::vector<const CowOperation*>* replace_zero_vec) {
int num_ops = *pending_ops;
int nr_consecutive = 0;
bool checkOrderedOp = (replace_zero_vec == nullptr);
@@ -70,7 +79,7 @@
return nr_consecutive;
}
-bool Worker::MergeReplaceZeroOps() {
+bool MergeWorker::MergeReplaceZeroOps() {
// Flush after merging 2MB. Since all ops are independent and there is no
// dependency between COW ops, we will flush the data and the number
// of ops merged in COW block device. If there is a crash, we will
@@ -99,20 +108,21 @@
for (size_t i = 0; i < replace_zero_vec.size(); i++) {
const CowOperation* cow_op = replace_zero_vec[i];
+
+ void* buffer = bufsink_.AcquireBuffer(BLOCK_SZ);
+ if (!buffer) {
+ SNAP_LOG(ERROR) << "AcquireBuffer failed in MergeReplaceOps";
+ return false;
+ }
if (cow_op->type == kCowReplaceOp) {
- if (!ProcessReplaceOp(cow_op)) {
- SNAP_LOG(ERROR) << "Merge - ReplaceOp failed for block: " << cow_op->new_block;
+ if (!reader_->ReadData(cow_op, buffer, BLOCK_SZ)) {
+ SNAP_LOG(ERROR) << "Failed to read COW in merge";
return false;
}
} else {
CHECK(cow_op->type == kCowZeroOp);
- if (!ProcessZeroOp()) {
- SNAP_LOG(ERROR) << "Merge ZeroOp failed.";
- return false;
- }
+ memset(buffer, 0, BLOCK_SZ);
}
-
- bufsink_.UpdateBufferOffset(BLOCK_SZ);
}
size_t io_size = linear_blocks * BLOCK_SZ;
@@ -149,7 +159,7 @@
if (snapuserd_->IsIOTerminated()) {
SNAP_LOG(ERROR)
- << "MergeReplaceZeroOps: Worker threads terminated - shutting down merge";
+ << "MergeReplaceZeroOps: MergeWorker threads terminated - shutting down merge";
return false;
}
}
@@ -173,7 +183,7 @@
return true;
}
-bool Worker::MergeOrderedOpsAsync() {
+bool MergeWorker::MergeOrderedOpsAsync() {
void* mapped_addr = snapuserd_->GetMappedAddr();
void* read_ahead_buffer =
static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
@@ -190,6 +200,7 @@
// Wait for RA thread to notify that the merge window
// is ready for merging.
if (!snapuserd_->WaitForMergeBegin()) {
+ SNAP_LOG(ERROR) << "Failed waiting for merge to begin";
return false;
}
@@ -295,7 +306,7 @@
// will fallback to synchronous I/O.
ret = io_uring_wait_cqe(ring_.get(), &cqe);
if (ret) {
- SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed: " << ret;
+ SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed: " << strerror(-ret);
status = false;
break;
}
@@ -354,7 +365,7 @@
return true;
}
-bool Worker::MergeOrderedOps() {
+bool MergeWorker::MergeOrderedOps() {
void* mapped_addr = snapuserd_->GetMappedAddr();
void* read_ahead_buffer =
static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
@@ -439,7 +450,7 @@
return true;
}
-bool Worker::AsyncMerge() {
+bool MergeWorker::AsyncMerge() {
if (!MergeOrderedOpsAsync()) {
SNAP_LOG(ERROR) << "MergeOrderedOpsAsync failed - Falling back to synchronous I/O";
// Reset the iter so that we retry the merge
@@ -455,7 +466,7 @@
return true;
}
-bool Worker::SyncMerge() {
+bool MergeWorker::SyncMerge() {
if (!MergeOrderedOps()) {
SNAP_LOG(ERROR) << "Merge failed for ordered ops";
return false;
@@ -465,7 +476,7 @@
return true;
}
-bool Worker::Merge() {
+bool MergeWorker::Merge() {
cowop_iter_ = reader_->GetOpIter(true);
bool retry = false;
@@ -511,7 +522,7 @@
return true;
}
-bool Worker::InitializeIouring() {
+bool MergeWorker::InitializeIouring() {
if (!snapuserd_->IsIouringSupported()) {
return false;
}
@@ -530,25 +541,30 @@
return true;
}
-void Worker::FinalizeIouring() {
+void MergeWorker::FinalizeIouring() {
if (merge_async_) {
io_uring_queue_exit(ring_.get());
}
}
-bool Worker::RunMergeThread() {
+bool MergeWorker::Run() {
SNAP_LOG(DEBUG) << "Waiting for merge begin...";
+
+ pthread_setname_np(pthread_self(), "MergeWorker");
+
if (!snapuserd_->WaitForMergeBegin()) {
SNAP_LOG(ERROR) << "Merge terminated early...";
return true;
}
- if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
- SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
+ if (!SetThreadPriority(kNiceValueForMergeThreads)) {
+ SNAP_PLOG(ERROR) << "Failed to set thread priority";
}
SNAP_LOG(INFO) << "Merge starting..";
+ bufsink_.Initialize(PAYLOAD_BUFFER_SZ);
+
if (!Init()) {
SNAP_LOG(ERROR) << "Merge thread initialization failed...";
snapuserd_->MergeFailed();
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.h
new file mode 100644
index 0000000..478d4c8
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.h
@@ -0,0 +1,59 @@
+// Copyright (C) 2023 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 "worker.h"
+
+#include <liburing.h>
+
+namespace android {
+namespace snapshot {
+
+class MergeWorker : public Worker {
+ public:
+ MergeWorker(const std::string& cow_device, const std::string& misc_name,
+ const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd);
+ bool Run();
+
+ private:
+ int PrepareMerge(uint64_t* source_offset, int* pending_ops,
+ std::vector<const CowOperation*>* replace_zero_vec = nullptr);
+ bool MergeReplaceZeroOps();
+ bool MergeOrderedOps();
+ bool MergeOrderedOpsAsync();
+ bool Merge();
+ bool AsyncMerge();
+ bool SyncMerge();
+ bool InitializeIouring();
+ void FinalizeIouring();
+
+ private:
+ BufferSink bufsink_;
+ std::unique_ptr<ICowOpIter> cowop_iter_;
+ std::unique_ptr<struct io_uring> ring_;
+ size_t ra_block_index_ = 0;
+ uint64_t blocks_merged_in_group_ = 0;
+ bool merge_async_ = false;
+ // Queue depth of 8 seems optimal. We don't want
+ // to have a huge depth as it may put more memory pressure
+ // on the kernel worker threads given that we use
+ // IOSQE_ASYNC flag - ASYNC flags can potentially
+ // result in EINTR; Since we don't restart
+ // syscalls and fallback to synchronous I/O, we
+ // don't want huge queue depth
+ int queue_depth_ = 8;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
new file mode 100644
index 0000000..5cb13e8
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
@@ -0,0 +1,489 @@
+/*
+ * Copyright (C) 2021 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 "read_worker.h"
+
+#include <pthread.h>
+
+#include "snapuserd_core.h"
+#include "utility.h"
+
+namespace android {
+namespace snapshot {
+
+using namespace android;
+using namespace android::dm;
+using android::base::unique_fd;
+
+void ReadWorker::CloseFds() {
+ block_server_ = {};
+ backing_store_fd_ = {};
+ Worker::CloseFds();
+}
+
+ReadWorker::ReadWorker(const std::string& cow_device, const std::string& backing_device,
+ const std::string& misc_name, const std::string& base_path_merge,
+ std::shared_ptr<SnapshotHandler> snapuserd,
+ std::shared_ptr<IBlockServerOpener> opener)
+ : Worker(cow_device, misc_name, base_path_merge, snapuserd),
+ backing_store_device_(backing_device),
+ block_server_opener_(opener) {}
+
+// Start the replace operation. This will read the
+// internal COW format and if the block is compressed,
+// it will be de-compressed.
+bool ReadWorker::ProcessReplaceOp(const CowOperation* cow_op, void* buffer) {
+ if (!reader_->ReadData(cow_op, buffer, BLOCK_SZ)) {
+ SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block;
+ return false;
+ }
+ return true;
+}
+
+bool ReadWorker::ReadFromSourceDevice(const CowOperation* cow_op, void* buffer) {
+ uint64_t offset;
+ if (!reader_->GetSourceOffset(cow_op, &offset)) {
+ SNAP_LOG(ERROR) << "ReadFromSourceDevice: Failed to get source offset";
+ return false;
+ }
+ SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
+ << " Op: " << *cow_op;
+ if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) {
+ std::string op;
+ if (cow_op->type == kCowCopyOp)
+ op = "Copy-op";
+ else {
+ op = "Xor-op";
+ }
+ SNAP_PLOG(ERROR) << op << " failed. Read from backing store: " << backing_store_device_
+ << "at block :" << offset / BLOCK_SZ << " offset:" << offset % BLOCK_SZ;
+ return false;
+ }
+
+ return true;
+}
+
+// Start the copy operation. This will read the backing
+// block device which is represented by cow_op->source.
+bool ReadWorker::ProcessCopyOp(const CowOperation* cow_op, void* buffer) {
+ if (!ReadFromSourceDevice(cow_op, buffer)) {
+ return false;
+ }
+ return true;
+}
+
+bool ReadWorker::ProcessXorOp(const CowOperation* cow_op, void* buffer) {
+ if (!ReadFromSourceDevice(cow_op, buffer)) {
+ return false;
+ }
+
+ if (xor_buffer_.empty()) {
+ xor_buffer_.resize(BLOCK_SZ);
+ }
+ CHECK(xor_buffer_.size() == BLOCK_SZ);
+
+ ssize_t size = reader_->ReadData(cow_op, xor_buffer_.data(), xor_buffer_.size());
+ if (size != BLOCK_SZ) {
+ SNAP_LOG(ERROR) << "ProcessXorOp failed for block " << cow_op->new_block
+ << ", return value: " << size;
+ return false;
+ }
+
+ auto xor_out = reinterpret_cast<uint8_t*>(buffer);
+ for (size_t i = 0; i < BLOCK_SZ; i++) {
+ xor_out[i] ^= xor_buffer_[i];
+ }
+ return true;
+}
+
+bool ReadWorker::ProcessZeroOp(void* buffer) {
+ memset(buffer, 0, BLOCK_SZ);
+ return true;
+}
+
+bool ReadWorker::ProcessOrderedOp(const CowOperation* cow_op, void* buffer) {
+ MERGE_GROUP_STATE state = snapuserd_->ProcessMergingBlock(cow_op->new_block, buffer);
+
+ switch (state) {
+ case MERGE_GROUP_STATE::GROUP_MERGE_COMPLETED: {
+ // Merge is completed for this COW op; just read directly from
+ // the base device
+ SNAP_LOG(DEBUG) << "Merge-completed: Reading from base device sector: "
+ << (cow_op->new_block >> SECTOR_SHIFT)
+ << " Block-number: " << cow_op->new_block;
+ if (!ReadDataFromBaseDevice(ChunkToSector(cow_op->new_block), buffer, BLOCK_SZ)) {
+ SNAP_LOG(ERROR) << "ReadDataFromBaseDevice at sector: "
+ << (cow_op->new_block >> SECTOR_SHIFT) << " after merge-complete.";
+ return false;
+ }
+ return true;
+ }
+ case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {
+ bool ret;
+ if (cow_op->type == kCowCopyOp) {
+ ret = ProcessCopyOp(cow_op, buffer);
+ } else {
+ ret = ProcessXorOp(cow_op, buffer);
+ }
+
+ // I/O is complete - decrement the refcount irrespective of the return
+ // status
+ snapuserd_->NotifyIOCompletion(cow_op->new_block);
+ return ret;
+ }
+ // We already have the data in the buffer retrieved from RA thread.
+ // Nothing to process further.
+ case MERGE_GROUP_STATE::GROUP_MERGE_RA_READY: {
+ [[fallthrough]];
+ }
+ case MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS: {
+ return true;
+ }
+ default: {
+ // All other states, fail the I/O viz (GROUP_MERGE_FAILED and GROUP_INVALID)
+ return false;
+ }
+ }
+
+ return false;
+}
+
+bool ReadWorker::ProcessCowOp(const CowOperation* cow_op, void* buffer) {
+ if (cow_op == nullptr) {
+ SNAP_LOG(ERROR) << "ProcessCowOp: Invalid cow_op";
+ return false;
+ }
+
+ switch (cow_op->type) {
+ case kCowReplaceOp: {
+ return ProcessReplaceOp(cow_op, buffer);
+ }
+
+ case kCowZeroOp: {
+ return ProcessZeroOp(buffer);
+ }
+
+ case kCowCopyOp:
+ [[fallthrough]];
+ case kCowXorOp: {
+ return ProcessOrderedOp(cow_op, buffer);
+ }
+
+ default: {
+ SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
+ }
+ }
+ return false;
+}
+
+bool ReadWorker::Init() {
+ if (!Worker::Init()) {
+ return false;
+ }
+
+ backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
+ if (backing_store_fd_ < 0) {
+ SNAP_PLOG(ERROR) << "Open Failed: " << backing_store_device_;
+ return false;
+ }
+
+ block_server_ = block_server_opener_->Open(this, PAYLOAD_BUFFER_SZ);
+ if (!block_server_) {
+ SNAP_PLOG(ERROR) << "Unable to open block server";
+ return false;
+ }
+ return true;
+}
+
+bool ReadWorker::Run() {
+ SNAP_LOG(INFO) << "Processing snapshot I/O requests....";
+
+ pthread_setname_np(pthread_self(), "ReadWorker");
+
+ if (!SetThreadPriority(kNiceValueForMergeThreads)) {
+ SNAP_PLOG(ERROR) << "Failed to set thread priority";
+ }
+
+ // Start serving IO
+ while (true) {
+ if (!block_server_->ProcessRequests()) {
+ break;
+ }
+ }
+
+ CloseFds();
+ reader_->CloseCowFd();
+
+ return true;
+}
+
+bool ReadWorker::ReadDataFromBaseDevice(sector_t sector, void* buffer, size_t read_size) {
+ CHECK(read_size <= BLOCK_SZ);
+
+ loff_t offset = sector << SECTOR_SHIFT;
+ if (!android::base::ReadFullyAtOffset(base_path_merge_fd_, buffer, read_size, offset)) {
+ SNAP_PLOG(ERROR) << "ReadDataFromBaseDevice failed. fd: " << base_path_merge_fd_
+ << "at sector :" << sector << " size: " << read_size;
+ return false;
+ }
+
+ return true;
+}
+
+bool ReadWorker::ReadAlignedSector(sector_t sector, size_t sz) {
+ size_t remaining_size = sz;
+ std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
+ int ret = 0;
+
+ do {
+ // Process 1MB payload at a time
+ size_t read_size = std::min(PAYLOAD_BUFFER_SZ, remaining_size);
+
+ size_t total_bytes_read = 0;
+
+ while (read_size) {
+ // We need to check every 4k block to verify if it is
+ // present in the mapping.
+ size_t size = std::min(BLOCK_SZ, read_size);
+
+ auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
+ std::make_pair(sector, nullptr), SnapshotHandler::compare);
+ bool not_found = (it == chunk_vec.end() || it->first != sector);
+
+ void* buffer = block_server_->GetResponseBuffer(BLOCK_SZ, size);
+ if (!buffer) {
+ SNAP_LOG(ERROR) << "AcquireBuffer failed in ReadAlignedSector";
+ return false;
+ }
+
+ if (not_found) {
+ // Block not found in map - which means this block was not
+ // changed as per the OTA. Just route the I/O to the base
+ // device.
+ if (!ReadDataFromBaseDevice(sector, buffer, size)) {
+ SNAP_LOG(ERROR) << "ReadDataFromBaseDevice failed";
+ return false;
+ }
+
+ ret = size;
+ } else {
+ // We found the sector in mapping. Check the type of COW OP and
+ // process it.
+ if (!ProcessCowOp(it->second, buffer)) {
+ SNAP_LOG(ERROR)
+ << "ProcessCowOp failed, sector = " << sector << ", size = " << sz;
+ return false;
+ }
+
+ ret = std::min(BLOCK_SZ, read_size);
+ }
+
+ read_size -= ret;
+ total_bytes_read += ret;
+ sector += (ret >> SECTOR_SHIFT);
+ }
+
+ if (!SendBufferedIo()) {
+ return false;
+ }
+
+ SNAP_LOG(DEBUG) << "SendBufferedIo success total_bytes_read: " << total_bytes_read
+ << " remaining_size: " << remaining_size;
+ remaining_size -= total_bytes_read;
+ } while (remaining_size > 0);
+
+ return true;
+}
+
+int ReadWorker::ReadUnalignedSector(
+ sector_t sector, size_t size,
+ std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it) {
+ SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
+ << " Aligned sector: " << it->first;
+
+ int num_sectors_skip = sector - it->first;
+ size_t skip_size = num_sectors_skip << SECTOR_SHIFT;
+ size_t write_size = std::min(size, BLOCK_SZ - skip_size);
+ auto buffer =
+ reinterpret_cast<uint8_t*>(block_server_->GetResponseBuffer(BLOCK_SZ, write_size));
+ if (!buffer) {
+ SNAP_LOG(ERROR) << "ProcessCowOp failed to allocate buffer";
+ return -1;
+ }
+
+ if (!ProcessCowOp(it->second, buffer)) {
+ SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed of size: " << size
+ << " Aligned sector: " << it->first;
+ return -1;
+ }
+
+ if (skip_size) {
+ if (skip_size == BLOCK_SZ) {
+ SNAP_LOG(ERROR) << "Invalid un-aligned IO request at sector: " << sector
+ << " Base-sector: " << it->first;
+ return -1;
+ }
+ memmove(buffer, buffer + skip_size, write_size);
+ }
+ return write_size;
+}
+
+bool ReadWorker::ReadUnalignedSector(sector_t sector, size_t size) {
+ std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
+
+ auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
+ SnapshotHandler::compare);
+
+ // |-------|-------|-------|
+ // 0 1 2 3
+ //
+ // Block 0 - op 1
+ // Block 1 - op 2
+ // Block 2 - op 3
+ //
+ // chunk_vec will have block 0, 1, 2 which maps to relavant COW ops.
+ //
+ // Each block is 4k bytes. Thus, the last block will span 8 sectors
+ // ranging till block 3 (However, block 3 won't be in chunk_vec as
+ // it doesn't have any mapping to COW ops. Now, if we get an I/O request for a sector
+ // spanning between block 2 and block 3, we need to step back
+ // and get hold of the last element.
+ //
+ // Additionally, we need to make sure that the requested sector is
+ // indeed within the range of the final sector. It is perfectly valid
+ // to get an I/O request for block 3 and beyond which are not mapped
+ // to any COW ops. In that case, we just need to read from the base
+ // device.
+ bool merge_complete = false;
+ if (it == chunk_vec.end()) {
+ if (chunk_vec.size() > 0) {
+ // I/O request beyond the last mapped sector
+ it = std::prev(chunk_vec.end());
+ } else {
+ // This can happen when a partition merge is complete but snapshot
+ // state in /metadata is not yet deleted; during this window if the
+ // device is rebooted, subsequent attempt will mount the snapshot.
+ // However, since the merge was completed we wouldn't have any
+ // mapping to COW ops thus chunk_vec will be empty. In that case,
+ // mark this as merge_complete and route the I/O to the base device.
+ merge_complete = true;
+ }
+ } else if (it->first != sector) {
+ if (it != chunk_vec.begin()) {
+ --it;
+ }
+ } else {
+ return ReadAlignedSector(sector, size);
+ }
+
+ loff_t requested_offset = sector << SECTOR_SHIFT;
+
+ loff_t final_offset = 0;
+ if (!merge_complete) {
+ final_offset = it->first << SECTOR_SHIFT;
+ }
+
+ // Since a COW op span 4k block size, we need to make sure that the requested
+ // offset is within the 4k region. Consider the following case:
+ //
+ // |-------|-------|-------|
+ // 0 1 2 3
+ //
+ // Block 0 - op 1
+ // Block 1 - op 2
+ //
+ // We have an I/O request for a sector between block 2 and block 3. However,
+ // we have mapping to COW ops only for block 0 and block 1. Thus, the
+ // requested offset in this case is beyond the last mapped COW op size (which
+ // is block 1 in this case).
+
+ size_t remaining_size = size;
+ int ret = 0;
+ if (!merge_complete && (requested_offset >= final_offset) &&
+ (requested_offset - final_offset) < BLOCK_SZ) {
+ // Read the partial un-aligned data
+ ret = ReadUnalignedSector(sector, remaining_size, it);
+ if (ret < 0) {
+ SNAP_LOG(ERROR) << "ReadUnalignedSector failed for sector: " << sector
+ << " size: " << size << " it->sector: " << it->first;
+ return false;
+ }
+
+ remaining_size -= ret;
+ sector += (ret >> SECTOR_SHIFT);
+
+ // Send the data back
+ if (!SendBufferedIo()) {
+ return false;
+ }
+
+ // If we still have pending data to be processed, this will be aligned I/O
+ if (remaining_size) {
+ return ReadAlignedSector(sector, remaining_size);
+ }
+ } else {
+ // This is all about handling I/O request to be routed to base device
+ // as the I/O is not mapped to any of the COW ops.
+ loff_t aligned_offset = requested_offset;
+ // Align to nearest 4k
+ aligned_offset += BLOCK_SZ - 1;
+ aligned_offset &= ~(BLOCK_SZ - 1);
+ // Find the diff of the aligned offset
+ size_t diff_size = aligned_offset - requested_offset;
+ CHECK(diff_size <= BLOCK_SZ);
+
+ size_t read_size = std::min(remaining_size, diff_size);
+ void* buffer = block_server_->GetResponseBuffer(BLOCK_SZ, read_size);
+ if (!buffer) {
+ SNAP_LOG(ERROR) << "AcquireBuffer failed in ReadUnalignedSector";
+ return false;
+ }
+ if (!ReadDataFromBaseDevice(sector, buffer, read_size)) {
+ return false;
+ }
+ if (!SendBufferedIo()) {
+ return false;
+ }
+
+ if (remaining_size >= diff_size) {
+ remaining_size -= diff_size;
+ size_t num_sectors_read = (diff_size >> SECTOR_SHIFT);
+ sector += num_sectors_read;
+ CHECK(IsBlockAligned(sector << SECTOR_SHIFT));
+
+ // If we still have pending data to be processed, this will be aligned I/O
+ return ReadAlignedSector(sector, remaining_size);
+ }
+ }
+
+ return true;
+}
+
+bool ReadWorker::RequestSectors(uint64_t sector, uint64_t len) {
+ // Unaligned I/O request
+ if (!IsBlockAligned(sector << SECTOR_SHIFT)) {
+ return ReadUnalignedSector(sector, len);
+ }
+
+ return ReadAlignedSector(sector, len);
+}
+
+bool ReadWorker::SendBufferedIo() {
+ return block_server_->SendBufferedIo();
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
new file mode 100644
index 0000000..6dbae81
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
@@ -0,0 +1,70 @@
+// Copyright (C) 2023 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 <utility>
+#include <vector>
+
+#include <snapuserd/block_server.h>
+#include "worker.h"
+
+namespace android {
+namespace snapshot {
+
+class ReadWorker : public Worker, public IBlockServer::Delegate {
+ public:
+ ReadWorker(const std::string& cow_device, const std::string& backing_device,
+ const std::string& misc_name, const std::string& base_path_merge,
+ std::shared_ptr<SnapshotHandler> snapuserd,
+ std::shared_ptr<IBlockServerOpener> opener);
+
+ bool Run();
+ bool Init() override;
+ void CloseFds() override;
+ bool RequestSectors(uint64_t sector, uint64_t size) override;
+
+ IBlockServer* block_server() const { return block_server_.get(); }
+
+ private:
+ bool SendBufferedIo();
+
+ bool ProcessCowOp(const CowOperation* cow_op, void* buffer);
+ bool ProcessXorOp(const CowOperation* cow_op, void* buffer);
+ bool ProcessOrderedOp(const CowOperation* cow_op, void* buffer);
+ bool ProcessCopyOp(const CowOperation* cow_op, void* buffer);
+ bool ProcessReplaceOp(const CowOperation* cow_op, void* buffer);
+ bool ProcessZeroOp(void* buffer);
+
+ bool ReadAlignedSector(sector_t sector, size_t sz);
+ bool ReadUnalignedSector(sector_t sector, size_t size);
+ int ReadUnalignedSector(sector_t sector, size_t size,
+ std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it);
+ bool ReadFromSourceDevice(const CowOperation* cow_op, void* buffer);
+ bool ReadDataFromBaseDevice(sector_t sector, void* buffer, size_t read_size);
+
+ constexpr bool IsBlockAligned(size_t size) { return ((size & (BLOCK_SZ - 1)) == 0); }
+ constexpr sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
+
+ std::string backing_store_device_;
+ unique_fd backing_store_fd_;
+
+ std::shared_ptr<IBlockServerOpener> block_server_opener_;
+ std::unique_ptr<IBlockServer> block_server_;
+
+ std::basic_string<uint8_t> xor_buffer_;
+};
+
+} // namespace snapshot
+} // namespace android
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 8e1212b..e886ec3 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -22,6 +22,10 @@
#include <android-base/properties.h>
#include <android-base/scopeguard.h>
#include <android-base/strings.h>
+#include <snapuserd/dm_user_block_server.h>
+
+#include "merge_worker.h"
+#include "read_worker.h"
namespace android {
namespace snapshot {
@@ -32,12 +36,12 @@
SnapshotHandler::SnapshotHandler(std::string misc_name, std::string cow_device,
std::string backing_device, std::string base_path_merge,
- int num_worker_threads, bool use_iouring,
- bool perform_verification) {
+ std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads,
+ bool use_iouring, bool perform_verification) {
misc_name_ = std::move(misc_name);
cow_device_ = std::move(cow_device);
backing_store_device_ = std::move(backing_device);
- control_device_ = "/dev/dm-user/" + misc_name_;
+ block_server_opener_ = std::move(opener);
base_path_merge_ = std::move(base_path_merge);
num_worker_threads_ = num_worker_threads;
is_io_uring_enabled_ = use_iouring;
@@ -46,9 +50,9 @@
bool SnapshotHandler::InitializeWorkers() {
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());
+ auto wt = std::make_unique<ReadWorker>(cow_device_, backing_store_device_, misc_name_,
+ base_path_merge_, GetSharedPtr(),
+ block_server_opener_);
if (!wt->Init()) {
SNAP_LOG(ERROR) << "Thread initialization failed";
return false;
@@ -57,8 +61,8 @@
worker_threads_.push_back(std::move(wt));
}
- merge_thread_ = std::make_unique<Worker>(cow_device_, backing_store_device_, control_device_,
- misc_name_, base_path_merge_, GetSharedPtr());
+ merge_thread_ = std::make_unique<MergeWorker>(cow_device_, misc_name_, base_path_merge_,
+ GetSharedPtr());
read_ahead_thread_ = std::make_unique<ReadAhead>(cow_device_, backing_store_device_, misc_name_,
GetSharedPtr());
@@ -169,6 +173,10 @@
}
SNAP_LOG(INFO) << "Merge-ops: " << header.num_merge_ops;
+ if (header.num_merge_ops) {
+ resume_merge_ = true;
+ SNAP_LOG(INFO) << "Resume Snapshot-merge";
+ }
if (!MmapMetadata()) {
SNAP_LOG(ERROR) << "mmap failed";
@@ -278,20 +286,6 @@
return false;
}
- unique_fd fd(TEMP_FAILURE_RETRY(open(base_path_merge_.c_str(), O_RDONLY | O_CLOEXEC)));
- if (fd < 0) {
- SNAP_LOG(ERROR) << "Cannot open block device";
- return false;
- }
-
- uint64_t dev_sz = get_block_device_size(fd.get());
- if (!dev_sz) {
- SNAP_LOG(ERROR) << "Failed to find block device size: " << base_path_merge_;
- return false;
- }
-
- num_sectors_ = dev_sz >> SECTOR_SHIFT;
-
return ReadMetadata();
}
@@ -305,21 +299,26 @@
if (ra_thread_) {
ra_thread_status =
std::async(std::launch::async, &ReadAhead::RunThread, read_ahead_thread_.get());
-
- SNAP_LOG(INFO) << "Read-ahead thread started...";
+ // If this is a merge-resume path, wait until RA thread is fully up as
+ // the data has to be re-constructed from the scratch space.
+ if (resume_merge_ && ShouldReconstructDataFromCow()) {
+ WaitForRaThreadToStart();
+ }
}
// Launch worker threads
for (int i = 0; i < worker_threads_.size(); i++) {
threads.emplace_back(
- std::async(std::launch::async, &Worker::RunThread, worker_threads_[i].get()));
+ std::async(std::launch::async, &ReadWorker::Run, worker_threads_[i].get()));
}
std::future<bool> merge_thread =
- std::async(std::launch::async, &Worker::RunMergeThread, merge_thread_.get());
+ std::async(std::launch::async, &MergeWorker::Run, merge_thread_.get());
// Now that the worker threads are up, scan the partitions.
- if (perform_verification_) {
+ // If the snapshot-merge is being resumed, there is no need to scan as the
+ // current slot is already marked as boot complete.
+ if (perform_verification_ && !resume_merge_) {
update_verify_->VerifyUpdatePartition();
}
@@ -452,5 +451,27 @@
return update_verify_->CheckPartitionVerification();
}
+void SnapshotHandler::FreeResources() {
+ worker_threads_.clear();
+ read_ahead_thread_ = nullptr;
+ merge_thread_ = nullptr;
+}
+
+uint64_t SnapshotHandler::GetNumSectors() const {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(base_path_merge_.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ SNAP_LOG(ERROR) << "Cannot open base path: " << base_path_merge_;
+ return false;
+ }
+
+ uint64_t dev_sz = get_block_device_size(fd.get());
+ if (!dev_sz) {
+ SNAP_LOG(ERROR) << "Failed to find block device size: " << base_path_merge_;
+ return false;
+ }
+
+ return dev_sz / SECTOR_SIZE;
+}
+
} // namespace snapshot
} // namespace android
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 c984a61..f88406d 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -28,6 +28,7 @@
#include <iostream>
#include <limits>
#include <mutex>
+#include <ostream>
#include <string>
#include <thread>
#include <unordered_map>
@@ -43,6 +44,7 @@
#include <libsnapshot/cow_reader.h>
#include <libsnapshot/cow_writer.h>
#include <liburing.h>
+#include <snapuserd/block_server.h>
#include <snapuserd/snapuserd_buffer.h>
#include <snapuserd/snapuserd_kernel.h>
#include <storage_literals/storage_literals.h>
@@ -67,15 +69,17 @@
#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
enum class MERGE_IO_TRANSITION {
+ INVALID,
MERGE_READY,
MERGE_BEGIN,
MERGE_FAILED,
MERGE_COMPLETE,
IO_TERMINATED,
- READ_AHEAD_FAILURE,
+ READ_AHEAD_FAILURE
};
-class SnapshotHandler;
+class MergeWorker;
+class ReadWorker;
enum class MERGE_GROUP_STATE {
GROUP_MERGE_PENDING,
@@ -98,113 +102,17 @@
: merge_state_(state), num_ios_in_progress(n_ios) {}
};
-class Worker {
- public:
- Worker(const std::string& cow_device, const std::string& backing_device,
- const std::string& control_device, const std::string& misc_name,
- const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd);
- bool RunThread();
- bool RunMergeThread();
- bool Init();
-
- private:
- // Initialization
- void InitializeBufsink();
- bool InitializeFds();
- bool InitReader();
- void CloseFds() {
- ctrl_fd_ = {};
- backing_store_fd_ = {};
- base_path_merge_fd_ = {};
- }
-
- // Functions interacting with dm-user
- bool WriteDmUserPayload(size_t size);
- bool DmuserReadRequest();
-
- // IO Path
- bool ProcessIORequest();
- bool IsBlockAligned(size_t size) { return ((size & (BLOCK_SZ - 1)) == 0); }
-
- bool ReadDataFromBaseDevice(sector_t sector, size_t read_size);
- bool ReadFromSourceDevice(const CowOperation* cow_op);
-
- bool ReadAlignedSector(sector_t sector, size_t sz);
- bool ReadUnalignedSector(sector_t sector, size_t size);
- int ReadUnalignedSector(sector_t sector, size_t size,
- std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it);
- void RespondIOError();
-
- // Processing COW operations
- bool ProcessCowOp(const CowOperation* cow_op);
- bool ProcessReplaceOp(const CowOperation* cow_op);
- bool ProcessZeroOp();
-
- // Handles Copy and Xor
- bool ProcessCopyOp(const CowOperation* cow_op);
- bool ProcessXorOp(const CowOperation* cow_op);
- bool ProcessOrderedOp(const CowOperation* cow_op);
-
- // Merge related ops
- bool Merge();
- bool AsyncMerge();
- bool SyncMerge();
- bool MergeOrderedOps();
- bool MergeOrderedOpsAsync();
- bool MergeReplaceZeroOps();
- int PrepareMerge(uint64_t* source_offset, int* pending_ops,
- std::vector<const CowOperation*>* replace_zero_vec = nullptr);
-
- sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
- chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
-
- bool InitializeIouring();
- void FinalizeIouring();
-
- std::unique_ptr<CowReader> reader_;
- BufferSink bufsink_;
- XorSink xorsink_;
-
- std::string cow_device_;
- std::string backing_store_device_;
- std::string control_device_;
- std::string misc_name_;
- std::string base_path_merge_;
-
- unique_fd cow_fd_;
- unique_fd backing_store_fd_;
- unique_fd base_path_merge_fd_;
- unique_fd ctrl_fd_;
- bool header_response_ = false;
-
- std::unique_ptr<ICowOpIter> cowop_iter_;
- size_t ra_block_index_ = 0;
- uint64_t blocks_merged_in_group_ = 0;
- bool merge_async_ = false;
- // Queue depth of 8 seems optimal. We don't want
- // to have a huge depth as it may put more memory pressure
- // on the kernel worker threads given that we use
- // IOSQE_ASYNC flag - ASYNC flags can potentially
- // result in EINTR; Since we don't restart
- // syscalls and fallback to synchronous I/O, we
- // don't want huge queue depth
- int queue_depth_ = 8;
- std::unique_ptr<struct io_uring> ring_;
-
- std::shared_ptr<SnapshotHandler> snapuserd_;
-};
-
class SnapshotHandler : public std::enable_shared_from_this<SnapshotHandler> {
public:
SnapshotHandler(std::string misc_name, std::string cow_device, std::string backing_device,
- std::string base_path_merge, int num_workers, bool use_iouring,
- bool perform_verification);
+ std::string base_path_merge, std::shared_ptr<IBlockServerOpener> opener,
+ int num_workers, bool use_iouring, bool perform_verification);
bool InitCowDevice();
bool Start();
const std::string& GetControlDevicePath() { return control_device_; }
const std::string& GetMiscName() { return misc_name_; }
- const uint64_t& GetNumSectors() { return num_sectors_; }
+ uint64_t GetNumSectors() const;
const bool& IsAttached() const { return attached_; }
void AttachControlDevice() { attached_ = true; }
@@ -212,11 +120,7 @@
bool CommitMerge(int num_merge_ops);
void CloseFds() { cow_fd_ = {}; }
- void FreeResources() {
- worker_threads_.clear();
- read_ahead_thread_ = nullptr;
- merge_thread_ = nullptr;
- }
+ void FreeResources();
bool InitializeWorkers();
std::unique_ptr<CowReader> CloneReaderForWorker();
@@ -243,6 +147,8 @@
void WakeupMonitorMergeThread();
void WaitForMergeComplete();
bool WaitForMergeBegin();
+ void RaThreadStarted();
+ void WaitForRaThreadToStart();
void NotifyRAForMergeReady();
bool WaitForMergeReady();
void MergeFailed();
@@ -301,8 +207,6 @@
unique_fd cow_fd_;
- uint64_t num_sectors_;
-
std::unique_ptr<CowReader> reader_;
// chunk_vec stores the pseudo mapping of sector
@@ -315,12 +219,13 @@
void* mapped_addr_;
size_t total_mapped_addr_length_;
- std::vector<std::unique_ptr<Worker>> worker_threads_;
+ std::vector<std::unique_ptr<ReadWorker>> worker_threads_;
// Read-ahead related
bool populate_data_from_cow_ = false;
bool ra_thread_ = false;
+ bool ra_thread_started_ = false;
int total_ra_blocks_merged_ = 0;
- MERGE_IO_TRANSITION io_state_;
+ MERGE_IO_TRANSITION io_state_ = MERGE_IO_TRANSITION::INVALID;
std::unique_ptr<ReadAhead> read_ahead_thread_;
std::unordered_map<uint64_t, void*> read_ahead_buffer_map_;
@@ -330,7 +235,7 @@
// Merge Block state
std::vector<std::unique_ptr<MergeGroupState>> merge_blk_state_;
- std::unique_ptr<Worker> merge_thread_;
+ std::unique_ptr<MergeWorker> merge_thread_;
double merge_completion_percentage_;
bool merge_initiated_ = false;
@@ -340,10 +245,15 @@
bool scratch_space_ = false;
int num_worker_threads_ = kNumWorkerThreads;
bool perform_verification_ = true;
+ bool resume_merge_ = false;
std::unique_ptr<struct io_uring> ring_;
std::unique_ptr<UpdateVerify> update_verify_;
+ std::shared_ptr<IBlockServerOpener> block_server_opener_;
};
+std::ostream& operator<<(std::ostream& os, MERGE_IO_TRANSITION value);
+static_assert(sizeof(off_t) == sizeof(uint64_t));
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
deleted file mode 100644
index 2b9d14e..0000000
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
+++ /dev/null
@@ -1,644 +0,0 @@
-/*
- * Copyright (C) 2021 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 "snapuserd_core.h"
-
-namespace android {
-namespace snapshot {
-
-using namespace android;
-using namespace android::dm;
-using android::base::unique_fd;
-
-Worker::Worker(const std::string& cow_device, const std::string& backing_device,
- const std::string& control_device, const std::string& misc_name,
- const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd) {
- cow_device_ = cow_device;
- backing_store_device_ = backing_device;
- control_device_ = control_device;
- misc_name_ = misc_name;
- base_path_merge_ = base_path_merge;
- snapuserd_ = snapuserd;
-}
-
-bool Worker::InitializeFds() {
- backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
- if (backing_store_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << backing_store_device_;
- return false;
- }
-
- cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
- if (cow_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
- return false;
- }
-
- ctrl_fd_.reset(open(control_device_.c_str(), O_RDWR));
- if (ctrl_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Unable to open " << control_device_;
- return false;
- }
-
- // Base device used by merge thread
- base_path_merge_fd_.reset(open(base_path_merge_.c_str(), O_RDWR));
- if (base_path_merge_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << base_path_merge_;
- return false;
- }
-
- return true;
-}
-
-bool Worker::InitReader() {
- reader_ = snapuserd_->CloneReaderForWorker();
-
- if (!reader_->InitForMerge(std::move(cow_fd_))) {
- return false;
- }
- return true;
-}
-
-// Start the replace operation. This will read the
-// internal COW format and if the block is compressed,
-// it will be de-compressed.
-bool Worker::ProcessReplaceOp(const CowOperation* cow_op) {
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (!buffer) {
- SNAP_LOG(ERROR) << "ProcessReplaceOp failed to allocate buffer";
- return false;
- }
- if (!reader_->ReadData(cow_op, buffer, BLOCK_SZ)) {
- SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block;
- return false;
- }
- return true;
-}
-
-bool Worker::ReadFromSourceDevice(const CowOperation* cow_op) {
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer";
- return false;
- }
- uint64_t offset;
- if (!reader_->GetSourceOffset(cow_op, &offset)) {
- SNAP_LOG(ERROR) << "ReadFromSourceDevice: Failed to get source offset";
- return false;
- }
- SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
- << " Op: " << *cow_op;
- if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) {
- std::string op;
- if (cow_op->type == kCowCopyOp)
- op = "Copy-op";
- else {
- op = "Xor-op";
- }
- SNAP_PLOG(ERROR) << op << " failed. Read from backing store: " << backing_store_device_
- << "at block :" << offset / BLOCK_SZ << " offset:" << offset % BLOCK_SZ;
- return false;
- }
-
- return true;
-}
-
-// Start the copy operation. This will read the backing
-// block device which is represented by cow_op->source.
-bool Worker::ProcessCopyOp(const CowOperation* cow_op) {
- if (!ReadFromSourceDevice(cow_op)) {
- return false;
- }
-
- return true;
-}
-
-bool Worker::ProcessXorOp(const CowOperation* cow_op) {
- if (!ReadFromSourceDevice(cow_op)) {
- return false;
- }
-
- xorsink_.Reset();
-
- size_t actual = 0;
- void* buffer = xorsink_.GetBuffer(BLOCK_SZ, &actual);
- if (!buffer || actual < BLOCK_SZ) {
- SNAP_LOG(ERROR) << "ProcessXorOp failed to get buffer of " << BLOCK_SZ << " size, got "
- << actual;
- return false;
- }
- ssize_t size = reader_->ReadData(cow_op, buffer, BLOCK_SZ);
- if (size != BLOCK_SZ) {
- SNAP_LOG(ERROR) << "ProcessXorOp failed for block " << cow_op->new_block
- << ", return value: " << size;
- return false;
- }
- if (!xorsink_.ReturnData(buffer, size)) {
- SNAP_LOG(ERROR) << "ProcessXorOp failed to return data";
- return false;
- }
- return true;
-}
-
-bool Worker::ProcessZeroOp() {
- // Zero out the entire block
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ProcessZeroOp: Failed to get payload buffer";
- return false;
- }
-
- memset(buffer, 0, BLOCK_SZ);
- return true;
-}
-
-bool Worker::ProcessOrderedOp(const CowOperation* cow_op) {
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ProcessOrderedOp: Failed to get payload buffer";
- return false;
- }
-
- MERGE_GROUP_STATE state = snapuserd_->ProcessMergingBlock(cow_op->new_block, buffer);
-
- switch (state) {
- case MERGE_GROUP_STATE::GROUP_MERGE_COMPLETED: {
- // Merge is completed for this COW op; just read directly from
- // the base device
- SNAP_LOG(DEBUG) << "Merge-completed: Reading from base device sector: "
- << (cow_op->new_block >> SECTOR_SHIFT)
- << " Block-number: " << cow_op->new_block;
- if (!ReadDataFromBaseDevice(ChunkToSector(cow_op->new_block), BLOCK_SZ)) {
- SNAP_LOG(ERROR) << "ReadDataFromBaseDevice at sector: "
- << (cow_op->new_block >> SECTOR_SHIFT) << " after merge-complete.";
- return false;
- }
- return true;
- }
- case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {
- bool ret;
- if (cow_op->type == kCowCopyOp) {
- ret = ProcessCopyOp(cow_op);
- } else {
- ret = ProcessXorOp(cow_op);
- }
-
- // I/O is complete - decrement the refcount irrespective of the return
- // status
- snapuserd_->NotifyIOCompletion(cow_op->new_block);
- return ret;
- }
- // We already have the data in the buffer retrieved from RA thread.
- // Nothing to process further.
- case MERGE_GROUP_STATE::GROUP_MERGE_RA_READY: {
- [[fallthrough]];
- }
- case MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS: {
- return true;
- }
- default: {
- // All other states, fail the I/O viz (GROUP_MERGE_FAILED and GROUP_INVALID)
- return false;
- }
- }
-
- return false;
-}
-
-bool Worker::ProcessCowOp(const CowOperation* cow_op) {
- if (cow_op == nullptr) {
- SNAP_LOG(ERROR) << "ProcessCowOp: Invalid cow_op";
- return false;
- }
-
- switch (cow_op->type) {
- case kCowReplaceOp: {
- return ProcessReplaceOp(cow_op);
- }
-
- case kCowZeroOp: {
- return ProcessZeroOp();
- }
-
- case kCowCopyOp:
- [[fallthrough]];
- case kCowXorOp: {
- return ProcessOrderedOp(cow_op);
- }
-
- default: {
- SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
- }
- }
- return false;
-}
-
-void Worker::InitializeBufsink() {
- // Allocate the buffer which is used to communicate between
- // daemon and dm-user. The buffer comprises of header and a fixed payload.
- // If the dm-user requests a big IO, the IO will be broken into chunks
- // of PAYLOAD_BUFFER_SZ.
- size_t buf_size = sizeof(struct dm_user_header) + PAYLOAD_BUFFER_SZ;
- bufsink_.Initialize(buf_size);
-}
-
-bool Worker::Init() {
- InitializeBufsink();
- xorsink_.Initialize(&bufsink_, BLOCK_SZ);
-
- if (!InitializeFds()) {
- return false;
- }
-
- if (!InitReader()) {
- return false;
- }
-
- return true;
-}
-
-bool Worker::RunThread() {
- SNAP_LOG(INFO) << "Processing snapshot I/O requests....";
-
- if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
- SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
- }
-
- // Start serving IO
- while (true) {
- if (!ProcessIORequest()) {
- break;
- }
- }
-
- CloseFds();
- reader_->CloseCowFd();
-
- return true;
-}
-
-// Send the payload/data back to dm-user misc device.
-bool Worker::WriteDmUserPayload(size_t size) {
- size_t payload_size = size;
- void* buf = bufsink_.GetPayloadBufPtr();
- if (header_response_) {
- payload_size += sizeof(struct dm_user_header);
- buf = bufsink_.GetBufPtr();
- }
-
- if (!android::base::WriteFully(ctrl_fd_, buf, payload_size)) {
- SNAP_PLOG(ERROR) << "Write to dm-user failed size: " << payload_size;
- return false;
- }
-
- // After the first header is sent in response to a request, we cannot
- // send any additional headers.
- header_response_ = false;
- return true;
-}
-
-bool Worker::ReadDataFromBaseDevice(sector_t sector, size_t read_size) {
- CHECK(read_size <= BLOCK_SZ);
-
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer";
- return false;
- }
-
- loff_t offset = sector << SECTOR_SHIFT;
- if (!android::base::ReadFullyAtOffset(base_path_merge_fd_, buffer, read_size, offset)) {
- SNAP_PLOG(ERROR) << "ReadDataFromBaseDevice failed. fd: " << base_path_merge_fd_
- << "at sector :" << sector << " size: " << read_size;
- return false;
- }
-
- return true;
-}
-
-bool Worker::ReadAlignedSector(sector_t sector, size_t sz) {
- size_t remaining_size = sz;
- std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
- int ret = 0;
-
- do {
- // Process 1MB payload at a time
- size_t read_size = std::min(PAYLOAD_BUFFER_SZ, remaining_size);
-
- size_t total_bytes_read = 0;
- bufsink_.ResetBufferOffset();
-
- while (read_size) {
- // We need to check every 4k block to verify if it is
- // present in the mapping.
- size_t size = std::min(BLOCK_SZ, read_size);
-
- auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
- std::make_pair(sector, nullptr), SnapshotHandler::compare);
- bool not_found = (it == chunk_vec.end() || it->first != sector);
-
- if (not_found) {
- // Block not found in map - which means this block was not
- // changed as per the OTA. Just route the I/O to the base
- // device.
- if (!ReadDataFromBaseDevice(sector, size)) {
- SNAP_LOG(ERROR) << "ReadDataFromBaseDevice failed";
- return false;
- }
-
- ret = size;
- } else {
- // We found the sector in mapping. Check the type of COW OP and
- // process it.
- if (!ProcessCowOp(it->second)) {
- SNAP_LOG(ERROR) << "ProcessCowOp failed";
- return false;
- }
-
- ret = BLOCK_SZ;
- }
-
- read_size -= ret;
- total_bytes_read += ret;
- sector += (ret >> SECTOR_SHIFT);
- bufsink_.UpdateBufferOffset(ret);
- }
-
- if (!WriteDmUserPayload(total_bytes_read)) {
- return false;
- }
-
- SNAP_LOG(DEBUG) << "WriteDmUserPayload success total_bytes_read: " << total_bytes_read
- << " remaining_size: " << remaining_size;
- remaining_size -= total_bytes_read;
- } while (remaining_size > 0);
-
- return true;
-}
-
-int Worker::ReadUnalignedSector(
- sector_t sector, size_t size,
- std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it) {
- size_t skip_sector_size = 0;
-
- SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
- << " Aligned sector: " << it->first;
-
- if (!ProcessCowOp(it->second)) {
- SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed of size: " << size
- << " Aligned sector: " << it->first;
- return -1;
- }
-
- int num_sectors_skip = sector - it->first;
-
- if (num_sectors_skip > 0) {
- skip_sector_size = num_sectors_skip << SECTOR_SHIFT;
- char* buffer = reinterpret_cast<char*>(bufsink_.GetBufPtr());
- struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
-
- if (skip_sector_size == BLOCK_SZ) {
- SNAP_LOG(ERROR) << "Invalid un-aligned IO request at sector: " << sector
- << " Base-sector: " << it->first;
- return -1;
- }
-
- memmove(msg->payload.buf, (char*)msg->payload.buf + skip_sector_size,
- (BLOCK_SZ - skip_sector_size));
- }
-
- bufsink_.ResetBufferOffset();
- return std::min(size, (BLOCK_SZ - skip_sector_size));
-}
-
-bool Worker::ReadUnalignedSector(sector_t sector, size_t size) {
- bufsink_.ResetBufferOffset();
- std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
-
- auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
- SnapshotHandler::compare);
-
- // |-------|-------|-------|
- // 0 1 2 3
- //
- // Block 0 - op 1
- // Block 1 - op 2
- // Block 2 - op 3
- //
- // chunk_vec will have block 0, 1, 2 which maps to relavant COW ops.
- //
- // Each block is 4k bytes. Thus, the last block will span 8 sectors
- // ranging till block 3 (However, block 3 won't be in chunk_vec as
- // it doesn't have any mapping to COW ops. Now, if we get an I/O request for a sector
- // spanning between block 2 and block 3, we need to step back
- // and get hold of the last element.
- //
- // Additionally, we need to make sure that the requested sector is
- // indeed within the range of the final sector. It is perfectly valid
- // to get an I/O request for block 3 and beyond which are not mapped
- // to any COW ops. In that case, we just need to read from the base
- // device.
- bool merge_complete = false;
- if (it == chunk_vec.end()) {
- if (chunk_vec.size() > 0) {
- // I/O request beyond the last mapped sector
- it = std::prev(chunk_vec.end());
- } else {
- // This can happen when a partition merge is complete but snapshot
- // state in /metadata is not yet deleted; during this window if the
- // device is rebooted, subsequent attempt will mount the snapshot.
- // However, since the merge was completed we wouldn't have any
- // mapping to COW ops thus chunk_vec will be empty. In that case,
- // mark this as merge_complete and route the I/O to the base device.
- merge_complete = true;
- }
- } else if (it->first != sector) {
- if (it != chunk_vec.begin()) {
- --it;
- }
- } else {
- return ReadAlignedSector(sector, size);
- }
-
- loff_t requested_offset = sector << SECTOR_SHIFT;
-
- loff_t final_offset = 0;
- if (!merge_complete) {
- final_offset = it->first << SECTOR_SHIFT;
- }
-
- // Since a COW op span 4k block size, we need to make sure that the requested
- // offset is within the 4k region. Consider the following case:
- //
- // |-------|-------|-------|
- // 0 1 2 3
- //
- // Block 0 - op 1
- // Block 1 - op 2
- //
- // We have an I/O request for a sector between block 2 and block 3. However,
- // we have mapping to COW ops only for block 0 and block 1. Thus, the
- // requested offset in this case is beyond the last mapped COW op size (which
- // is block 1 in this case).
-
- size_t total_bytes_read = 0;
- size_t remaining_size = size;
- int ret = 0;
- if (!merge_complete && (requested_offset >= final_offset) &&
- (requested_offset - final_offset) < BLOCK_SZ) {
- // Read the partial un-aligned data
- ret = ReadUnalignedSector(sector, remaining_size, it);
- if (ret < 0) {
- SNAP_LOG(ERROR) << "ReadUnalignedSector failed for sector: " << sector
- << " size: " << size << " it->sector: " << it->first;
- return false;
- }
-
- remaining_size -= ret;
- total_bytes_read += ret;
- sector += (ret >> SECTOR_SHIFT);
-
- // Send the data back
- if (!WriteDmUserPayload(total_bytes_read)) {
- return false;
- }
-
- // If we still have pending data to be processed, this will be aligned I/O
- if (remaining_size) {
- return ReadAlignedSector(sector, remaining_size);
- }
- } else {
- // This is all about handling I/O request to be routed to base device
- // as the I/O is not mapped to any of the COW ops.
- loff_t aligned_offset = requested_offset;
- // Align to nearest 4k
- aligned_offset += BLOCK_SZ - 1;
- aligned_offset &= ~(BLOCK_SZ - 1);
- // Find the diff of the aligned offset
- size_t diff_size = aligned_offset - requested_offset;
- CHECK(diff_size <= BLOCK_SZ);
- if (remaining_size < diff_size) {
- if (!ReadDataFromBaseDevice(sector, remaining_size)) {
- return false;
- }
- total_bytes_read += remaining_size;
-
- if (!WriteDmUserPayload(total_bytes_read)) {
- return false;
- }
- } else {
- if (!ReadDataFromBaseDevice(sector, diff_size)) {
- return false;
- }
-
- total_bytes_read += diff_size;
-
- if (!WriteDmUserPayload(total_bytes_read)) {
- return false;
- }
-
- remaining_size -= diff_size;
- size_t num_sectors_read = (diff_size >> SECTOR_SHIFT);
- sector += num_sectors_read;
- CHECK(IsBlockAligned(sector << SECTOR_SHIFT));
-
- // If we still have pending data to be processed, this will be aligned I/O
- return ReadAlignedSector(sector, remaining_size);
- }
- }
-
- return true;
-}
-
-void Worker::RespondIOError() {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
- header->type = DM_USER_RESP_ERROR;
- // This is an issue with the dm-user interface. There
- // is no way to propagate the I/O error back to dm-user
- // if we have already communicated the header back. Header
- // is responded once at the beginning; however I/O can
- // be processed in chunks. If we encounter an I/O error
- // somewhere in the middle of the processing, we can't communicate
- // this back to dm-user.
- //
- // TODO: Fix the interface
- CHECK(header_response_);
-
- WriteDmUserPayload(0);
-}
-
-bool Worker::DmuserReadRequest() {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
-
- // Unaligned I/O request
- if (!IsBlockAligned(header->sector << SECTOR_SHIFT)) {
- return ReadUnalignedSector(header->sector, header->len);
- }
-
- return ReadAlignedSector(header->sector, header->len);
-}
-
-bool Worker::ProcessIORequest() {
- // Read Header from dm-user misc device. This gives
- // us the sector number for which IO is issued by dm-snapshot device
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
- if (!android::base::ReadFully(ctrl_fd_, header, sizeof(*header))) {
- if (errno != ENOTBLK) {
- SNAP_PLOG(ERROR) << "Control-read failed";
- }
-
- SNAP_PLOG(DEBUG) << "ReadDmUserHeader failed....";
- return false;
- }
-
- SNAP_LOG(DEBUG) << "Daemon: msg->seq: " << std::dec << header->seq;
- SNAP_LOG(DEBUG) << "Daemon: msg->len: " << std::dec << header->len;
- SNAP_LOG(DEBUG) << "Daemon: msg->sector: " << std::dec << header->sector;
- SNAP_LOG(DEBUG) << "Daemon: msg->type: " << std::dec << header->type;
- SNAP_LOG(DEBUG) << "Daemon: msg->flags: " << std::dec << header->flags;
-
- // Use the same header buffer as the response header.
- int request_type = header->type;
- header->type = DM_USER_RESP_SUCCESS;
- header_response_ = true;
-
- bool ok;
- switch (request_type) {
- case DM_USER_REQ_MAP_READ:
- ok = DmuserReadRequest();
- break;
-
- case DM_USER_REQ_MAP_WRITE:
- // TODO: We should not get any write request
- // to dm-user as we mount all partitions
- // as read-only. Need to verify how are TRIM commands
- // handled during mount.
- ok = false;
- break;
-
- default:
- ok = false;
- break;
- }
-
- if (!ok && header->type != DM_USER_RESP_ERROR) {
- RespondIOError();
- }
- return ok;
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
index 399f7b8..998d233 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -16,7 +16,10 @@
#include "snapuserd_readahead.h"
+#include <pthread.h>
+
#include "snapuserd_core.h"
+#include "utility.h"
namespace android {
namespace snapshot {
@@ -203,6 +206,7 @@
return false;
}
+ snapuserd_->RaThreadStarted();
SNAP_LOG(INFO) << "ReconstructDataFromCow success";
notify_read_ahead_failed.Cancel();
return true;
@@ -427,7 +431,7 @@
// will fallback to synchronous I/O.
int ret = io_uring_wait_cqe(ring_.get(), &cqe);
if (ret) {
- SNAP_LOG(ERROR) << "Read-ahead - io_uring_wait_cqe failed: " << ret;
+ SNAP_LOG(ERROR) << "Read-ahead - io_uring_wait_cqe failed: " << strerror(-ret);
status = false;
break;
}
@@ -492,7 +496,7 @@
if (xor_op_index < xor_op_vec.size()) {
const CowOperation* xor_op = xor_op_vec[xor_op_index];
if (xor_op->new_block == new_block) {
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
+ void* buffer = bufsink_.AcquireBuffer(BLOCK_SZ);
if (!buffer) {
SNAP_LOG(ERROR) << "ReadAhead - failed to allocate buffer for block: "
<< xor_op->new_block;
@@ -506,7 +510,6 @@
}
xor_op_index += 1;
- bufsink_.UpdateBufferOffset(BLOCK_SZ);
}
}
block_index += 1;
@@ -593,7 +596,7 @@
// Check if this block is an XOR op
if (xor_op->new_block == new_block) {
// Read the xor'ed data from COW
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
+ void* buffer = bufsink.GetPayloadBuffer(BLOCK_SZ);
if (!buffer) {
SNAP_LOG(ERROR) << "ReadAhead - failed to allocate buffer";
return false;
@@ -691,6 +694,7 @@
// window. If there is a crash during this time frame, merge should resume
// based on the contents of the scratch space.
if (!snapuserd_->WaitForMergeReady()) {
+ SNAP_LOG(ERROR) << "ReadAhead failed to wait for merge ready";
return false;
}
@@ -713,9 +717,13 @@
total_ra_blocks_completed_ += total_blocks_merged_;
snapuserd_->SetMergedBlockCountForNextCommit(total_blocks_merged_);
- // Flush the data only if we have a overlapping blocks in the region
+ // Flush the scratch data - Technically, we should flush only for overlapping
+ // blocks; However, since this region is mmap'ed, the dirty pages can still
+ // get flushed to disk at any random point in time. Instead, make sure
+ // the data in scratch is in the correct state before merge thread resumes.
+ //
// Notify the Merge thread to resume merging this window
- if (!snapuserd_->ReadAheadIOCompleted(overlap_)) {
+ if (!snapuserd_->ReadAheadIOCompleted(true)) {
SNAP_LOG(ERROR) << "ReadAheadIOCompleted failed...";
snapuserd_->ReadAheadIOFailed();
return false;
@@ -752,6 +760,10 @@
}
bool ReadAhead::RunThread() {
+ SNAP_LOG(INFO) << "ReadAhead thread started.";
+
+ pthread_setname_np(pthread_self(), "ReadAhead");
+
if (!InitializeFds()) {
return false;
}
@@ -766,10 +778,11 @@
InitializeIouring();
- if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
- SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
+ if (!SetThreadPriority(kNiceValueForMergeThreads)) {
+ SNAP_PLOG(ERROR) << "Failed to set thread priority";
}
+ SNAP_LOG(INFO) << "ReadAhead processing.";
while (!RAIterDone()) {
if (!ReadAheadIOStart()) {
break;
@@ -780,7 +793,7 @@
CloseFds();
reader_->CloseCowFd();
- SNAP_LOG(INFO) << " ReadAhead thread terminating....";
+ SNAP_LOG(INFO) << " ReadAhead thread terminating.";
return true;
}
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 c953f1a..13b9a00 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -31,6 +31,7 @@
#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <fs_mgr/file_wait.h>
+#include <snapuserd/dm_user_block_server.h>
#include <snapuserd/snapuserd_client.h>
#include "snapuserd_server.h"
@@ -48,6 +49,7 @@
UserSnapshotServer::UserSnapshotServer() {
terminating_ = false;
handlers_ = std::make_unique<SnapshotHandlerManager>();
+ block_server_factory_ = std::make_unique<DmUserBlockServerFactory>();
}
UserSnapshotServer::~UserSnapshotServer() {
@@ -130,7 +132,12 @@
return Sendmsg(fd, "fail");
}
- auto retval = "success," + std::to_string(handler->snapuserd()->GetNumSectors());
+ auto num_sectors = handler->snapuserd()->GetNumSectors();
+ if (!num_sectors) {
+ return Sendmsg(fd, "fail");
+ }
+
+ auto retval = "success," + std::to_string(num_sectors);
return Sendmsg(fd, retval);
} else if (cmd == "start") {
// Message format:
@@ -358,8 +365,11 @@
perform_verification = false;
}
+ auto opener = block_server_factory_->CreateOpener(misc_name);
+
return handlers_->AddHandler(misc_name, cow_device_path, backing_device, base_path_merge,
- num_worker_threads, io_uring_enabled_, perform_verification);
+ opener, num_worker_threads, io_uring_enabled_,
+ perform_verification);
}
bool UserSnapshotServer::WaitForSocket() {
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 988c01a..be28541 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -31,6 +31,7 @@
#include <vector>
#include <android-base/unique_fd.h>
+#include <snapuserd/block_server.h>
#include "handler_manager.h"
#include "snapuserd_core.h"
@@ -50,6 +51,7 @@
bool is_server_running_ = false;
bool io_uring_enabled_ = false;
std::unique_ptr<ISnapshotHandlerManager> handlers_;
+ std::unique_ptr<IBlockServerFactory> block_server_factory_;
std::mutex lock_;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
index efe0c14..65f31cf 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -17,7 +17,6 @@
#include <fcntl.h>
#include <linux/fs.h>
-#include <linux/memfd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
@@ -37,10 +36,16 @@
#include <libdm/dm.h>
#include <libdm/loop_control.h>
#include <libsnapshot/cow_writer.h>
+#include <snapuserd/dm_user_block_server.h>
#include <storage_literals/storage_literals.h>
-
#include "handler_manager.h"
+#include "merge_worker.h"
+#include "read_worker.h"
#include "snapuserd_core.h"
+#include "testing/dm_user_harness.h"
+#include "testing/host_harness.h"
+#include "testing/temp_device.h"
+#include "utility.h"
DEFINE_string(force_config, "", "Force testing mode with iouring disabled");
@@ -53,186 +58,54 @@
using namespace std::chrono_literals;
using namespace android::dm;
using namespace std;
+using testing::AssertionFailure;
+using testing::AssertionResult;
+using testing::AssertionSuccess;
-class Tempdevice {
- public:
- Tempdevice(const std::string& name, const DmTable& table)
- : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
- valid_ = dm_.CreateDevice(name, table, &path_, std::chrono::seconds(5));
- }
- Tempdevice(Tempdevice&& other) noexcept
- : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
- other.valid_ = false;
- }
- ~Tempdevice() {
- if (valid_) {
- dm_.DeleteDeviceIfExists(name_);
- }
- }
- bool Destroy() {
- if (!valid_) {
- return true;
- }
- valid_ = false;
- return dm_.DeleteDeviceIfExists(name_);
- }
- const std::string& path() const { return path_; }
- const std::string& name() const { return name_; }
- bool valid() const { return valid_; }
-
- Tempdevice(const Tempdevice&) = delete;
- Tempdevice& operator=(const Tempdevice&) = delete;
-
- Tempdevice& operator=(Tempdevice&& other) noexcept {
- name_ = other.name_;
- valid_ = other.valid_;
- other.valid_ = false;
- return *this;
- }
-
- private:
- DeviceMapper& dm_;
- std::string name_;
- std::string path_;
- bool valid_;
-};
-
-class SnapuserdTest : public ::testing::Test {
- public:
- bool SetupDefault();
- bool SetupOrderedOps();
- bool SetupOrderedOpsInverted();
- bool SetupCopyOverlap_1();
- bool SetupCopyOverlap_2();
- bool Merge();
- void ValidateMerge();
- void ReadSnapshotDeviceAndValidate();
- void Shutdown();
- void MergeInterrupt();
- void MergeInterruptFixed(int duration);
- void MergeInterruptRandomly(int max_duration);
- void StartMerge();
- void CheckMergeCompletion();
-
- static const uint64_t kSectorSize = 512;
-
+class SnapuserdTestBase : public ::testing::Test {
protected:
- void SetUp() override {}
- void TearDown() override { Shutdown(); }
-
- private:
- void SetupImpl();
-
- void SimulateDaemonRestart();
-
- std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
- void CreateCowDevice();
- void CreateCowDeviceOrderedOps();
- void CreateCowDeviceOrderedOpsInverted();
- void CreateCowDeviceWithCopyOverlap_1();
- void CreateCowDeviceWithCopyOverlap_2();
- bool SetupDaemon();
+ void SetUp() override;
+ void TearDown() override;
void CreateBaseDevice();
- void InitCowDevice();
+ void CreateCowDevice();
void SetDeviceControlName();
- void InitDaemon();
- void CreateDmUserDevice();
+ std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
- unique_ptr<LoopDevice> base_loop_;
- unique_ptr<Tempdevice> dmuser_dev_;
-
+ std::unique_ptr<ITestHarness> harness_;
+ size_t size_ = 10_MiB;
+ int total_base_size_ = 0;
std::string system_device_ctrl_name_;
std::string system_device_name_;
+ unique_ptr<IBackingDevice> base_dev_;
unique_fd base_fd_;
+
std::unique_ptr<TemporaryFile> cow_system_;
+
std::unique_ptr<uint8_t[]> orig_buffer_;
- std::unique_ptr<uint8_t[]> merged_buffer_;
- SnapshotHandlerManager handlers_;
- bool setup_ok_ = false;
- bool merge_ok_ = false;
- size_t size_ = 100_MiB;
- int cow_num_sectors_;
- int total_base_size_;
};
-static unique_fd CreateTempFile(const std::string& name, size_t size) {
- unique_fd fd(syscall(__NR_memfd_create, name.c_str(), MFD_ALLOW_SEALING));
- if (fd < 0) {
- return {};
- }
- if (size) {
- if (ftruncate(fd, size) < 0) {
- perror("ftruncate");
- return {};
- }
- if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
- perror("fcntl");
- return {};
- }
- }
- return fd;
+void SnapuserdTestBase::SetUp() {
+#if __ANDROID__
+ harness_ = std::make_unique<DmUserTestHarness>();
+#else
+ harness_ = std::make_unique<HostTestHarness>();
+#endif
}
-void SnapuserdTest::Shutdown() {
- ASSERT_TRUE(dmuser_dev_->Destroy());
+void SnapuserdTestBase::TearDown() {}
- auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
- ASSERT_TRUE(handlers_.DeleteHandler(system_device_ctrl_name_));
- ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted(misc_device, 10s));
- handlers_.TerminateMergeThreads();
-}
-
-bool SnapuserdTest::SetupDefault() {
- SetupImpl();
- return setup_ok_;
-}
-
-bool SnapuserdTest::SetupOrderedOps() {
- CreateBaseDevice();
- CreateCowDeviceOrderedOps();
- return SetupDaemon();
-}
-
-bool SnapuserdTest::SetupOrderedOpsInverted() {
- CreateBaseDevice();
- CreateCowDeviceOrderedOpsInverted();
- return SetupDaemon();
-}
-
-bool SnapuserdTest::SetupCopyOverlap_1() {
- CreateBaseDevice();
- CreateCowDeviceWithCopyOverlap_1();
- return SetupDaemon();
-}
-
-bool SnapuserdTest::SetupCopyOverlap_2() {
- CreateBaseDevice();
- CreateCowDeviceWithCopyOverlap_2();
- return SetupDaemon();
-}
-
-bool SnapuserdTest::SetupDaemon() {
- SetDeviceControlName();
-
- CreateDmUserDevice();
- InitCowDevice();
- InitDaemon();
-
- setup_ok_ = true;
-
- return setup_ok_;
-}
-
-void SnapuserdTest::CreateBaseDevice() {
- unique_fd rnd_fd;
-
+void SnapuserdTestBase::CreateBaseDevice() {
total_base_size_ = (size_ * 5);
- base_fd_ = CreateTempFile("base_device", total_base_size_);
+
+ base_dev_ = harness_->CreateBackingDevice(total_base_size_);
+ ASSERT_NE(base_dev_, nullptr);
+
+ base_fd_.reset(open(base_dev_->GetPath().c_str(), O_RDWR | O_CLOEXEC));
ASSERT_GE(base_fd_, 0);
- rnd_fd.reset(open("/dev/random", O_RDONLY));
- ASSERT_TRUE(rnd_fd > 0);
+ unique_fd rnd_fd(open("/dev/random", O_RDONLY));
+ ASSERT_GE(rnd_fd, 0);
std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(1_MiB);
@@ -242,13 +115,222 @@
}
ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0);
+}
- base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
- ASSERT_TRUE(base_loop_->valid());
+std::unique_ptr<ICowWriter> SnapuserdTestBase::CreateCowDeviceInternal() {
+ cow_system_ = std::make_unique<TemporaryFile>();
+
+ CowOptions options;
+ options.compression = "gz";
+
+ unique_fd fd(cow_system_->fd);
+ cow_system_->fd = -1;
+
+ return CreateCowWriter(kDefaultCowVersion, options, std::move(fd));
+}
+
+void SnapuserdTestBase::CreateCowDevice() {
+ unique_fd rnd_fd;
+ loff_t offset = 0;
+
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
+
+ rnd_fd.reset(open("/dev/random", O_RDONLY));
+ ASSERT_TRUE(rnd_fd > 0);
+
+ std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
+
+ // Fill random data
+ for (size_t j = 0; j < (size_ / 1_MiB); j++) {
+ ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
+ true);
+
+ offset += 1_MiB;
+ }
+
+ size_t num_blocks = size_ / writer->GetBlockSize();
+ size_t blk_end_copy = num_blocks * 2;
+ size_t source_blk = num_blocks - 1;
+ size_t blk_src_copy = blk_end_copy - 1;
+
+ uint32_t sequence[num_blocks * 2];
+ // Sequence for Copy ops
+ for (int i = 0; i < num_blocks; i++) {
+ sequence[i] = num_blocks - 1 - i;
+ }
+ // Sequence for Xor ops
+ for (int i = 0; i < num_blocks; i++) {
+ sequence[num_blocks + i] = 5 * num_blocks - 1 - i;
+ }
+ ASSERT_TRUE(writer->AddSequenceData(2 * num_blocks, sequence));
+
+ size_t x = num_blocks;
+ while (1) {
+ ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
+ x -= 1;
+ if (x == 0) {
+ break;
+ }
+ source_blk -= 1;
+ blk_src_copy -= 1;
+ }
+
+ source_blk = num_blocks;
+ blk_src_copy = blk_end_copy;
+
+ ASSERT_TRUE(writer->AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
+
+ size_t blk_zero_copy_start = source_blk + num_blocks;
+ size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks;
+
+ ASSERT_TRUE(writer->AddZeroBlocks(blk_zero_copy_start, num_blocks));
+
+ size_t blk_random2_replace_start = blk_zero_copy_end;
+
+ ASSERT_TRUE(writer->AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
+
+ size_t blk_xor_start = blk_random2_replace_start + num_blocks;
+ size_t xor_offset = BLOCK_SZ / 2;
+ ASSERT_TRUE(writer->AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
+ xor_offset));
+
+ // Flush operations
+ ASSERT_TRUE(writer->Finalize());
+ // Construct the buffer required for validation
+ orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
+ std::string zero_buffer(size_, 0);
+ ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), size_, size_), true);
+ memcpy((char*)orig_buffer_.get() + size_, random_buffer_1_.get(), size_);
+ memcpy((char*)orig_buffer_.get() + (size_ * 2), (void*)zero_buffer.c_str(), size_);
+ memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_1_.get(), size_);
+ ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, &orig_buffer_.get()[size_ * 4], size_,
+ size_ + xor_offset),
+ true);
+ for (int i = 0; i < size_; i++) {
+ orig_buffer_.get()[(size_ * 4) + i] =
+ (uint8_t)(orig_buffer_.get()[(size_ * 4) + i] ^ random_buffer_1_.get()[i]);
+ }
+}
+
+void SnapuserdTestBase::SetDeviceControlName() {
+ system_device_name_.clear();
+ system_device_ctrl_name_.clear();
+
+ std::string str(cow_system_->path);
+ std::size_t found = str.find_last_of("/\\");
+ ASSERT_NE(found, std::string::npos);
+ system_device_name_ = str.substr(found + 1);
+
+ system_device_ctrl_name_ = system_device_name_ + "-ctrl";
+}
+
+class SnapuserdTest : public SnapuserdTestBase {
+ public:
+ void SetupDefault();
+ void SetupOrderedOps();
+ void SetupOrderedOpsInverted();
+ void SetupCopyOverlap_1();
+ void SetupCopyOverlap_2();
+ bool Merge();
+ void ValidateMerge();
+ void ReadSnapshotDeviceAndValidate();
+ void ReadSnapshotAndValidateOverlappingBlocks();
+ void Shutdown();
+ void MergeInterrupt();
+ void MergeInterruptFixed(int duration);
+ void MergeInterruptAndValidate(int duration);
+ void MergeInterruptRandomly(int max_duration);
+ bool StartMerge();
+ void CheckMergeCompletion();
+
+ static const uint64_t kSectorSize = 512;
+
+ protected:
+ void SetUp() override;
+ void TearDown() override;
+
+ void SetupImpl();
+
+ void SimulateDaemonRestart();
+
+ void CreateCowDeviceOrderedOps();
+ void CreateCowDeviceOrderedOpsInverted();
+ void CreateCowDeviceWithCopyOverlap_1();
+ void CreateCowDeviceWithCopyOverlap_2();
+ void SetupDaemon();
+ void InitCowDevice();
+ void InitDaemon();
+ void CreateUserDevice();
+
+ unique_ptr<IUserDevice> dmuser_dev_;
+
+ std::unique_ptr<uint8_t[]> merged_buffer_;
+ std::unique_ptr<SnapshotHandlerManager> handlers_;
+ int cow_num_sectors_;
+};
+
+void SnapuserdTest::SetUp() {
+ ASSERT_NO_FATAL_FAILURE(SnapuserdTestBase::SetUp());
+ handlers_ = std::make_unique<SnapshotHandlerManager>();
+}
+
+void SnapuserdTest::TearDown() {
+ SnapuserdTestBase::TearDown();
+ Shutdown();
+}
+
+void SnapuserdTest::Shutdown() {
+ if (dmuser_dev_) {
+ ASSERT_TRUE(dmuser_dev_->Destroy());
+ }
+
+ auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
+ ASSERT_TRUE(handlers_->DeleteHandler(system_device_ctrl_name_));
+ ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted(misc_device, 10s));
+ handlers_->TerminateMergeThreads();
+ handlers_->JoinAllThreads();
+ handlers_ = std::make_unique<SnapshotHandlerManager>();
+}
+
+void SnapuserdTest::SetupDefault() {
+ ASSERT_NO_FATAL_FAILURE(SetupImpl());
+}
+
+void SnapuserdTest::SetupOrderedOps() {
+ ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+ ASSERT_NO_FATAL_FAILURE(CreateCowDeviceOrderedOps());
+ ASSERT_NO_FATAL_FAILURE(SetupDaemon());
+}
+
+void SnapuserdTest::SetupOrderedOpsInverted() {
+ ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+ ASSERT_NO_FATAL_FAILURE(CreateCowDeviceOrderedOpsInverted());
+ ASSERT_NO_FATAL_FAILURE(SetupDaemon());
+}
+
+void SnapuserdTest::SetupCopyOverlap_1() {
+ ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+ ASSERT_NO_FATAL_FAILURE(CreateCowDeviceWithCopyOverlap_1());
+ ASSERT_NO_FATAL_FAILURE(SetupDaemon());
+}
+
+void SnapuserdTest::SetupCopyOverlap_2() {
+ ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+ ASSERT_NO_FATAL_FAILURE(CreateCowDeviceWithCopyOverlap_2());
+ ASSERT_NO_FATAL_FAILURE(SetupDaemon());
+}
+
+void SnapuserdTest::SetupDaemon() {
+ SetDeviceControlName();
+
+ ASSERT_NO_FATAL_FAILURE(CreateUserDevice());
+ ASSERT_NO_FATAL_FAILURE(InitCowDevice());
+ ASSERT_NO_FATAL_FAILURE(InitDaemon());
}
void SnapuserdTest::ReadSnapshotDeviceAndValidate() {
- unique_fd fd(open(dmuser_dev_->path().c_str(), O_RDONLY));
+ unique_fd fd(open(dmuser_dev_->GetPath().c_str(), O_RDONLY));
ASSERT_GE(fd, 0);
std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
@@ -278,17 +360,74 @@
ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);
}
-std::unique_ptr<ICowWriter> SnapuserdTest::CreateCowDeviceInternal() {
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+void SnapuserdTest::ReadSnapshotAndValidateOverlappingBlocks() {
+ // Open COW device
+ unique_fd fd(open(cow_system_->path, O_RDONLY));
+ ASSERT_GE(fd, 0);
- CowOptions options;
- options.compression = "gz";
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(fd));
- unique_fd fd(cow_system_->fd);
- cow_system_->fd = -1;
+ const auto& header = reader.GetHeader();
+ size_t total_mapped_addr_length = header.prefix.header_size + BUFFER_REGION_DEFAULT_SIZE;
- return CreateCowWriter(kDefaultCowVersion, options, std::move(fd));
+ ASSERT_GE(header.prefix.major_version, 2);
+
+ void* mapped_addr = mmap(NULL, total_mapped_addr_length, PROT_READ, MAP_SHARED, fd.get(), 0);
+ ASSERT_NE(mapped_addr, MAP_FAILED);
+
+ bool populate_data_from_scratch = false;
+ struct BufferState* ra_state =
+ reinterpret_cast<struct BufferState*>((char*)mapped_addr + header.prefix.header_size);
+ if (ra_state->read_ahead_state == kCowReadAheadDone) {
+ populate_data_from_scratch = true;
+ }
+
+ size_t num_merge_ops = header.num_merge_ops;
+ // We have some partial merge operations completed.
+ // To test the merge-resume path, forcefully corrupt the data of the base
+ // device for the offsets where the merge is still pending.
+ if (num_merge_ops && populate_data_from_scratch) {
+ std::string corrupt_buffer(4096, 0);
+ // Corrupt two blocks from the point where the merge has to be resumed by
+ // writing down zeroe's.
+ //
+ // Now, since this is a merge-resume path, the "correct" data should be
+ // in the scratch space of the COW device. When there is an I/O request
+ // from the snapshot device, the data has to be retrieved from the
+ // scratch space. If not and I/O is routed to the base device, we
+ // may end up with corruption.
+ off_t corrupt_offset = (num_merge_ops + 2) * 4096;
+
+ if (corrupt_offset < size_) {
+ ASSERT_EQ(android::base::WriteFullyAtOffset(base_fd_, (void*)corrupt_buffer.c_str(),
+ 4096, corrupt_offset),
+ true);
+ corrupt_offset -= 4096;
+ ASSERT_EQ(android::base::WriteFullyAtOffset(base_fd_, (void*)corrupt_buffer.c_str(),
+ 4096, corrupt_offset),
+ true);
+ fsync(base_fd_.get());
+ }
+ }
+
+ // Time to read the snapshot device.
+ unique_fd snapshot_fd(open(dmuser_dev_->GetPath().c_str(), O_RDONLY | O_DIRECT | O_SYNC));
+ ASSERT_GE(snapshot_fd, 0);
+
+ void* buff_addr;
+ ASSERT_EQ(posix_memalign(&buff_addr, 4096, size_), 0);
+
+ std::unique_ptr<void, decltype(&::free)> snapshot_buffer(buff_addr, ::free);
+
+ // Scan the entire snapshot device and read the data and verify data
+ // integrity. Since the base device was forcefully corrupted, the data from
+ // this scan should be retrieved from scratch space of the COW partition.
+ //
+ // Furthermore, after the merge is complete, base device data is again
+ // verified as the aforementioned corrupted blocks aren't persisted.
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapshot_buffer.get(), size_, 0), true);
+ ASSERT_EQ(memcmp(snapshot_buffer.get(), orig_buffer_.get(), size_), 0);
}
void SnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
@@ -487,145 +626,42 @@
}
}
-void SnapuserdTest::CreateCowDevice() {
- unique_fd rnd_fd;
- loff_t offset = 0;
-
- auto writer = CreateCowDeviceInternal();
- ASSERT_NE(writer, nullptr);
-
- rnd_fd.reset(open("/dev/random", O_RDONLY));
- ASSERT_TRUE(rnd_fd > 0);
-
- std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
-
- // Fill random data
- for (size_t j = 0; j < (size_ / 1_MiB); j++) {
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
- true);
-
- offset += 1_MiB;
- }
-
- size_t num_blocks = size_ / writer->GetBlockSize();
- size_t blk_end_copy = num_blocks * 2;
- size_t source_blk = num_blocks - 1;
- size_t blk_src_copy = blk_end_copy - 1;
-
- uint32_t sequence[num_blocks * 2];
- // Sequence for Copy ops
- for (int i = 0; i < num_blocks; i++) {
- sequence[i] = num_blocks - 1 - i;
- }
- // Sequence for Xor ops
- for (int i = 0; i < num_blocks; i++) {
- sequence[num_blocks + i] = 5 * num_blocks - 1 - i;
- }
- ASSERT_TRUE(writer->AddSequenceData(2 * num_blocks, sequence));
-
- size_t x = num_blocks;
- while (1) {
- ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
- x -= 1;
- if (x == 0) {
- break;
- }
- source_blk -= 1;
- blk_src_copy -= 1;
- }
-
- source_blk = num_blocks;
- blk_src_copy = blk_end_copy;
-
- ASSERT_TRUE(writer->AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
-
- size_t blk_zero_copy_start = source_blk + num_blocks;
- size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks;
-
- ASSERT_TRUE(writer->AddZeroBlocks(blk_zero_copy_start, num_blocks));
-
- size_t blk_random2_replace_start = blk_zero_copy_end;
-
- ASSERT_TRUE(writer->AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
-
- size_t blk_xor_start = blk_random2_replace_start + num_blocks;
- size_t xor_offset = BLOCK_SZ / 2;
- ASSERT_TRUE(writer->AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
- xor_offset));
-
- // Flush operations
- ASSERT_TRUE(writer->Finalize());
- // Construct the buffer required for validation
- orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
- std::string zero_buffer(size_, 0);
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), size_, size_), true);
- memcpy((char*)orig_buffer_.get() + size_, random_buffer_1_.get(), size_);
- memcpy((char*)orig_buffer_.get() + (size_ * 2), (void*)zero_buffer.c_str(), size_);
- memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_1_.get(), size_);
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, &orig_buffer_.get()[size_ * 4], size_,
- size_ + xor_offset),
- true);
- for (int i = 0; i < size_; i++) {
- orig_buffer_.get()[(size_ * 4) + i] =
- (uint8_t)(orig_buffer_.get()[(size_ * 4) + i] ^ random_buffer_1_.get()[i]);
- }
-}
-
void SnapuserdTest::InitCowDevice() {
bool use_iouring = true;
if (FLAGS_force_config == "iouring_disabled") {
use_iouring = false;
}
+ auto factory = harness_->GetBlockServerFactory();
+ auto opener = factory->CreateOpener(system_device_ctrl_name_);
auto handler =
- handlers_.AddHandler(system_device_ctrl_name_, cow_system_->path, base_loop_->device(),
- base_loop_->device(), 1, use_iouring, false);
+ handlers_->AddHandler(system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(),
+ base_dev_->GetPath(), opener, 1, use_iouring, false);
ASSERT_NE(handler, nullptr);
ASSERT_NE(handler->snapuserd(), nullptr);
+#ifdef __ANDROID__
ASSERT_NE(handler->snapuserd()->GetNumSectors(), 0);
+#endif
}
-void SnapuserdTest::SetDeviceControlName() {
- system_device_name_.clear();
- system_device_ctrl_name_.clear();
-
- std::string str(cow_system_->path);
- std::size_t found = str.find_last_of("/\\");
- ASSERT_NE(found, std::string::npos);
- system_device_name_ = str.substr(found + 1);
-
- system_device_ctrl_name_ = system_device_name_ + "-ctrl";
-}
-
-void SnapuserdTest::CreateDmUserDevice() {
- unique_fd fd(TEMP_FAILURE_RETRY(open(base_loop_->device().c_str(), O_RDONLY | O_CLOEXEC)));
- ASSERT_TRUE(fd > 0);
-
- uint64_t dev_sz = get_block_device_size(fd.get());
- ASSERT_TRUE(dev_sz > 0);
+void SnapuserdTest::CreateUserDevice() {
+ auto dev_sz = base_dev_->GetSize();
+ ASSERT_NE(dev_sz, 0);
cow_num_sectors_ = dev_sz >> 9;
- DmTable dmuser_table;
- ASSERT_TRUE(dmuser_table.AddTarget(
- std::make_unique<DmTargetUser>(0, cow_num_sectors_, system_device_ctrl_name_)));
- ASSERT_TRUE(dmuser_table.valid());
-
- dmuser_dev_ = std::make_unique<Tempdevice>(system_device_name_, dmuser_table);
- ASSERT_TRUE(dmuser_dev_->valid());
- ASSERT_FALSE(dmuser_dev_->path().empty());
-
- auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
- ASSERT_TRUE(android::fs_mgr::WaitForFile(misc_device, 10s));
+ dmuser_dev_ = harness_->CreateUserDevice(system_device_name_, system_device_ctrl_name_,
+ cow_num_sectors_);
+ ASSERT_NE(dmuser_dev_, nullptr);
}
void SnapuserdTest::InitDaemon() {
- ASSERT_TRUE(handlers_.StartHandler(system_device_ctrl_name_));
+ ASSERT_TRUE(handlers_->StartHandler(system_device_ctrl_name_));
}
void SnapuserdTest::CheckMergeCompletion() {
while (true) {
- double percentage = handlers_.GetMergePercentage();
+ double percentage = handlers_->GetMergePercentage();
if ((int)percentage == 100) {
break;
}
@@ -635,27 +671,26 @@
}
void SnapuserdTest::SetupImpl() {
- CreateBaseDevice();
- CreateCowDevice();
+ ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+ ASSERT_NO_FATAL_FAILURE(CreateCowDevice());
SetDeviceControlName();
- CreateDmUserDevice();
- InitCowDevice();
- InitDaemon();
-
- setup_ok_ = true;
+ ASSERT_NO_FATAL_FAILURE(CreateUserDevice());
+ ASSERT_NO_FATAL_FAILURE(InitCowDevice());
+ ASSERT_NO_FATAL_FAILURE(InitDaemon());
}
bool SnapuserdTest::Merge() {
- StartMerge();
+ if (!StartMerge()) {
+ return false;
+ }
CheckMergeCompletion();
- merge_ok_ = true;
- return merge_ok_;
+ return true;
}
-void SnapuserdTest::StartMerge() {
- ASSERT_TRUE(handlers_.InitiateMerge(system_device_ctrl_name_));
+bool SnapuserdTest::StartMerge() {
+ return handlers_->InitiateMerge(system_device_ctrl_name_);
}
void SnapuserdTest::ValidateMerge() {
@@ -666,158 +701,287 @@
}
void SnapuserdTest::SimulateDaemonRestart() {
- Shutdown();
+ ASSERT_NO_FATAL_FAILURE(Shutdown());
std::this_thread::sleep_for(500ms);
SetDeviceControlName();
- CreateDmUserDevice();
- InitCowDevice();
- InitDaemon();
+ ASSERT_NO_FATAL_FAILURE(CreateUserDevice());
+ ASSERT_NO_FATAL_FAILURE(InitCowDevice());
+ ASSERT_NO_FATAL_FAILURE(InitDaemon());
}
void SnapuserdTest::MergeInterruptRandomly(int max_duration) {
std::srand(std::time(nullptr));
- StartMerge();
+ ASSERT_TRUE(StartMerge());
for (int i = 0; i < 20; i++) {
int duration = std::rand() % max_duration;
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
- SimulateDaemonRestart();
- StartMerge();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
+ ASSERT_TRUE(StartMerge());
}
- SimulateDaemonRestart();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ASSERT_TRUE(Merge());
}
void SnapuserdTest::MergeInterruptFixed(int duration) {
- StartMerge();
+ ASSERT_TRUE(StartMerge());
for (int i = 0; i < 25; i++) {
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
- SimulateDaemonRestart();
- StartMerge();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
+ ASSERT_TRUE(StartMerge());
}
- SimulateDaemonRestart();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
+ ASSERT_TRUE(Merge());
+}
+
+void SnapuserdTest::MergeInterruptAndValidate(int duration) {
+ ASSERT_TRUE(StartMerge());
+
+ for (int i = 0; i < 15; i++) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(duration));
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
+ ReadSnapshotAndValidateOverlappingBlocks();
+ ASSERT_TRUE(StartMerge());
+ }
+
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ASSERT_TRUE(Merge());
}
void SnapuserdTest::MergeInterrupt() {
// Interrupt merge at various intervals
- StartMerge();
+ ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(250ms);
- SimulateDaemonRestart();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
- StartMerge();
+ ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(250ms);
- SimulateDaemonRestart();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
- StartMerge();
+ ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(150ms);
- SimulateDaemonRestart();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
- StartMerge();
+ ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(100ms);
- SimulateDaemonRestart();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
- StartMerge();
+ ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(800ms);
- SimulateDaemonRestart();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
- StartMerge();
+ ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(600ms);
- SimulateDaemonRestart();
+ ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ASSERT_TRUE(Merge());
}
TEST_F(SnapuserdTest, Snapshot_IO_TEST) {
- ASSERT_TRUE(SetupDefault());
+ if (!harness_->HasUserDevice()) {
+ GTEST_SKIP() << "Skipping snapshot read; not supported";
+ }
+ ASSERT_NO_FATAL_FAILURE(SetupDefault());
// I/O before merge
- ReadSnapshotDeviceAndValidate();
+ ASSERT_NO_FATAL_FAILURE(ReadSnapshotDeviceAndValidate());
ASSERT_TRUE(Merge());
ValidateMerge();
// I/O after merge - daemon should read directly
// from base device
- ReadSnapshotDeviceAndValidate();
- Shutdown();
+ ASSERT_NO_FATAL_FAILURE(ReadSnapshotDeviceAndValidate());
}
TEST_F(SnapuserdTest, Snapshot_MERGE_IO_TEST) {
- ASSERT_TRUE(SetupDefault());
+ if (!harness_->HasUserDevice()) {
+ GTEST_SKIP() << "Skipping snapshot read; not supported";
+ }
+ ASSERT_NO_FATAL_FAILURE(SetupDefault());
// Issue I/O before merge begins
- std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
+ auto read_future =
+ std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
// Start the merge
ASSERT_TRUE(Merge());
ValidateMerge();
- Shutdown();
+ read_future.wait();
}
TEST_F(SnapuserdTest, Snapshot_MERGE_IO_TEST_1) {
- ASSERT_TRUE(SetupDefault());
+ if (!harness_->HasUserDevice()) {
+ GTEST_SKIP() << "Skipping snapshot read; not supported";
+ }
+ ASSERT_NO_FATAL_FAILURE(SetupDefault());
// Start the merge
- StartMerge();
+ ASSERT_TRUE(StartMerge());
// Issue I/O in parallel when merge is in-progress
- std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
+ auto read_future =
+ std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
CheckMergeCompletion();
ValidateMerge();
- Shutdown();
+ read_future.wait();
}
TEST_F(SnapuserdTest, Snapshot_Merge_Resume) {
- ASSERT_TRUE(SetupDefault());
- MergeInterrupt();
+ ASSERT_NO_FATAL_FAILURE(SetupDefault());
+ ASSERT_NO_FATAL_FAILURE(MergeInterrupt());
ValidateMerge();
- Shutdown();
}
TEST_F(SnapuserdTest, Snapshot_COPY_Overlap_TEST_1) {
- ASSERT_TRUE(SetupCopyOverlap_1());
+ ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_1());
ASSERT_TRUE(Merge());
ValidateMerge();
- Shutdown();
}
TEST_F(SnapuserdTest, Snapshot_COPY_Overlap_TEST_2) {
- ASSERT_TRUE(SetupCopyOverlap_2());
+ ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_2());
ASSERT_TRUE(Merge());
ValidateMerge();
- Shutdown();
}
TEST_F(SnapuserdTest, Snapshot_COPY_Overlap_Merge_Resume_TEST) {
- ASSERT_TRUE(SetupCopyOverlap_1());
- MergeInterrupt();
+ ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_1());
+ ASSERT_NO_FATAL_FAILURE(MergeInterrupt());
ValidateMerge();
- Shutdown();
+}
+
+TEST_F(SnapuserdTest, Snapshot_COPY_Overlap_Merge_Resume_IO_Validate_TEST) {
+ if (!harness_->HasUserDevice()) {
+ GTEST_SKIP() << "Skipping snapshot read; not supported";
+ }
+ ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_2());
+ ASSERT_NO_FATAL_FAILURE(MergeInterruptAndValidate(2));
+ ValidateMerge();
}
TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Fixed_Ordered) {
- ASSERT_TRUE(SetupOrderedOps());
- MergeInterruptFixed(300);
+ ASSERT_NO_FATAL_FAILURE(SetupOrderedOps());
+ ASSERT_NO_FATAL_FAILURE(MergeInterruptFixed(300));
ValidateMerge();
- Shutdown();
}
TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Random_Ordered) {
- ASSERT_TRUE(SetupOrderedOps());
- MergeInterruptRandomly(500);
+ ASSERT_NO_FATAL_FAILURE(SetupOrderedOps());
+ ASSERT_NO_FATAL_FAILURE(MergeInterruptRandomly(500));
ValidateMerge();
- Shutdown();
}
TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Fixed_Inverted) {
- ASSERT_TRUE(SetupOrderedOpsInverted());
- MergeInterruptFixed(50);
+ ASSERT_NO_FATAL_FAILURE(SetupOrderedOpsInverted());
+ ASSERT_NO_FATAL_FAILURE(MergeInterruptFixed(50));
ValidateMerge();
- Shutdown();
}
TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Random_Inverted) {
- ASSERT_TRUE(SetupOrderedOpsInverted());
- MergeInterruptRandomly(50);
+ ASSERT_NO_FATAL_FAILURE(SetupOrderedOpsInverted());
+ ASSERT_NO_FATAL_FAILURE(MergeInterruptRandomly(50));
ValidateMerge();
- Shutdown();
+}
+
+class HandlerTest : public SnapuserdTestBase {
+ protected:
+ void SetUp() override;
+ void TearDown() override;
+
+ AssertionResult ReadSectors(sector_t sector, uint64_t size, void* buffer);
+
+ TestBlockServerFactory factory_;
+ std::shared_ptr<TestBlockServerOpener> opener_;
+ std::shared_ptr<SnapshotHandler> handler_;
+ std::unique_ptr<ReadWorker> read_worker_;
+ TestBlockServer* block_server_;
+ std::future<bool> handler_thread_;
+};
+
+void HandlerTest::SetUp() {
+ ASSERT_NO_FATAL_FAILURE(SnapuserdTestBase::SetUp());
+ ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+ ASSERT_NO_FATAL_FAILURE(CreateCowDevice());
+ ASSERT_NO_FATAL_FAILURE(SetDeviceControlName());
+
+ opener_ = factory_.CreateTestOpener(system_device_ctrl_name_);
+ ASSERT_NE(opener_, nullptr);
+
+ handler_ = std::make_shared<SnapshotHandler>(system_device_ctrl_name_, cow_system_->path,
+ base_dev_->GetPath(), base_dev_->GetPath(),
+ opener_, 1, false, false);
+ ASSERT_TRUE(handler_->InitCowDevice());
+ ASSERT_TRUE(handler_->InitializeWorkers());
+
+ read_worker_ = std::make_unique<ReadWorker>(cow_system_->path, base_dev_->GetPath(),
+ system_device_ctrl_name_, base_dev_->GetPath(),
+ handler_->GetSharedPtr(), opener_);
+ ASSERT_TRUE(read_worker_->Init());
+ block_server_ = static_cast<TestBlockServer*>(read_worker_->block_server());
+
+ handler_thread_ = std::async(std::launch::async, &SnapshotHandler::Start, handler_.get());
+}
+
+void HandlerTest::TearDown() {
+ ASSERT_TRUE(factory_.DeleteQueue(system_device_ctrl_name_));
+ ASSERT_TRUE(handler_thread_.get());
+ SnapuserdTestBase::TearDown();
+}
+
+AssertionResult HandlerTest::ReadSectors(sector_t sector, uint64_t size, void* buffer) {
+ if (!read_worker_->RequestSectors(sector, size)) {
+ return AssertionFailure() << "request sectors failed";
+ }
+
+ std::string result = std::move(block_server_->sent_io());
+ if (result.size() != size) {
+ return AssertionFailure() << "size mismatch in result, got " << result.size()
+ << ", expected " << size;
+ }
+
+ memcpy(buffer, result.data(), size);
+ return AssertionSuccess();
+}
+
+// This test mirrors ReadSnapshotDeviceAndValidate.
+TEST_F(HandlerTest, Read) {
+ std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
+
+ // COPY
+ loff_t offset = 0;
+ ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), size_), 0);
+
+ // REPLACE
+ offset += size_;
+ ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + size_, size_), 0);
+
+ // ZERO
+ offset += size_;
+ ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 2), size_), 0);
+
+ // REPLACE
+ offset += size_;
+ ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 3), size_), 0);
+
+ // XOR
+ offset += size_;
+ ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);
+}
+
+TEST_F(HandlerTest, ReadUnalignedSector) {
+ std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(BLOCK_SZ);
+
+ ASSERT_TRUE(ReadSectors(1, BLOCK_SZ, snapuserd_buffer.get()));
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get() + SECTOR_SIZE, BLOCK_SZ), 0);
+}
+
+TEST_F(HandlerTest, ReadUnalignedSize) {
+ std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(SECTOR_SIZE);
+
+ ASSERT_TRUE(ReadSectors(0, SECTOR_SIZE, snapuserd_buffer.get()));
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), SECTOR_SIZE), 0);
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
index 28c9f68..8d090bf 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
@@ -189,33 +189,32 @@
cv.notify_all();
}
+static inline bool IsMergeBeginError(MERGE_IO_TRANSITION io_state) {
+ return io_state == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE ||
+ io_state == MERGE_IO_TRANSITION::IO_TERMINATED;
+}
+
// Invoked by Merge thread - Waits on RA thread to resume merging. Will
// be waken up RA thread.
bool SnapshotHandler::WaitForMergeBegin() {
- {
- std::unique_lock<std::mutex> lock(lock_);
- while (!MergeInitiated()) {
- cv.wait(lock);
+ std::unique_lock<std::mutex> lock(lock_);
- if (io_state_ == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE ||
- io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED) {
- return false;
- }
- }
+ cv.wait(lock, [this]() -> bool { return MergeInitiated() || IsMergeBeginError(io_state_); });
- while (!(io_state_ == MERGE_IO_TRANSITION::MERGE_BEGIN ||
- io_state_ == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE ||
- io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED)) {
- cv.wait(lock);
- }
-
- if (io_state_ == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE ||
- io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED) {
- return false;
- }
-
- return true;
+ if (IsMergeBeginError(io_state_)) {
+ SNAP_LOG(ERROR) << "WaitForMergeBegin failed with state: " << io_state_;
+ return false;
}
+
+ cv.wait(lock, [this]() -> bool {
+ return io_state_ == MERGE_IO_TRANSITION::MERGE_BEGIN || IsMergeBeginError(io_state_);
+ });
+
+ if (IsMergeBeginError(io_state_)) {
+ SNAP_LOG(ERROR) << "WaitForMergeBegin failed with state: " << io_state_;
+ return false;
+ }
+ return true;
}
// Invoked by RA thread - Flushes the RA block to scratch space if necessary
@@ -277,6 +276,7 @@
if (io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED ||
io_state_ == MERGE_IO_TRANSITION::MERGE_COMPLETE ||
io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED) {
+ SNAP_LOG(ERROR) << "Wait for merge ready failed: " << io_state_;
return false;
}
return true;
@@ -366,6 +366,26 @@
}
}
+void SnapshotHandler::RaThreadStarted() {
+ std::unique_lock<std::mutex> lock(lock_);
+ ra_thread_started_ = true;
+}
+
+void SnapshotHandler::WaitForRaThreadToStart() {
+ auto now = std::chrono::system_clock::now();
+ auto deadline = now + 3s;
+ {
+ std::unique_lock<std::mutex> lock(lock_);
+ while (!ra_thread_started_) {
+ auto status = cv.wait_until(lock, deadline);
+ if (status == std::cv_status::timeout) {
+ SNAP_LOG(ERROR) << "Read-ahead thread did not start";
+ return;
+ }
+ }
+ }
+}
+
std::string SnapshotHandler::GetMergeStatus() {
bool merge_not_initiated = false;
bool merge_monitored = false;
@@ -618,7 +638,6 @@
std::unordered_map<uint64_t, void*>::iterator it = read_ahead_buffer_map_.find(block);
if (it == read_ahead_buffer_map_.end()) {
- SNAP_LOG(ERROR) << "Block: " << block << " not found in RA buffer";
return false;
}
@@ -642,6 +661,13 @@
MERGE_GROUP_STATE state = blk_state->merge_state_;
switch (state) {
case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {
+ // If this is a merge-resume path, check if the data is
+ // available from scratch space. Data from scratch space takes
+ // higher precedence than from source device for overlapping
+ // blocks.
+ if (resume_merge_ && GetRABuffer(&lock, new_block, buffer)) {
+ return (MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS);
+ }
blk_state->num_ios_in_progress += 1; // ref count
[[fallthrough]];
}
@@ -668,5 +694,26 @@
}
}
+std::ostream& operator<<(std::ostream& os, MERGE_IO_TRANSITION value) {
+ switch (value) {
+ case MERGE_IO_TRANSITION::INVALID:
+ return os << "INVALID";
+ case MERGE_IO_TRANSITION::MERGE_READY:
+ return os << "MERGE_READY";
+ case MERGE_IO_TRANSITION::MERGE_BEGIN:
+ return os << "MERGE_BEGIN";
+ case MERGE_IO_TRANSITION::MERGE_FAILED:
+ return os << "MERGE_FAILED";
+ case MERGE_IO_TRANSITION::MERGE_COMPLETE:
+ return os << "MERGE_COMPLETE";
+ case MERGE_IO_TRANSITION::IO_TERMINATED:
+ return os << "IO_TERMINATED";
+ case MERGE_IO_TRANSITION::READ_AHEAD_FAILURE:
+ return os << "READ_AHEAD_FAILURE";
+ default:
+ return os << "unknown";
+ }
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.cpp
new file mode 100644
index 0000000..65208fb
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.cpp
@@ -0,0 +1,69 @@
+// Copyright (C) 2023 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 "worker.h"
+
+#include "snapuserd_core.h"
+
+namespace android {
+namespace snapshot {
+
+Worker::Worker(const std::string& cow_device, const std::string& misc_name,
+ const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd) {
+ cow_device_ = cow_device;
+ misc_name_ = misc_name;
+ base_path_merge_ = base_path_merge;
+ snapuserd_ = snapuserd;
+}
+
+bool Worker::Init() {
+ if (!InitializeFds()) {
+ return false;
+ }
+
+ if (!InitReader()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool Worker::InitReader() {
+ reader_ = snapuserd_->CloneReaderForWorker();
+
+ if (!reader_->InitForMerge(std::move(cow_fd_))) {
+ return false;
+ }
+ return true;
+}
+
+bool Worker::InitializeFds() {
+ cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
+ if (cow_fd_ < 0) {
+ SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
+ return false;
+ }
+
+ // Base device used by merge thread
+ base_path_merge_fd_.reset(open(base_path_merge_.c_str(), O_RDWR));
+ if (base_path_merge_fd_ < 0) {
+ SNAP_PLOG(ERROR) << "Open Failed: " << base_path_merge_;
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.h
new file mode 100644
index 0000000..c89d1b4
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2023 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 <stddef.h>
+
+#include <memory>
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_reader.h>
+#include <snapuserd/snapuserd_buffer.h>
+#include <snapuserd/snapuserd_kernel.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+
+class SnapshotHandler;
+
+class Worker {
+ public:
+ Worker(const std::string& cow_device, const std::string& misc_name,
+ const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd);
+ virtual ~Worker() = default;
+
+ virtual bool Init();
+
+ protected:
+ bool InitializeFds();
+ bool InitReader();
+ virtual void CloseFds() { base_path_merge_fd_ = {}; }
+
+ std::unique_ptr<CowReader> reader_;
+
+ std::string misc_name_; // Needed for SNAP_LOG.
+
+ unique_fd base_path_merge_fd_;
+
+ std::shared_ptr<SnapshotHandler> snapuserd_;
+
+ private:
+ std::string cow_device_;
+ std::string base_path_merge_;
+ unique_fd cow_fd_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/utility.cpp b/fs_mgr/libsnapshot/snapuserd/utility.cpp
new file mode 100644
index 0000000..a84a7c1
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/utility.cpp
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 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 "utility.h"
+
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+
+bool SetThreadPriority([[maybe_unused]] int priority) {
+#ifdef __ANDROID__
+ return setpriority(PRIO_PROCESS, gettid(), priority) != -1;
+#else
+ return true;
+#endif
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/utility.h b/fs_mgr/libsnapshot/snapuserd/utility.h
new file mode 100644
index 0000000..58ec3e6
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/utility.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 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
+
+namespace android {
+namespace snapshot {
+
+bool SetThreadPriority(int priority);
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/tools/Android.bp b/fs_mgr/libsnapshot/tools/Android.bp
new file mode 100644
index 0000000..0f08286
--- /dev/null
+++ b/fs_mgr/libsnapshot/tools/Android.bp
@@ -0,0 +1,45 @@
+
+cc_binary {
+ name: "cow_benchmark",
+ host_supported: true,
+ defaults: [
+ "fs_mgr_defaults",
+ "libsnapshot_cow_defaults",
+ ],
+
+ srcs: ["cow_benchmark.cpp"],
+
+ static_libs: [
+ "libsnapshot_cow",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ cflags: ["-Werror"],
+}
+
+
+cc_binary {
+ name: "basic_v2_cow_writer",
+ host_supported: true,
+ defaults: [
+ "fs_mgr_defaults",
+ "libsnapshot_cow_defaults",
+ ],
+
+ srcs: ["basic_v2_cow_writer.cpp"],
+
+ static_libs: [
+ "libsnapshot_cow",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ cflags: ["-Werror"],
+}
diff --git a/fs_mgr/libsnapshot/tools/basic_v2_cow_writer.cpp b/fs_mgr/libsnapshot/tools/basic_v2_cow_writer.cpp
new file mode 100644
index 0000000..72fb0f5
--- /dev/null
+++ b/fs_mgr/libsnapshot/tools/basic_v2_cow_writer.cpp
@@ -0,0 +1,63 @@
+//
+// Copyright (C) 2023 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/file.h>
+#include <android-base/logging.h>
+#include <libsnapshot/cow_compress.h>
+#include <libsnapshot/cow_format.h>
+#include <libsnapshot/cow_writer.h>
+#include <filesystem>
+
+#include "android-base/unique_fd.h"
+
+using namespace android::snapshot;
+
+// This writes a simple cow v2 file in the current directory. This file will serve as testdata for
+// ensuring our v3 cow reader will be able to read a cow file created by the v2 writer.
+//
+// WARNING: We should not be overriding this test file, as it will serve as historic marker for what
+// a device with old writer_v2 will write as a cow.
+void write_cow_v2() {
+ CowOptions options;
+ options.cluster_ops = 5;
+ options.num_merge_ops = 1;
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ char cwd_buffer[1024];
+ size_t cwd_buffer_size = sizeof(cwd_buffer);
+
+ // Get the current working directory path.
+ char* err = getcwd(cwd_buffer, cwd_buffer_size);
+ if (!err) {
+ LOG(ERROR) << "Couldn't get current directory";
+ }
+ android::base::unique_fd fd(open(strcat(cwd_buffer, "/cow_v2"), O_CREAT | O_RDWR, 0666));
+ if (fd.get() == -1) {
+ LOG(FATAL) << "couldn't open tmp_cow";
+ }
+ std::unique_ptr<ICowWriter> writer = CreateCowWriter(2, options, std::move(fd));
+ writer->AddCopy(0, 5);
+ writer->AddRawBlocks(2, data.data(), data.size());
+ writer->AddLabel(1);
+ writer->AddXorBlocks(50, data.data(), data.size(), 24, 10);
+ writer->AddZeroBlocks(5, 10);
+ writer->AddLabel(2);
+ writer->Finalize();
+}
+
+int main() {
+ write_cow_v2();
+}
diff --git a/fs_mgr/libsnapshot/tools/cow_benchmark.cpp b/fs_mgr/libsnapshot/tools/cow_benchmark.cpp
new file mode 100644
index 0000000..4d5e346
--- /dev/null
+++ b/fs_mgr/libsnapshot/tools/cow_benchmark.cpp
@@ -0,0 +1,202 @@
+//
+// Copyright (C) 2023 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 <memory>
+
+#include <array>
+#include <iostream>
+#include <random>
+
+#include <libsnapshot/cow_compress.h>
+#include <libsnapshot/cow_format.h>
+
+static const uint32_t BLOCK_SZ = 4096;
+static const uint32_t SEED_NUMBER = 10;
+
+namespace android {
+namespace snapshot {
+
+static std::string CompressionToString(CowCompression& compression) {
+ std::string output;
+ switch (compression.algorithm) {
+ case kCowCompressBrotli:
+ output.append("brotli");
+ break;
+ case kCowCompressGz:
+ output.append("gz");
+ break;
+ case kCowCompressLz4:
+ output.append("lz4");
+ break;
+ case kCowCompressZstd:
+ output.append("zstd");
+ break;
+ case kCowCompressNone:
+ return "No Compression";
+ }
+ output.append(" " + std::to_string(compression.compression_level));
+ return output;
+}
+
+void OneShotCompressionTest() {
+ std::cout << "\n-------One Shot Compressor Perf Analysis-------\n";
+
+ std::vector<CowCompression> compression_list = {
+ {kCowCompressLz4, 0}, {kCowCompressBrotli, 1}, {kCowCompressBrotli, 3},
+ {kCowCompressBrotli, 11}, {kCowCompressZstd, 3}, {kCowCompressZstd, 6},
+ {kCowCompressZstd, 9}, {kCowCompressZstd, 22}, {kCowCompressGz, 1},
+ {kCowCompressGz, 3}, {kCowCompressGz, 6}, {kCowCompressGz, 9}};
+ std::vector<std::unique_ptr<ICompressor>> compressors;
+ for (auto i : compression_list) {
+ compressors.emplace_back(ICompressor::Create(i, BLOCK_SZ));
+ }
+
+ // Allocate a buffer of size 8 blocks.
+ std::array<char, 32768> buffer;
+
+ // Generate a random 4k buffer of characters
+ std::default_random_engine gen(SEED_NUMBER);
+ std::uniform_int_distribution<int> distribution(0, 10);
+ for (int i = 0; i < buffer.size(); i++) {
+ buffer[i] = static_cast<char>(distribution(gen));
+ }
+
+ std::vector<std::pair<double, std::string>> latencies;
+ std::vector<std::pair<double, std::string>> ratios;
+
+ for (size_t i = 0; i < compressors.size(); i++) {
+ const auto start = std::chrono::steady_clock::now();
+ std::basic_string<uint8_t> compressed_data =
+ compressors[i]->Compress(buffer.data(), buffer.size());
+ const auto end = std::chrono::steady_clock::now();
+ const auto latency =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(end - start) / 1000.0;
+ const double compression_ratio =
+ static_cast<uint16_t>(compressed_data.size()) * 1.00 / buffer.size();
+
+ std::cout << "Metrics for " << CompressionToString(compression_list[i]) << ": latency -> "
+ << latency.count() << "ms "
+ << " compression ratio ->" << compression_ratio << " \n";
+
+ latencies.emplace_back(
+ std::make_pair(latency.count(), CompressionToString(compression_list[i])));
+ ratios.emplace_back(
+ std::make_pair(compression_ratio, CompressionToString(compression_list[i])));
+ }
+
+ int best_speed = 0;
+ int best_ratio = 0;
+
+ for (size_t i = 1; i < latencies.size(); i++) {
+ if (latencies[i].first < latencies[best_speed].first) {
+ best_speed = i;
+ }
+ if (ratios[i].first < ratios[best_ratio].first) {
+ best_ratio = i;
+ }
+ }
+
+ std::cout << "BEST SPEED: " << latencies[best_speed].first << "ms "
+ << latencies[best_speed].second << "\n";
+ std::cout << "BEST RATIO: " << ratios[best_ratio].first << " " << ratios[best_ratio].second
+ << "\n";
+}
+
+void IncrementalCompressionTest() {
+ std::cout << "\n-------Incremental Compressor Perf Analysis-------\n";
+
+ std::vector<CowCompression> compression_list = {
+ {kCowCompressLz4, 0}, {kCowCompressBrotli, 1}, {kCowCompressBrotli, 3},
+ {kCowCompressBrotli, 11}, {kCowCompressZstd, 3}, {kCowCompressZstd, 6},
+ {kCowCompressZstd, 9}, {kCowCompressZstd, 22}, {kCowCompressGz, 1},
+ {kCowCompressGz, 3}, {kCowCompressGz, 6}, {kCowCompressGz, 9}};
+ std::vector<std::unique_ptr<ICompressor>> compressors;
+ for (auto i : compression_list) {
+ compressors.emplace_back(ICompressor::Create(i, BLOCK_SZ));
+ }
+
+ // Allocate a buffer of size 8 blocks.
+ std::array<char, 32768> buffer;
+
+ // Generate a random 4k buffer of characters
+ std::default_random_engine gen(SEED_NUMBER);
+ std::uniform_int_distribution<int> distribution(0, 10);
+ for (int i = 0; i < buffer.size(); i++) {
+ buffer[i] = static_cast<char>(distribution(gen));
+ }
+
+ std::vector<std::pair<double, std::string>> latencies;
+ std::vector<std::pair<double, std::string>> ratios;
+
+ for (size_t i = 0; i < compressors.size(); i++) {
+ std::vector<std::basic_string<uint8_t>> compressed_data_vec;
+ int num_blocks = buffer.size() / BLOCK_SZ;
+ const uint8_t* iter = reinterpret_cast<const uint8_t*>(buffer.data());
+
+ const auto start = std::chrono::steady_clock::now();
+ while (num_blocks > 0) {
+ std::basic_string<uint8_t> compressed_data = compressors[i]->Compress(iter, BLOCK_SZ);
+ compressed_data_vec.emplace_back(compressed_data);
+ num_blocks--;
+ iter += BLOCK_SZ;
+ }
+
+ const auto end = std::chrono::steady_clock::now();
+ const auto latency =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(end - start) / 1000.0;
+
+ size_t size = 0;
+ for (auto& i : compressed_data_vec) {
+ size += i.size();
+ }
+ const double compression_ratio = size * 1.00 / buffer.size();
+
+ std::cout << "Metrics for " << CompressionToString(compression_list[i]) << ": latency -> "
+ << latency.count() << "ms "
+ << " compression ratio ->" << compression_ratio << " \n";
+
+ latencies.emplace_back(
+ std::make_pair(latency.count(), CompressionToString(compression_list[i])));
+ ratios.emplace_back(
+ std::make_pair(compression_ratio, CompressionToString(compression_list[i])));
+ }
+
+ int best_speed = 0;
+ int best_ratio = 0;
+
+ for (size_t i = 1; i < latencies.size(); i++) {
+ if (latencies[i].first < latencies[best_speed].first) {
+ best_speed = i;
+ }
+ if (ratios[i].first < ratios[best_ratio].first) {
+ best_ratio = i;
+ }
+ }
+
+ std::cout << "BEST SPEED: " << latencies[best_speed].first << "ms "
+ << latencies[best_speed].second << "\n";
+ std::cout << "BEST RATIO: " << ratios[best_ratio].first << " " << ratios[best_ratio].second
+ << "\n";
+}
+
+} // namespace snapshot
+} // namespace android
+
+int main() {
+ android::snapshot::OneShotCompressionTest();
+ android::snapshot::IncrementalCompressionTest();
+
+ return 0;
+}
\ No newline at end of file
diff --git a/fs_mgr/libsnapshot/tools/testdata/cow_v2 b/fs_mgr/libsnapshot/tools/testdata/cow_v2
new file mode 100644
index 0000000..9f37dfc
--- /dev/null
+++ b/fs_mgr/libsnapshot/tools/testdata/cow_v2
Binary files differ
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index b9bae25..2aeba0a 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -38,7 +38,8 @@
],
static_libs: [
"libfs_mgr",
- "libfstab",
+ "libgmock",
+ "libgtest",
],
srcs: [
"file_wait_test.cpp",
@@ -109,7 +110,6 @@
],
static_libs: [
"libfs_mgr",
- "libfstab",
"libgmock",
"libgtest",
],
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 5f889ca..322bf1b 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -29,11 +29,13 @@
#include <android-base/strings.h>
#include <fs_mgr.h>
#include <fstab/fstab.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include "../fs_mgr_priv_boot_config.h"
+#include "../fs_mgr_priv.h"
using namespace android::fs_mgr;
+using namespace testing;
namespace {
@@ -119,37 +121,42 @@
{"terminator", "truncated"},
};
-const std::string bootconfig =
- "androidboot.bootdevice = \"1d84000.ufshc\"\n"
- "androidboot.boot_devices = \"dev1\", \"dev2,withcomma\", \"dev3\"\n"
- "androidboot.baseband = \"sdy\"\n"
- "androidboot.keymaster = \"1\"\n"
- "androidboot.serialno = \"BLAHBLAHBLAH\"\n"
- "androidboot.slot_suffix = \"_a\"\n"
- "androidboot.hardware.platform = \"sdw813\"\n"
- "androidboot.hardware = \"foo\"\n"
- "androidboot.revision = \"EVT1.0\"\n"
- "androidboot.bootloader = \"burp-0.1-7521\"\n"
- "androidboot.hardware.sku = \"mary\"\n"
- "androidboot.hardware.radio.subtype = \"0\"\n"
- "androidboot.dtbo_idx = \"2\"\n"
- "androidboot.mode = \"normal\"\n"
- "androidboot.hardware.ddr = \"1GB,combuchi,LPDDR4X\"\n"
- "androidboot.ddr_info = \"combuchiandroidboot.ddr_size=2GB\"\n"
- "androidboot.hardware.ufs = \"2GB,combushi\"\n"
- "androidboot.boottime = \"0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123\"\n"
- "androidboot.ramdump = \"disabled\"\n"
- "androidboot.vbmeta.device = \"PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb\"\n"
- "androidboot.vbmeta.avb_version = \"1.1\"\n"
- "androidboot.vbmeta.device_state = \"unlocked\"\n"
- "androidboot.vbmeta.hash_alg = \"sha256\"\n"
- "androidboot.vbmeta.size = \"5248\"\n"
- "androidboot.vbmeta.digest = \""
- "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860\"\n"
- "androidboot.vbmeta.invalidate_on_error = \"yes\"\n"
- "androidboot.veritymode = \"enforcing\"\n"
- "androidboot.verifiedbootstate = \"orange\"\n"
- "androidboot.space = \"sha256 5248 androidboot.nospace = nope\"\n";
+const std::string bootconfig = R"(
+androidboot.bootdevice = "1d84000.ufshc"
+androidboot.boot_devices = "dev1", "dev2,withcomma", "dev3"
+androidboot.baseband = "sdy"
+androidboot.keymaster = "1"
+androidboot.serialno = "BLAHBLAHBLAH"
+androidboot.slot_suffix = "_a"
+androidboot.hardware.platform = "sdw813"
+androidboot.hardware = "foo"
+androidboot.revision = "EVT1.0"
+androidboot.bootloader = "burp-0.1-7521"
+androidboot.hardware.sku = "mary"
+androidboot.hardware.radio.subtype = "0"
+androidboot.dtbo_idx = "2"
+androidboot.mode = "normal"
+androidboot.hardware.ddr = "1GB,combuchi,LPDDR4X"
+androidboot.ddr_info = "combuchiandroidboot.ddr_size=2GB"
+androidboot.hardware.ufs = "2GB,combushi"
+androidboot.boottime = "0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123"
+androidboot.ramdump = "disabled"
+androidboot.vbmeta.device = "PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb"
+androidboot.vbmeta.avb_version = "1.1"
+androidboot.vbmeta.device_state = "unlocked"
+androidboot.vbmeta.hash_alg = "sha256"
+androidboot.vbmeta.size = "5248"
+androidboot.vbmeta.digest = "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860"
+androidboot.vbmeta.invalidate_on_error = "yes"
+androidboot.veritymode = "enforcing"
+androidboot.verifiedbootstate = "orange"
+androidboot.space = "sha256 5248 androidboot.nospace = nope"
+just.key
+key.empty.value =
+dessert.value = "ice, cream"
+dessert.list = "ice", "cream"
+ambiguous.list = ", ", ", "
+)";
const std::vector<std::pair<std::string, std::string>> bootconfig_result_space = {
{"androidboot.bootdevice", "1d84000.ufshc"},
@@ -182,6 +189,11 @@
{"androidboot.veritymode", "enforcing"},
{"androidboot.verifiedbootstate", "orange"},
{"androidboot.space", "sha256 5248 androidboot.nospace = nope"},
+ {"just.key", ""},
+ {"key.empty.value", ""},
+ {"dessert.value", "ice, cream"},
+ {"dessert.list", "ice,cream"},
+ {"ambiguous.list", ", ,, "},
};
bool CompareFlags(FstabEntry::FsMgrFlags& lhs, FstabEntry::FsMgrFlags& rhs) {
@@ -212,44 +224,61 @@
} // namespace
-TEST(fs_mgr, fs_mgr_parse_cmdline) {
- EXPECT_EQ(result_space, fs_mgr_parse_cmdline(cmdline));
+TEST(fs_mgr, ImportKernelCmdline) {
+ std::vector<std::pair<std::string, std::string>> result;
+ ImportKernelCmdlineFromString(
+ cmdline, [&](std::string key, std::string value) { result.emplace_back(key, value); });
+ EXPECT_THAT(result, ContainerEq(result_space));
}
-TEST(fs_mgr, fs_mgr_get_boot_config_from_kernel_cmdline) {
+TEST(fs_mgr, GetKernelCmdline) {
std::string content;
- for (const auto& entry : result_space) {
- static constexpr char androidboot[] = "androidboot.";
- if (!android::base::StartsWith(entry.first, androidboot)) continue;
- auto key = entry.first.substr(strlen(androidboot));
- EXPECT_TRUE(fs_mgr_get_boot_config_from_kernel(cmdline, key, &content)) << " for " << key;
- EXPECT_EQ(entry.second, content);
- }
- EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "vbmeta.avb_versio", &content));
- EXPECT_TRUE(content.empty()) << content;
- EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "nospace", &content));
- EXPECT_TRUE(content.empty()) << content;
-}
-
-TEST(fs_mgr, fs_mgr_parse_bootconfig) {
- EXPECT_EQ(bootconfig_result_space, fs_mgr_parse_proc_bootconfig(bootconfig));
-}
-
-TEST(fs_mgr, fs_mgr_get_boot_config_from_bootconfig) {
- std::string content;
- for (const auto& entry : bootconfig_result_space) {
- static constexpr char androidboot[] = "androidboot.";
- if (!android::base::StartsWith(entry.first, androidboot)) continue;
- auto key = entry.first.substr(strlen(androidboot));
- EXPECT_TRUE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, key, &content))
- << " for " << key;
- EXPECT_EQ(entry.second, content);
+ for (const auto& [key, value] : result_space) {
+ EXPECT_TRUE(GetKernelCmdlineFromString(cmdline, key, &content)) << " for " << key;
+ EXPECT_EQ(content, value);
}
- EXPECT_FALSE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, "vbmeta.avb_versio", &content));
- EXPECT_TRUE(content.empty()) << content;
- EXPECT_FALSE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, "nospace", &content));
- EXPECT_TRUE(content.empty()) << content;
+ const std::string kUnmodifiedToken = "<UNMODIFIED>";
+ content = kUnmodifiedToken;
+ EXPECT_FALSE(GetKernelCmdlineFromString(cmdline, "", &content));
+ EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+
+ content = kUnmodifiedToken;
+ EXPECT_FALSE(GetKernelCmdlineFromString(cmdline, "androidboot.vbmeta.avb_versio", &content));
+ EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+
+ content = kUnmodifiedToken;
+ EXPECT_FALSE(GetKernelCmdlineFromString(bootconfig, "androidboot.nospace", &content));
+ EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+}
+
+TEST(fs_mgr, ImportBootconfig) {
+ std::vector<std::pair<std::string, std::string>> result;
+ ImportBootconfigFromString(bootconfig, [&](std::string key, std::string value) {
+ result.emplace_back(key, value);
+ });
+ EXPECT_THAT(result, ContainerEq(bootconfig_result_space));
+}
+
+TEST(fs_mgr, GetBootconfig) {
+ std::string content;
+ for (const auto& [key, value] : bootconfig_result_space) {
+ EXPECT_TRUE(GetBootconfigFromString(bootconfig, key, &content)) << " for " << key;
+ EXPECT_EQ(content, value);
+ }
+
+ const std::string kUnmodifiedToken = "<UNMODIFIED>";
+ content = kUnmodifiedToken;
+ EXPECT_FALSE(GetBootconfigFromString(bootconfig, "", &content));
+ EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+
+ content = kUnmodifiedToken;
+ EXPECT_FALSE(GetBootconfigFromString(bootconfig, "androidboot.vbmeta.avb_versio", &content));
+ EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+
+ content = kUnmodifiedToken;
+ EXPECT_FALSE(GetBootconfigFromString(bootconfig, "androidboot.nospace", &content));
+ EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
}
TEST(fs_mgr, fs_mgr_read_fstab_file_proc_mounts) {
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
index 4d771fa..32947b5 100644
--- a/fs_mgr/tests/vts_fs_test.cpp
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -23,6 +23,8 @@
#include <gtest/gtest.h>
#include <libdm/dm.h>
+#include "../fs_mgr_priv.h"
+
using testing::Contains;
using testing::Not;
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index bd7955a..0c97632 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -361,7 +361,7 @@
void BatteryMonitor::updateValues(void) {
initHealthInfo(mHealthInfo.get());
- if (!mHealthdConfig->batteryPresentPath.isEmpty())
+ if (!mHealthdConfig->batteryPresentPath.empty())
mHealthInfo->batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
else
mHealthInfo->batteryPresent = mBatteryDevicePresent;
@@ -371,43 +371,43 @@
: getIntField(mHealthdConfig->batteryCapacityPath);
mHealthInfo->batteryVoltageMillivolts = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
- if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
+ if (!mHealthdConfig->batteryCurrentNowPath.empty())
mHealthInfo->batteryCurrentMicroamps = getIntField(mHealthdConfig->batteryCurrentNowPath);
- if (!mHealthdConfig->batteryFullChargePath.isEmpty())
+ if (!mHealthdConfig->batteryFullChargePath.empty())
mHealthInfo->batteryFullChargeUah = getIntField(mHealthdConfig->batteryFullChargePath);
- if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
+ if (!mHealthdConfig->batteryCycleCountPath.empty())
mHealthInfo->batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
- if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
+ if (!mHealthdConfig->batteryChargeCounterPath.empty())
mHealthInfo->batteryChargeCounterUah =
getIntField(mHealthdConfig->batteryChargeCounterPath);
- if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty())
+ if (!mHealthdConfig->batteryCurrentAvgPath.empty())
mHealthInfo->batteryCurrentAverageMicroamps =
getIntField(mHealthdConfig->batteryCurrentAvgPath);
- if (!mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+ if (!mHealthdConfig->batteryChargeTimeToFullNowPath.empty())
mHealthInfo->batteryChargeTimeToFullNowSeconds =
getIntField(mHealthdConfig->batteryChargeTimeToFullNowPath);
- if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+ if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())
mHealthInfo->batteryFullChargeDesignCapacityUah =
getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
- if (!mHealthdConfig->batteryHealthStatusPath.isEmpty())
+ if (!mHealthdConfig->batteryHealthStatusPath.empty())
mBatteryHealthStatus = getIntField(mHealthdConfig->batteryHealthStatusPath);
- if (!mHealthdConfig->batteryStateOfHealthPath.isEmpty())
+ if (!mHealthdConfig->batteryStateOfHealthPath.empty())
mHealthInfo->batteryHealthData->batteryStateOfHealth =
getIntField(mHealthdConfig->batteryStateOfHealthPath);
- if (!mHealthdConfig->batteryManufacturingDatePath.isEmpty())
+ if (!mHealthdConfig->batteryManufacturingDatePath.empty())
mHealthInfo->batteryHealthData->batteryManufacturingDateSeconds =
getIntField(mHealthdConfig->batteryManufacturingDatePath);
- if (!mHealthdConfig->batteryFirstUsageDatePath.isEmpty())
+ if (!mHealthdConfig->batteryFirstUsageDatePath.empty())
mHealthInfo->batteryHealthData->batteryFirstUsageSeconds =
getIntField(mHealthdConfig->batteryFirstUsageDatePath);
@@ -432,7 +432,7 @@
}
if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
- mHealthInfo->batteryTechnology = String8(buf.c_str());
+ mHealthInfo->batteryTechnology = buf;
if (readFromFile(mHealthdConfig->chargingPolicyPath, &buf) > 0)
mHealthInfo->chargingPolicy = getBatteryChargingPolicy(buf.c_str());
@@ -444,12 +444,10 @@
for (size_t i = 0; i < mChargerNames.size(); i++) {
String8 path;
- path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
+ path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());
if (getIntField(path)) {
path.clear();
- path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
+ path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());
switch(readPowerSupplyType(path)) {
case ANDROID_POWER_SUPPLY_TYPE_AC:
mHealthInfo->chargerAcOnline = true;
@@ -466,26 +464,24 @@
default:
path.clear();
path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
- if (access(path.string(), R_OK) == 0)
+ mChargerNames[i].c_str());
+ if (access(path.c_str(), R_OK) == 0)
mHealthInfo->chargerDockOnline = true;
else
KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
- mChargerNames[i].string());
+ mChargerNames[i].c_str());
}
path.clear();
path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
- int ChargingCurrent =
- (access(path.string(), R_OK) == 0) ? getIntField(path) : 0;
+ mChargerNames[i].c_str());
+ int ChargingCurrent = (access(path.c_str(), R_OK) == 0) ? getIntField(path) : 0;
path.clear();
path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
+ mChargerNames[i].c_str());
int ChargingVoltage =
- (access(path.string(), R_OK) == 0) ? getIntField(path) :
- DEFAULT_VBUS_VOLTAGE;
+ (access(path.c_str(), R_OK) == 0) ? getIntField(path) : DEFAULT_VBUS_VOLTAGE;
double power = ((double)ChargingCurrent / MILLION) *
((double)ChargingVoltage / MILLION);
@@ -510,17 +506,17 @@
props.batteryStatus);
len = strlen(dmesgline);
- if (!healthd_config.batteryCurrentNowPath.isEmpty()) {
+ if (!healthd_config.batteryCurrentNowPath.empty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
props.batteryCurrentMicroamps);
}
- if (!healthd_config.batteryFullChargePath.isEmpty()) {
+ if (!healthd_config.batteryFullChargePath.empty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
props.batteryFullChargeUah);
}
- if (!healthd_config.batteryCycleCountPath.isEmpty()) {
+ if (!healthd_config.batteryCycleCountPath.empty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
props.batteryCycleCount);
}
@@ -554,7 +550,7 @@
int BatteryMonitor::getChargeStatus() {
BatteryStatus result = BatteryStatus::UNKNOWN;
- if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
+ if (!mHealthdConfig->batteryStatusPath.empty()) {
std::string buf;
if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
result = getBatteryStatus(buf.c_str());
@@ -565,7 +561,7 @@
status_t BatteryMonitor::setChargingPolicy(int value) {
status_t ret = NAME_NOT_FOUND;
bool result;
- if (!mHealthdConfig->chargingPolicyPath.isEmpty()) {
+ if (!mHealthdConfig->chargingPolicyPath.empty()) {
result = writeToFile(mHealthdConfig->chargingPolicyPath, value);
if (!result) {
KLOG_WARNING(LOG_TAG, "setChargingPolicy fail\n");
@@ -579,7 +575,7 @@
int BatteryMonitor::getChargingPolicy() {
BatteryChargingPolicy result = BatteryChargingPolicy::DEFAULT;
- if (!mHealthdConfig->chargingPolicyPath.isEmpty()) {
+ if (!mHealthdConfig->chargingPolicyPath.empty()) {
std::string buf;
if (readFromFile(mHealthdConfig->chargingPolicyPath, &buf) > 0)
result = getBatteryChargingPolicy(buf.c_str());
@@ -589,15 +585,15 @@
int BatteryMonitor::getBatteryHealthData(int id) {
if (id == BATTERY_PROP_MANUFACTURING_DATE) {
- if (!mHealthdConfig->batteryManufacturingDatePath.isEmpty())
+ if (!mHealthdConfig->batteryManufacturingDatePath.empty())
return getIntField(mHealthdConfig->batteryManufacturingDatePath);
}
if (id == BATTERY_PROP_FIRST_USAGE_DATE) {
- if (!mHealthdConfig->batteryFirstUsageDatePath.isEmpty())
+ if (!mHealthdConfig->batteryFirstUsageDatePath.empty())
return getIntField(mHealthdConfig->batteryFirstUsageDatePath);
}
if (id == BATTERY_PROP_STATE_OF_HEALTH) {
- if (!mHealthdConfig->batteryStateOfHealthPath.isEmpty())
+ if (!mHealthdConfig->batteryStateOfHealthPath.empty())
return getIntField(mHealthdConfig->batteryStateOfHealthPath);
}
return 0;
@@ -611,7 +607,7 @@
switch(id) {
case BATTERY_PROP_CHARGE_COUNTER:
- if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ if (!mHealthdConfig->batteryChargeCounterPath.empty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryChargeCounterPath);
ret = OK;
@@ -621,7 +617,7 @@
break;
case BATTERY_PROP_CURRENT_NOW:
- if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCurrentNowPath);
ret = OK;
@@ -631,7 +627,7 @@
break;
case BATTERY_PROP_CURRENT_AVG:
- if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCurrentAvgPath);
ret = OK;
@@ -641,7 +637,7 @@
break;
case BATTERY_PROP_CAPACITY:
- if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCapacityPath.empty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCapacityPath);
ret = OK;
@@ -708,35 +704,35 @@
props.batteryVoltageMillivolts, props.batteryTemperatureTenthsCelsius);
write(fd, vs, strlen(vs));
- if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
v = getIntField(mHealthdConfig->batteryCurrentNowPath);
snprintf(vs, sizeof(vs), "current now: %d\n", v);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {
v = getIntField(mHealthdConfig->batteryCurrentAvgPath);
snprintf(vs, sizeof(vs), "current avg: %d\n", v);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ if (!mHealthdConfig->batteryChargeCounterPath.empty()) {
v = getIntField(mHealthdConfig->batteryChargeCounterPath);
snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrentMicroamps);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCycleCountPath.empty()) {
snprintf(vs, sizeof(vs), "cycle count: %d\n", props.batteryCycleCount);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+ if (!mHealthdConfig->batteryFullChargePath.empty()) {
snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullChargeUah);
write(fd, vs, strlen(vs));
}
@@ -775,8 +771,7 @@
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)
- mChargerNames.add(String8(name));
+ if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));
break;
case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
@@ -787,166 +782,168 @@
if (isScopedPowerSupply(name)) continue;
mBatteryDevicePresent = true;
- if (mHealthdConfig->batteryStatusPath.isEmpty()) {
+ if (mHealthdConfig->batteryStatusPath.empty()) {
path.clear();
path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0)
- mHealthdConfig->batteryStatusPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryStatusPath = path;
}
- if (mHealthdConfig->batteryHealthPath.isEmpty()) {
+ if (mHealthdConfig->batteryHealthPath.empty()) {
path.clear();
path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0)
- mHealthdConfig->batteryHealthPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryHealthPath = path;
}
- if (mHealthdConfig->batteryPresentPath.isEmpty()) {
+ if (mHealthdConfig->batteryPresentPath.empty()) {
path.clear();
path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0)
- mHealthdConfig->batteryPresentPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryPresentPath = path;
}
- if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
+ if (mHealthdConfig->batteryCapacityPath.empty()) {
path.clear();
path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0)
- mHealthdConfig->batteryCapacityPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryCapacityPath = path;
}
- if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
+ if (mHealthdConfig->batteryVoltagePath.empty()) {
path.clear();
path.appendFormat("%s/%s/voltage_now",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) {
+ if (access(path.c_str(), R_OK) == 0) {
mHealthdConfig->batteryVoltagePath = path;
}
}
- if (mHealthdConfig->batteryFullChargePath.isEmpty()) {
+ if (mHealthdConfig->batteryFullChargePath.empty()) {
path.clear();
path.appendFormat("%s/%s/charge_full",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryFullChargePath = path;
}
- if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ if (mHealthdConfig->batteryCurrentNowPath.empty()) {
path.clear();
path.appendFormat("%s/%s/current_now",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryCurrentNowPath = path;
}
- if (mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+ if (mHealthdConfig->batteryCycleCountPath.empty()) {
path.clear();
path.appendFormat("%s/%s/cycle_count",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryCycleCountPath = path;
}
- if (mHealthdConfig->batteryCapacityLevelPath.isEmpty()) {
+ if (mHealthdConfig->batteryCapacityLevelPath.empty()) {
path.clear();
path.appendFormat("%s/%s/capacity_level", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) mHealthdConfig->batteryCapacityLevelPath = path;
+ if (access(path.c_str(), R_OK) == 0) {
+ mHealthdConfig->batteryCapacityLevelPath = path;
+ }
}
- if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty()) {
+ if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty()) {
path.clear();
path.appendFormat("%s/%s/time_to_full_now", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryChargeTimeToFullNowPath = path;
}
- if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty()) {
+ if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty()) {
path.clear();
path.appendFormat("%s/%s/charge_full_design", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryFullChargeDesignCapacityUahPath = path;
}
- if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ if (mHealthdConfig->batteryCurrentAvgPath.empty()) {
path.clear();
path.appendFormat("%s/%s/current_avg",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryCurrentAvgPath = path;
}
- if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ if (mHealthdConfig->batteryChargeCounterPath.empty()) {
path.clear();
path.appendFormat("%s/%s/charge_counter",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryChargeCounterPath = path;
}
- if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
+ if (mHealthdConfig->batteryTemperaturePath.empty()) {
path.clear();
path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0) {
+ if (access(path.c_str(), R_OK) == 0) {
mHealthdConfig->batteryTemperaturePath = path;
}
}
- if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
+ if (mHealthdConfig->batteryTechnologyPath.empty()) {
path.clear();
path.appendFormat("%s/%s/technology",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryTechnologyPath = path;
}
- if (mHealthdConfig->batteryStateOfHealthPath.isEmpty()) {
+ if (mHealthdConfig->batteryStateOfHealthPath.empty()) {
path.clear();
path.appendFormat("%s/%s/state_of_health", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) {
+ if (access(path.c_str(), R_OK) == 0) {
mHealthdConfig->batteryStateOfHealthPath = path;
} else {
path.clear();
path.appendFormat("%s/%s/health_index", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryStateOfHealthPath = path;
}
}
- if (mHealthdConfig->batteryHealthStatusPath.isEmpty()) {
+ if (mHealthdConfig->batteryHealthStatusPath.empty()) {
path.clear();
path.appendFormat("%s/%s/health_status", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) mHealthdConfig->batteryHealthStatusPath = path;
+ if (access(path.c_str(), R_OK) == 0) {
+ mHealthdConfig->batteryHealthStatusPath = path;
+ }
}
- if (mHealthdConfig->batteryManufacturingDatePath.isEmpty()) {
+ if (mHealthdConfig->batteryManufacturingDatePath.empty()) {
path.clear();
path.appendFormat("%s/%s/manufacturing_date", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryManufacturingDatePath = path;
}
- if (mHealthdConfig->batteryFirstUsageDatePath.isEmpty()) {
+ if (mHealthdConfig->batteryFirstUsageDatePath.empty()) {
path.clear();
path.appendFormat("%s/%s/first_usage_date", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) mHealthdConfig->batteryFirstUsageDatePath = path;
+ if (access(path.c_str(), R_OK) == 0) {
+ mHealthdConfig->batteryFirstUsageDatePath = path;
+ }
}
- if (mHealthdConfig->chargingStatePath.isEmpty()) {
+ if (mHealthdConfig->chargingStatePath.empty()) {
path.clear();
path.appendFormat("%s/%s/charging_state", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) mHealthdConfig->chargingStatePath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->chargingStatePath = path;
}
- if (mHealthdConfig->chargingPolicyPath.isEmpty()) {
+ if (mHealthdConfig->chargingPolicyPath.empty()) {
path.clear();
path.appendFormat("%s/%s/charging_policy", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) mHealthdConfig->chargingPolicyPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->chargingPolicyPath = path;
}
break;
@@ -958,12 +955,10 @@
// 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) {
+ if (access(path.c_str(), 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));
-
+ if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));
}
}
}
@@ -975,43 +970,43 @@
hc->periodic_chores_interval_fast = -1;
hc->periodic_chores_interval_slow = -1;
} else {
- if (mHealthdConfig->batteryStatusPath.isEmpty())
+ if (mHealthdConfig->batteryStatusPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
- if (mHealthdConfig->batteryHealthPath.isEmpty())
+ if (mHealthdConfig->batteryHealthPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
- if (mHealthdConfig->batteryPresentPath.isEmpty())
+ if (mHealthdConfig->batteryPresentPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
- if (mHealthdConfig->batteryCapacityPath.isEmpty())
+ if (mHealthdConfig->batteryCapacityPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
- if (mHealthdConfig->batteryVoltagePath.isEmpty())
+ if (mHealthdConfig->batteryVoltagePath.empty())
KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
- if (mHealthdConfig->batteryTemperaturePath.isEmpty())
+ if (mHealthdConfig->batteryTemperaturePath.empty())
KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
- if (mHealthdConfig->batteryTechnologyPath.isEmpty())
+ if (mHealthdConfig->batteryTechnologyPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
- if (mHealthdConfig->batteryCurrentNowPath.isEmpty())
+ if (mHealthdConfig->batteryCurrentNowPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");
- if (mHealthdConfig->batteryFullChargePath.isEmpty())
+ if (mHealthdConfig->batteryFullChargePath.empty())
KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
- if (mHealthdConfig->batteryCycleCountPath.isEmpty())
+ if (mHealthdConfig->batteryCycleCountPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
- if (mHealthdConfig->batteryCapacityLevelPath.isEmpty())
+ if (mHealthdConfig->batteryCapacityLevelPath.empty())
KLOG_WARNING(LOG_TAG, "batteryCapacityLevelPath not found\n");
- if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+ if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty())
KLOG_WARNING(LOG_TAG, "batteryChargeTimeToFullNowPath. not found\n");
- if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+ if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())
KLOG_WARNING(LOG_TAG, "batteryFullChargeDesignCapacityUahPath. not found\n");
- if (mHealthdConfig->batteryStateOfHealthPath.isEmpty())
+ if (mHealthdConfig->batteryStateOfHealthPath.empty())
KLOG_WARNING(LOG_TAG, "batteryStateOfHealthPath not found\n");
- if (mHealthdConfig->batteryHealthStatusPath.isEmpty())
+ if (mHealthdConfig->batteryHealthStatusPath.empty())
KLOG_WARNING(LOG_TAG, "batteryHealthStatusPath not found\n");
- if (mHealthdConfig->batteryManufacturingDatePath.isEmpty())
+ if (mHealthdConfig->batteryManufacturingDatePath.empty())
KLOG_WARNING(LOG_TAG, "batteryManufacturingDatePath not found\n");
- if (mHealthdConfig->batteryFirstUsageDatePath.isEmpty())
+ if (mHealthdConfig->batteryFirstUsageDatePath.empty())
KLOG_WARNING(LOG_TAG, "batteryFirstUsageDatePath not found\n");
- if (mHealthdConfig->chargingStatePath.isEmpty())
+ if (mHealthdConfig->chargingStatePath.empty())
KLOG_WARNING(LOG_TAG, "chargingStatePath not found\n");
- if (mHealthdConfig->chargingPolicyPath.isEmpty())
+ if (mHealthdConfig->chargingPolicyPath.empty())
KLOG_WARNING(LOG_TAG, "chargingPolicyPath not found\n");
}
diff --git a/healthd/BatteryMonitor_v1.cpp b/healthd/BatteryMonitor_v1.cpp
index b87c493..2e0cfc9 100644
--- a/healthd/BatteryMonitor_v1.cpp
+++ b/healthd/BatteryMonitor_v1.cpp
@@ -301,7 +301,7 @@
void BatteryMonitor::updateValues(void) {
initHealthInfo(mHealthInfo.get());
- if (!mHealthdConfig->batteryPresentPath.isEmpty())
+ if (!mHealthdConfig->batteryPresentPath.empty())
mHealthInfo->batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
else
mHealthInfo->batteryPresent = mBatteryDevicePresent;
@@ -311,28 +311,28 @@
: getIntField(mHealthdConfig->batteryCapacityPath);
mHealthInfo->batteryVoltageMillivolts = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
- if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
+ if (!mHealthdConfig->batteryCurrentNowPath.empty())
mHealthInfo->batteryCurrentMicroamps = getIntField(mHealthdConfig->batteryCurrentNowPath);
- if (!mHealthdConfig->batteryFullChargePath.isEmpty())
+ if (!mHealthdConfig->batteryFullChargePath.empty())
mHealthInfo->batteryFullChargeUah = getIntField(mHealthdConfig->batteryFullChargePath);
- if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
+ if (!mHealthdConfig->batteryCycleCountPath.empty())
mHealthInfo->batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
- if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
+ if (!mHealthdConfig->batteryChargeCounterPath.empty())
mHealthInfo->batteryChargeCounterUah =
getIntField(mHealthdConfig->batteryChargeCounterPath);
- if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty())
+ if (!mHealthdConfig->batteryCurrentAvgPath.empty())
mHealthInfo->batteryCurrentAverageMicroamps =
getIntField(mHealthdConfig->batteryCurrentAvgPath);
- if (!mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+ if (!mHealthdConfig->batteryChargeTimeToFullNowPath.empty())
mHealthInfo->batteryChargeTimeToFullNowSeconds =
getIntField(mHealthdConfig->batteryChargeTimeToFullNowPath);
- if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+ if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())
mHealthInfo->batteryFullChargeDesignCapacityUah =
getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
@@ -352,18 +352,16 @@
mHealthInfo->batteryHealth = getBatteryHealth(buf.c_str());
if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
- mHealthInfo->batteryTechnology = String8(buf.c_str());
+ mHealthInfo->batteryTechnology = buf;
double MaxPower = 0;
for (size_t i = 0; i < mChargerNames.size(); i++) {
String8 path;
- path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
+ path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());
if (getIntField(path)) {
path.clear();
- path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
+ path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());
switch(readPowerSupplyType(path)) {
case ANDROID_POWER_SUPPLY_TYPE_AC:
mHealthInfo->chargerAcOnline = true;
@@ -380,26 +378,24 @@
default:
path.clear();
path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
- if (access(path.string(), R_OK) == 0)
+ mChargerNames[i].c_str());
+ if (access(path.c_str(), R_OK) == 0)
mHealthInfo->chargerDockOnline = true;
else
KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
- mChargerNames[i].string());
+ mChargerNames[i].c_str());
}
path.clear();
path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
- int ChargingCurrent =
- (access(path.string(), R_OK) == 0) ? getIntField(path) : 0;
+ mChargerNames[i].c_str());
+ int ChargingCurrent = (access(path.c_str(), R_OK) == 0) ? getIntField(path) : 0;
path.clear();
path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
- mChargerNames[i].string());
+ mChargerNames[i].c_str());
int ChargingVoltage =
- (access(path.string(), R_OK) == 0) ? getIntField(path) :
- DEFAULT_VBUS_VOLTAGE;
+ (access(path.c_str(), R_OK) == 0) ? getIntField(path) : DEFAULT_VBUS_VOLTAGE;
double power = ((double)ChargingCurrent / MILLION) *
((double)ChargingVoltage / MILLION);
@@ -424,17 +420,17 @@
props.batteryStatus);
len = strlen(dmesgline);
- if (!healthd_config.batteryCurrentNowPath.isEmpty()) {
+ if (!healthd_config.batteryCurrentNowPath.empty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
props.batteryCurrentMicroamps);
}
- if (!healthd_config.batteryFullChargePath.isEmpty()) {
+ if (!healthd_config.batteryFullChargePath.empty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
props.batteryFullChargeUah);
}
- if (!healthd_config.batteryCycleCountPath.isEmpty()) {
+ if (!healthd_config.batteryCycleCountPath.empty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
props.batteryCycleCount);
}
@@ -468,7 +464,7 @@
int BatteryMonitor::getChargeStatus() {
BatteryStatus result = BatteryStatus::UNKNOWN;
- if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
+ if (!mHealthdConfig->batteryStatusPath.empty()) {
std::string buf;
if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
result = getBatteryStatus(buf.c_str());
@@ -484,7 +480,7 @@
switch(id) {
case BATTERY_PROP_CHARGE_COUNTER:
- if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ if (!mHealthdConfig->batteryChargeCounterPath.empty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryChargeCounterPath);
ret = OK;
@@ -494,7 +490,7 @@
break;
case BATTERY_PROP_CURRENT_NOW:
- if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCurrentNowPath);
ret = OK;
@@ -504,7 +500,7 @@
break;
case BATTERY_PROP_CURRENT_AVG:
- if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCurrentAvgPath);
ret = OK;
@@ -514,7 +510,7 @@
break;
case BATTERY_PROP_CAPACITY:
- if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCapacityPath.empty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCapacityPath);
ret = OK;
@@ -561,35 +557,35 @@
props.batteryVoltageMillivolts, props.batteryTemperatureTenthsCelsius);
write(fd, vs, strlen(vs));
- if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
v = getIntField(mHealthdConfig->batteryCurrentNowPath);
snprintf(vs, sizeof(vs), "current now: %d\n", v);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {
v = getIntField(mHealthdConfig->batteryCurrentAvgPath);
snprintf(vs, sizeof(vs), "current avg: %d\n", v);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ if (!mHealthdConfig->batteryChargeCounterPath.empty()) {
v = getIntField(mHealthdConfig->batteryChargeCounterPath);
snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrentMicroamps);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCycleCountPath.empty()) {
snprintf(vs, sizeof(vs), "cycle count: %d\n", props.batteryCycleCount);
write(fd, vs, strlen(vs));
}
- if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+ if (!mHealthdConfig->batteryFullChargePath.empty()) {
snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullChargeUah);
write(fd, vs, strlen(vs));
}
@@ -628,8 +624,7 @@
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)
- mChargerNames.add(String8(name));
+ if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));
break;
case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
@@ -640,121 +635,119 @@
if (isScopedPowerSupply(name)) continue;
mBatteryDevicePresent = true;
- if (mHealthdConfig->batteryStatusPath.isEmpty()) {
+ if (mHealthdConfig->batteryStatusPath.empty()) {
path.clear();
path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0)
- mHealthdConfig->batteryStatusPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryStatusPath = path;
}
- if (mHealthdConfig->batteryHealthPath.isEmpty()) {
+ if (mHealthdConfig->batteryHealthPath.empty()) {
path.clear();
path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0)
- mHealthdConfig->batteryHealthPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryHealthPath = path;
}
- if (mHealthdConfig->batteryPresentPath.isEmpty()) {
+ if (mHealthdConfig->batteryPresentPath.empty()) {
path.clear();
path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0)
- mHealthdConfig->batteryPresentPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryPresentPath = path;
}
- if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
+ if (mHealthdConfig->batteryCapacityPath.empty()) {
path.clear();
path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0)
- mHealthdConfig->batteryCapacityPath = path;
+ if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryCapacityPath = path;
}
- if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
+ if (mHealthdConfig->batteryVoltagePath.empty()) {
path.clear();
path.appendFormat("%s/%s/voltage_now",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) {
+ if (access(path.c_str(), R_OK) == 0) {
mHealthdConfig->batteryVoltagePath = path;
}
}
- if (mHealthdConfig->batteryFullChargePath.isEmpty()) {
+ if (mHealthdConfig->batteryFullChargePath.empty()) {
path.clear();
path.appendFormat("%s/%s/charge_full",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryFullChargePath = path;
}
- if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ if (mHealthdConfig->batteryCurrentNowPath.empty()) {
path.clear();
path.appendFormat("%s/%s/current_now",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryCurrentNowPath = path;
}
- if (mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+ if (mHealthdConfig->batteryCycleCountPath.empty()) {
path.clear();
path.appendFormat("%s/%s/cycle_count",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryCycleCountPath = path;
}
- if (mHealthdConfig->batteryCapacityLevelPath.isEmpty()) {
+ if (mHealthdConfig->batteryCapacityLevelPath.empty()) {
path.clear();
path.appendFormat("%s/%s/capacity_level", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0) mHealthdConfig->batteryCapacityLevelPath = path;
+ if (access(path.c_str(), R_OK) == 0) {
+ mHealthdConfig->batteryCapacityLevelPath = path;
+ }
}
- if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty()) {
+ if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty()) {
path.clear();
path.appendFormat("%s/%s/time_to_full_now", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryChargeTimeToFullNowPath = path;
}
- if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty()) {
+ if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty()) {
path.clear();
path.appendFormat("%s/%s/charge_full_design", POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryFullChargeDesignCapacityUahPath = path;
}
- if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ if (mHealthdConfig->batteryCurrentAvgPath.empty()) {
path.clear();
path.appendFormat("%s/%s/current_avg",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryCurrentAvgPath = path;
}
- if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ if (mHealthdConfig->batteryChargeCounterPath.empty()) {
path.clear();
path.appendFormat("%s/%s/charge_counter",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryChargeCounterPath = path;
}
- if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
+ if (mHealthdConfig->batteryTemperaturePath.empty()) {
path.clear();
path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
name);
- if (access(path, R_OK) == 0) {
+ if (access(path.c_str(), R_OK) == 0) {
mHealthdConfig->batteryTemperaturePath = path;
}
}
- if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
+ if (mHealthdConfig->batteryTechnologyPath.empty()) {
path.clear();
path.appendFormat("%s/%s/technology",
POWER_SUPPLY_SYSFS_PATH, name);
- if (access(path, R_OK) == 0)
+ if (access(path.c_str(), R_OK) == 0)
mHealthdConfig->batteryTechnologyPath = path;
}
@@ -767,12 +760,10 @@
// 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) {
+ if (access(path.c_str(), 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));
-
+ if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));
}
}
}
@@ -784,31 +775,31 @@
hc->periodic_chores_interval_fast = -1;
hc->periodic_chores_interval_slow = -1;
} else {
- if (mHealthdConfig->batteryStatusPath.isEmpty())
+ if (mHealthdConfig->batteryStatusPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
- if (mHealthdConfig->batteryHealthPath.isEmpty())
+ if (mHealthdConfig->batteryHealthPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
- if (mHealthdConfig->batteryPresentPath.isEmpty())
+ if (mHealthdConfig->batteryPresentPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
- if (mHealthdConfig->batteryCapacityPath.isEmpty())
+ if (mHealthdConfig->batteryCapacityPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
- if (mHealthdConfig->batteryVoltagePath.isEmpty())
+ if (mHealthdConfig->batteryVoltagePath.empty())
KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
- if (mHealthdConfig->batteryTemperaturePath.isEmpty())
+ if (mHealthdConfig->batteryTemperaturePath.empty())
KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
- if (mHealthdConfig->batteryTechnologyPath.isEmpty())
+ if (mHealthdConfig->batteryTechnologyPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
- if (mHealthdConfig->batteryCurrentNowPath.isEmpty())
+ if (mHealthdConfig->batteryCurrentNowPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");
- if (mHealthdConfig->batteryFullChargePath.isEmpty())
+ if (mHealthdConfig->batteryFullChargePath.empty())
KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
- if (mHealthdConfig->batteryCycleCountPath.isEmpty())
+ if (mHealthdConfig->batteryCycleCountPath.empty())
KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
- if (mHealthdConfig->batteryCapacityLevelPath.isEmpty())
+ if (mHealthdConfig->batteryCapacityLevelPath.empty())
KLOG_WARNING(LOG_TAG, "batteryCapacityLevelPath not found\n");
- if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+ if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty())
KLOG_WARNING(LOG_TAG, "batteryChargeTimeToFullNowPath. not found\n");
- if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+ if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())
KLOG_WARNING(LOG_TAG, "batteryFullChargeDesignCapacityUahPath. not found\n");
}
diff --git a/init/Android.bp b/init/Android.bp
index 1af398a..e5512e6 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -166,17 +166,14 @@
"libbootloader_message",
"libc++fs",
"libcgrouprc_format",
- "libfsverity_init",
"liblmkd_utils",
"liblz4",
"libzstd",
- "libmini_keyctl_static",
"libmodprobe",
"libprocinfo",
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
- "libsigningutils",
"libsnapshot_cow",
"libsnapshot_init",
"libxml2",
@@ -185,7 +182,6 @@
],
shared_libs: [
"libbase",
- "libcrypto",
"libcutils",
"libdl",
"libext4_utils",
@@ -201,7 +197,6 @@
"libselinux",
"libunwindstack",
"libutils",
- "libziparchive",
],
header_libs: ["bionic_libc_platform_headers"],
bootstrap: true,
@@ -537,6 +532,7 @@
"libprotobuf-cpp-lite",
],
static_libs: [
+ "libfs_mgr",
"libhidl-gen-utils",
],
}
diff --git a/init/README.md b/init/README.md
index 5fced19..11c4e1c 100644
--- a/init/README.md
+++ b/init/README.md
@@ -674,11 +674,12 @@
_options_ include "barrier=1", "noauto\_da\_alloc", "discard", ... as
a comma separated string, e.g. barrier=1,noauto\_da\_alloc
-`perform_apex_config`
+`perform_apex_config [--bootstrap]`
> Performs tasks after APEXes are mounted. For example, creates data directories
for the mounted APEXes, parses config file(s) from them, and updates linker
configurations. Intended to be used only once when apexd notifies the mount
event by setting `apexd.status` to ready.
+ Use --bootstrap when invoking in the bootstrap mount namespace.
`restart [--only-if-running] <service>`
> Stops and restarts a running service, does nothing if the service is currently
diff --git a/init/TEST_MAPPING b/init/TEST_MAPPING
index 402b501..36ca379 100644
--- a/init/TEST_MAPPING
+++ b/init/TEST_MAPPING
@@ -8,14 +8,6 @@
},
{
"name": "MicrodroidHostTestCases"
- },
- {
- "name": "CtsSecurityHostTestCases",
- "options": [
- {
- "include-filter": "android.security.cts.SeamendcHostTest"
- }
- ]
}
],
"hwasan-presubmit": [
@@ -27,14 +19,6 @@
},
{
"name": "MicrodroidHostTestCases"
- },
- {
- "name": "CtsSecurityHostTestCases",
- "options": [
- {
- "include-filter": "android.security.cts.SeamendcHostTest"
- }
- ]
}
]
}
diff --git a/init/apex_init_util.cpp b/init/apex_init_util.cpp
index c818f8f..6d17f36 100644
--- a/init/apex_init_util.cpp
+++ b/init/apex_init_util.cpp
@@ -16,13 +16,15 @@
#include "apex_init_util.h"
+#include <dirent.h>
#include <glob.h>
+#include <set>
#include <vector>
#include <android-base/logging.h>
-#include <android-base/result.h>
#include <android-base/properties.h>
+#include <android-base/result.h>
#include <android-base/strings.h>
#include "action_manager.h"
@@ -34,10 +36,13 @@
namespace android {
namespace init {
-static Result<std::vector<std::string>> CollectApexConfigs(const std::string& apex_name) {
+static Result<std::vector<std::string>> CollectRcScriptsFromApex(
+ const std::string& apex_name, const std::set<std::string>& skip_apexes) {
glob_t glob_result;
- std::string glob_pattern = apex_name.empty() ?
- "/apex/*/etc/*rc" : "/apex/" + apex_name + "/etc/*rc";
+ // Pattern uses "*rc" instead of ".rc" because APEXes can have versioned RC files
+ // like foo.34rc.
+ std::string glob_pattern =
+ apex_name.empty() ? "/apex/*/etc/*rc" : "/apex/" + apex_name + "/etc/*rc";
const int ret = glob(glob_pattern.c_str(), GLOB_MARK, nullptr, &glob_result);
if (ret != 0 && ret != GLOB_NOMATCH) {
@@ -47,15 +52,28 @@
std::vector<std::string> configs;
for (size_t i = 0; i < glob_result.gl_pathc; i++) {
std::string path = glob_result.gl_pathv[i];
- // Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to
- // /apex/<name> paths, so unless we filter them out, we will parse the
- // same file twice.
- std::vector<std::string> paths = android::base::Split(path, "/");
- if (paths.size() >= 3 && paths[2].find('@') != std::string::npos) {
+
+ // Filter out directories
+ if (path.back() == '/') {
continue;
}
- // Filter directories
- if (path.back() == '/') {
+
+ // Get apex name from path.
+ std::vector<std::string> paths = android::base::Split(path, "/");
+ if (paths.size() < 3) {
+ continue;
+ }
+ const std::string& apex_name = paths[2];
+
+ // Filter out /apex/<name>@<ver> paths. The paths are bind-mounted to
+ // /apex/<name> paths, so unless we filter them out, we will parse the
+ // same file twice.
+ if (apex_name.find('@') != std::string::npos) {
+ continue;
+ }
+
+ // Filter out skip_set apexes
+ if (skip_apexes.count(apex_name) > 0) {
continue;
}
configs.push_back(path);
@@ -64,11 +82,41 @@
return configs;
}
-static Result<void> ParseConfigs(const std::vector<std::string>& configs) {
+std::set<std::string> GetApexListFrom(const std::string& apex_dir) {
+ std::set<std::string> apex_list;
+ auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(apex_dir.c_str()), closedir);
+ if (!dirp) {
+ return apex_list;
+ }
+ struct dirent* entry;
+ while ((entry = readdir(dirp.get())) != nullptr) {
+ if (entry->d_type != DT_DIR) continue;
+
+ const char* name = entry->d_name;
+ if (name[0] == '.') continue;
+ if (strchr(name, '@') != nullptr) continue;
+ if (strcmp(name, "sharedlibs") == 0) continue;
+ apex_list.insert(name);
+ }
+ return apex_list;
+}
+
+static Result<void> ParseRcScripts(const std::vector<std::string>& files) {
+ if (files.empty()) {
+ return {};
+ }
+ // APEXes can have versioned RC files. These should be filtered based on
+ // SDK version.
+ auto filtered = FilterVersionedConfigs(
+ files, android::base::GetIntProperty("ro.build.version.sdk", INT_MAX));
+ if (filtered.empty()) {
+ return {};
+ }
+
Parser parser =
CreateApexConfigParser(ActionManager::GetInstance(), ServiceList::GetInstance());
std::vector<std::string> errors;
- for (const auto& c : configs) {
+ for (const auto& c : filtered) {
auto result = parser.ParseConfigFile(c);
// We should handle other config files even when there's an error.
if (!result.ok()) {
@@ -81,16 +129,21 @@
return {};
}
-Result<void> ParseApexConfigs(const std::string& apex_name) {
- auto configs = OR_RETURN(CollectApexConfigs(apex_name));
+Result<void> ParseRcScriptsFromApex(const std::string& apex_name) {
+ auto configs = OR_RETURN(CollectRcScriptsFromApex(apex_name, /*skip_apexes=*/{}));
+ return ParseRcScripts(configs);
+}
- if (configs.empty()) {
- return {};
+Result<void> ParseRcScriptsFromAllApexes(bool bootstrap) {
+ std::set<std::string> skip_apexes;
+ if (!bootstrap) {
+ // In case we already loaded config files from bootstrap APEXes, we need to avoid loading
+ // them again. We can get the list of bootstrap APEXes by scanning /bootstrap-apex and
+ // skip them in CollectRcScriptsFromApex.
+ skip_apexes = GetApexListFrom("/bootstrap-apex");
}
-
- auto filtered_configs = FilterVersionedConfigs(configs,
- android::base::GetIntProperty("ro.build.version.sdk", INT_MAX));
- return ParseConfigs(filtered_configs);
+ auto configs = OR_RETURN(CollectRcScriptsFromApex(/*apex_name=*/"", skip_apexes));
+ return ParseRcScripts(configs);
}
} // namespace init
diff --git a/init/apex_init_util.h b/init/apex_init_util.h
index 43f8ad5..75dfee1 100644
--- a/init/apex_init_util.h
+++ b/init/apex_init_util.h
@@ -16,6 +16,7 @@
#pragma once
+#include <set>
#include <string>
#include <vector>
@@ -24,9 +25,14 @@
namespace android {
namespace init {
-// Parse all config files for a given apex.
-// If apex name is empty(""), config files for all apexes will be parsed.
-Result<void> ParseApexConfigs(const std::string& apex_name);
+// Scans apex_dir (/apex) to get the list of active APEXes.
+std::set<std::string> GetApexListFrom(const std::string& apex_dir);
+
+// Parse all RC scripts for a given apex.
+Result<void> ParseRcScriptsFromApex(const std::string& apex_name);
+
+// Parse all RC scripts for all apexes under /apex.
+Result<void> ParseRcScriptsFromAllApexes(bool bootstrap);
} // namespace init
} // namespace android
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 2176233..a95a4a3 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -475,8 +475,6 @@
{ 0, 0 },
};
-#define DATA_MNT_POINT "/data"
-
/* mount <type> <device> <path> <flags ...> <options> */
static Result<void> do_mount(const BuiltinArguments& args) {
const char* options = nullptr;
@@ -746,6 +744,7 @@
static Result<void> do_start(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "service " << args[1] << " not found";
+ errno = 0;
if (auto result = svc->Start(); !result.ok()) {
return ErrorIgnoreEnoent() << "Could not start service: " << result.error();
}
@@ -1269,37 +1268,33 @@
/*
* Creates a directory under /data/misc/apexdata/ for each APEX.
*/
-static Result<void> create_apex_data_dirs() {
- auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir("/apex"), closedir);
- if (!dirp) {
- return ErrnoError() << "Unable to open apex directory";
- }
- struct dirent* entry;
- while ((entry = readdir(dirp.get())) != nullptr) {
- if (entry->d_type != DT_DIR) continue;
-
- const char* name = entry->d_name;
- // skip any starting with "."
- if (name[0] == '.') continue;
-
- if (strchr(name, '@') != nullptr) continue;
-
- auto path = "/data/misc/apexdata/" + std::string(name);
+static void create_apex_data_dirs() {
+ for (const auto& name : GetApexListFrom("/apex")) {
+ auto path = "/data/misc/apexdata/" + name;
auto options = MkdirOptions{path, 0771, AID_ROOT, AID_SYSTEM, FscryptAction::kNone, "ref"};
- make_dir_with_options(options);
+ auto result = make_dir_with_options(options);
+ if (!result.ok()) {
+ LOG(ERROR) << result.error();
+ }
}
- return {};
}
static Result<void> do_perform_apex_config(const BuiltinArguments& args) {
- auto create_dirs = create_apex_data_dirs();
- if (!create_dirs.ok()) {
- return create_dirs.error();
+ bool bootstrap = false;
+ if (args.size() == 2) {
+ if (args[1] != "--bootstrap") {
+ return Error() << "Unexpected argument: " << args[1];
+ }
+ bootstrap = true;
}
- auto parse_configs = ParseApexConfigs(/*apex_name=*/"");
- ServiceList::GetInstance().MarkServicesUpdate();
- if (!parse_configs.ok()) {
- return parse_configs.error();
+
+ if (!bootstrap) {
+ create_apex_data_dirs();
+ }
+
+ auto parse_result = ParseRcScriptsFromAllApexes(bootstrap);
+ if (!parse_result.ok()) {
+ return parse_result.error();
}
auto update_linker_config = do_update_linker_config(args);
@@ -1307,6 +1302,9 @@
return update_linker_config.error();
}
+ if (!bootstrap) {
+ ServiceList::GetInstance().StartDelayedServices();
+ }
return {};
}
@@ -1361,7 +1359,7 @@
// mount and umount are run in the same context as mount_all for symmetry.
{"mount_all", {0, kMax, {false, do_mount_all}}},
{"mount", {3, kMax, {false, do_mount}}},
- {"perform_apex_config", {0, 0, {false, do_perform_apex_config}}},
+ {"perform_apex_config", {0, 1, {false, do_perform_apex_config}}},
{"umount", {1, 1, {false, do_umount}}},
{"umount_all", {0, 1, {false, do_umount_all}}},
{"update_linker_config", {0, 0, {false, do_update_linker_config}}},
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index ab6ff03..0e2cd2a 100644
--- a/init/capabilities.cpp
+++ b/init/capabilities.cpp
@@ -66,18 +66,12 @@
CAP_MAP_ENTRY(WAKE_ALARM),
CAP_MAP_ENTRY(BLOCK_SUSPEND),
CAP_MAP_ENTRY(AUDIT_READ),
-#if defined(__BIONIC__)
CAP_MAP_ENTRY(PERFMON),
CAP_MAP_ENTRY(BPF),
CAP_MAP_ENTRY(CHECKPOINT_RESTORE),
-#endif
};
-#if defined(__BIONIC__)
static_assert(CAP_LAST_CAP == CAP_CHECKPOINT_RESTORE, "CAP_LAST_CAP is not CAP_CHECKPOINT_RESTORE");
-#else
-static_assert(CAP_LAST_CAP == CAP_AUDIT_READ, "CAP_LAST_CAP is not CAP_AUDIT_READ");
-#endif
static bool ComputeCapAmbientSupported() {
#if defined(__ANDROID__)
diff --git a/init/capabilities.h b/init/capabilities.h
index 891e0ac..fc80c98 100644
--- a/init/capabilities.h
+++ b/init/capabilities.h
@@ -21,17 +21,6 @@
#include <string>
#include <type_traits>
-#if !defined(__ANDROID__)
-#ifndef CAP_BLOCK_SUSPEND
-#define CAP_BLOCK_SUSPEND 36
-#endif
-#ifndef CAP_AUDIT_READ
-#define CAP_AUDIT_READ 37
-#endif
-#undef CAP_LAST_CAP
-#define CAP_LAST_CAP CAP_AUDIT_READ
-#endif
-
namespace android {
namespace init {
diff --git a/init/devices.cpp b/init/devices.cpp
index d29ffd6..e76786a 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -32,6 +32,7 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <fs_mgr.h>
#include <libdm/dm.h>
#include <private/android_filesystem_config.h>
#include <selinux/android.h>
@@ -203,8 +204,8 @@
partition_map.emplace_back(map_pieces[0], map_pieces[1]);
}
};
- ImportKernelCmdline(parser);
- ImportBootconfig(parser);
+ android::fs_mgr::ImportKernelCmdline(parser);
+ android::fs_mgr::ImportBootconfig(parser);
return partition_map;
}();
@@ -568,6 +569,8 @@
return;
} else if (uevent.subsystem == "misc" && StartsWith(uevent.device_name, "dm-user/")) {
devpath = "/dev/dm-user/" + uevent.device_name.substr(8);
+ } else if (uevent.subsystem == "misc" && uevent.device_name == "vfio/vfio") {
+ devpath = "/dev/" + uevent.device_name;
} else {
devpath = "/dev/" + Basename(uevent.path);
}
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 7fabbac..e48fa15 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -35,6 +35,7 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include <modprobe/modprobe.h>
#include <private/android_filesystem_config.h>
@@ -67,7 +68,7 @@
void FreeRamdisk(DIR* dir, dev_t dev) {
int dfd = dirfd(dir);
- dirent* de;
+ dirent* de = nullptr;
while ((de = readdir(dir)) != nullptr) {
if (de->d_name == "."s || de->d_name == ".."s) {
continue;
@@ -76,7 +77,7 @@
bool is_dir = false;
if (de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) {
- struct stat info;
+ struct stat info {};
if (fstatat(dfd, de->d_name, &info, AT_SYMLINK_NOFOLLOW) != 0) {
continue;
}
@@ -153,6 +154,30 @@
Copy(snapuserd, dst);
}
}
+
+std::string GetPageSizeSuffix() {
+ static const size_t page_size = sysconf(_SC_PAGE_SIZE);
+ if (page_size <= 4096) {
+ return "";
+ }
+ return android::base::StringPrintf("_%zuk", page_size / 1024);
+}
+
+constexpr bool EndsWith(const std::string_view str, const std::string_view suffix) {
+ return str.size() >= suffix.size() &&
+ 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix);
+}
+
+constexpr std::string_view GetPageSizeSuffix(std::string_view dirname) {
+ if (EndsWith(dirname, "_16k")) {
+ return "_16k";
+ }
+ if (EndsWith(dirname, "_64k")) {
+ return "_64k";
+ }
+ return "";
+}
+
} // namespace
std::string GetModuleLoadList(BootMode boot_mode, const std::string& dir_path) {
@@ -171,7 +196,7 @@
}
if (module_load_file != "modules.load") {
- struct stat fileStat;
+ struct stat fileStat {};
std::string load_path = dir_path + "/" + module_load_file;
// Fall back to modules.load if the other files aren't accessible
if (stat(load_path.c_str(), &fileStat)) {
@@ -185,11 +210,11 @@
#define MODULE_BASE_DIR "/lib/modules"
bool LoadKernelModules(BootMode boot_mode, bool want_console, bool want_parallel,
int& modules_loaded) {
- struct utsname uts;
+ struct utsname uts {};
if (uname(&uts)) {
LOG(FATAL) << "Failed to get kernel version.";
}
- int major, minor;
+ int major = 0, minor = 0;
if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
LOG(FATAL) << "Failed to parse kernel version " << uts.release;
}
@@ -199,13 +224,30 @@
LOG(INFO) << "Unable to open /lib/modules, skipping module loading.";
return true;
}
- dirent* entry;
+ dirent* entry = nullptr;
std::vector<std::string> module_dirs;
+ const auto page_size_suffix = GetPageSizeSuffix();
+ const std::string release_specific_module_dir = uts.release + page_size_suffix;
while ((entry = readdir(base_dir.get()))) {
if (entry->d_type != DT_DIR) {
continue;
}
- int dir_major, dir_minor;
+ if (entry->d_name == release_specific_module_dir) {
+ LOG(INFO) << "Release specific kernel module dir " << release_specific_module_dir
+ << " found, loading modules from here with no fallbacks.";
+ module_dirs.clear();
+ module_dirs.emplace_back(entry->d_name);
+ break;
+ }
+ // Is a directory does not have page size suffix, it does not mean this directory is for 4K
+ // kernels. Certain 16K kernel builds put all modules in /lib/modules/`uname -r` without any
+ // suffix. Therefore, only ignore a directory if it has _16k/_64k suffix and the suffix does
+ // not match system page size.
+ const auto dir_page_size_suffix = GetPageSizeSuffix(entry->d_name);
+ if (!dir_page_size_suffix.empty() && dir_page_size_suffix != page_size_suffix) {
+ continue;
+ }
+ int dir_major = 0, dir_minor = 0;
if (sscanf(entry->d_name, "%d.%d", &dir_major, &dir_minor) != 2 || dir_major != major ||
dir_minor != minor) {
continue;
@@ -228,6 +270,7 @@
bool retval = m.LoadListedModules(!want_console);
modules_loaded = m.GetModuleCount();
if (modules_loaded > 0) {
+ LOG(INFO) << "Loaded " << modules_loaded << " modules from " << dir_path;
return retval;
}
}
@@ -237,6 +280,7 @@
: m.LoadListedModules(!want_console);
modules_loaded = m.GetModuleCount();
if (modules_loaded > 0) {
+ LOG(INFO) << "Loaded " << modules_loaded << " modules from " << MODULE_BASE_DIR;
return retval;
}
return true;
@@ -374,7 +418,7 @@
PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
}
- struct stat old_root_info;
+ struct stat old_root_info {};
if (stat("/", &old_root_info) != 0) {
PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
old_root_dir.reset();
@@ -483,7 +527,7 @@
}
}
- struct stat new_root_info;
+ struct stat new_root_info {};
if (stat("/", &new_root_info) != 0) {
PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
old_root_dir.reset();
diff --git a/init/init.cpp b/init/init.cpp
index da63fdc..40e2169 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -487,7 +487,7 @@
}
static Result<void> DoLoadApex(const std::string& apex_name) {
- if (auto result = ParseApexConfigs(apex_name); !result.ok()) {
+ if (auto result = ParseRcScriptsFromApex(apex_name); !result.ok()) {
return result.error();
}
@@ -832,6 +832,12 @@
CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"));
+ if (NeedsTwoMountNamespaces()) {
+ // /bootstrap-apex is used to mount "bootstrap" APEXes.
+ CHECKCALL(mount("tmpfs", "/bootstrap-apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+ "mode=0755,uid=0,gid=0"));
+ }
+
// /linkerconfig is used to keep generated linker configuration
CHECKCALL(mount("tmpfs", "/linkerconfig", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"));
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 5b53d50..7918f23 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -66,15 +66,6 @@
return ret;
}
-// In case we have two sets of APEXes (non-updatable, updatable), we need two separate mount
-// namespaces.
-static bool NeedsTwoMountNamespaces() {
- if (IsRecoveryMode()) return false;
- // In microdroid, there's only one set of APEXes in built-in directories include block devices.
- if (IsMicrodroid()) return false;
- return true;
-}
-
static android::base::unique_fd bootstrap_ns_fd;
static android::base::unique_fd default_ns_fd;
@@ -83,6 +74,15 @@
} // namespace
+// In case we have two sets of APEXes (non-updatable, updatable), we need two separate mount
+// namespaces.
+bool NeedsTwoMountNamespaces() {
+ if (IsRecoveryMode()) return false;
+ // In microdroid, there's only one set of APEXes in built-in directories include block devices.
+ if (IsMicrodroid()) return false;
+ return true;
+}
+
bool SetupMountNamespaces() {
// Set the propagation type of / as shared so that any mounting event (e.g.
// /data) is by default visible to all processes. When private mounting is
@@ -163,6 +163,23 @@
PLOG(ERROR) << "Cannot switch back to bootstrap mount namespace";
return false;
}
+
+ // Some components (e.g. servicemanager) need to access bootstrap
+ // APEXes from the default mount namespace. To achieve that, we bind-mount
+ // /apex to /bootstrap-apex in the bootstrap mount namespace. Since /bootstrap-apex
+ // is "shared", the mounts are visible in the default mount namespace as well.
+ //
+ // The end result will look like:
+ // in the bootstrap mount namespace:
+ // /apex (== /bootstrap-apex)
+ // {bootstrap APEXes from the read-only partition}
+ //
+ // in the default mount namespace:
+ // /bootstrap-apex
+ // {bootstrap APEXes from the read-only partition}
+ // /apex
+ // {APEXes, can be from /data partition}
+ if (!(BindMount("/bootstrap-apex", "/apex"))) return false;
} else {
// Otherwise, default == bootstrap
default_ns_fd.reset(OpenMountNamespace());
diff --git a/init/mount_namespace.h b/init/mount_namespace.h
index 5e3dab2..43c5476 100644
--- a/init/mount_namespace.h
+++ b/init/mount_namespace.h
@@ -24,9 +24,12 @@
enum MountNamespace { NS_BOOTSTRAP, NS_DEFAULT };
bool SetupMountNamespaces();
+
base::Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace);
base::Result<MountNamespace> GetCurrentMountNamespace();
+bool NeedsTwoMountNamespaces();
+
} // namespace init
} // namespace android
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 8da6982..cdd0afe 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -57,6 +57,7 @@
#include <android-base/result.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <fs_mgr.h>
#include <property_info_parser/property_info_parser.h>
#include <property_info_serializer/property_info_serializer.h>
#include <selinux/android.h>
@@ -1246,9 +1247,6 @@
// 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);
@@ -1272,7 +1270,6 @@
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();
@@ -1317,7 +1314,8 @@
return;
}
- std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(android::fs_mgr::GetAndroidDtDir().c_str()),
+ closedir);
if (!dir) return;
std::string dt_file;
@@ -1328,7 +1326,7 @@
continue;
}
- std::string file_name = get_android_dt_dir() + dp->d_name;
+ std::string file_name = android::fs_mgr::GetAndroidDtDir() + dp->d_name;
android::base::ReadFileToString(file_name, &dt_file);
std::replace(dt_file.begin(), dt_file.end(), ',', '.');
@@ -1340,7 +1338,7 @@
constexpr auto ANDROIDBOOT_PREFIX = "androidboot."sv;
static void ProcessKernelCmdline() {
- ImportKernelCmdline([&](const std::string& key, const std::string& value) {
+ android::fs_mgr::ImportKernelCmdline([&](const std::string& key, const std::string& value) {
if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
}
@@ -1349,7 +1347,7 @@
static void ProcessBootconfig() {
- ImportBootconfig([&](const std::string& key, const std::string& value) {
+ android::fs_mgr::ImportBootconfig([&](const std::string& key, const std::string& value) {
if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
}
@@ -1463,8 +1461,6 @@
work_.pop_front();
}
- std::this_thread::sleep_for(1s);
-
// Perform write/fsync outside the lock.
WritePersistentProperty(std::get<0>(item), std::get<1>(item));
NotifyPropertyChange(std::get<0>(item), std::get<1>(item));
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index e6b868e..547b186 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -27,6 +27,7 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <cutils/android_reboot.h>
+#include <fs_mgr.h>
#include <unwindstack/AndroidUnwinder.h>
#include "capabilities.h"
@@ -48,13 +49,9 @@
const std::string kInitFatalPanicParamString = "androidboot.init_fatal_panic";
if (cmdline.find(kInitFatalPanicParamString) == std::string::npos) {
- init_fatal_panic = false;
- ImportBootconfig(
- [kInitFatalPanicParamString](const std::string& key, const std::string& value) {
- if (key == kInitFatalPanicParamString && value == "true") {
- init_fatal_panic = true;
- }
- });
+ std::string value;
+ init_fatal_panic = (android::fs_mgr::GetBootconfig(kInitFatalPanicParamString, &value) &&
+ value == "true");
} else {
const std::string kInitFatalPanicString = kInitFatalPanicParamString + "=true";
init_fatal_panic = cmdline.find(kInitFatalPanicString) != std::string::npos;
@@ -68,11 +65,7 @@
const std::string kRebootTargetString = "androidboot.init_fatal_reboot_target";
auto start_pos = cmdline.find(kRebootTargetString);
if (start_pos == std::string::npos) {
- ImportBootconfig([kRebootTargetString](const std::string& key, const std::string& value) {
- if (key == kRebootTargetString) {
- init_fatal_reboot_target = value;
- }
- });
+ android::fs_mgr::GetBootconfig(kRebootTargetString, &init_fatal_reboot_target);
// We already default to bootloader if no setting is provided.
} else {
const std::string kRebootTargetStringPattern = kRebootTargetString + "=";
diff --git a/init/security.cpp b/init/security.cpp
index 6e616be..0c73fae 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -106,21 +106,17 @@
// uml does not support mmap_rnd_bits
return {};
#elif defined(__aarch64__)
- // arm64 architecture supports 18 - 33 rnd bits depending on pagesize and
- // VA_SIZE. However the kernel might have been compiled with a narrower
- // range using CONFIG_ARCH_MMAP_RND_BITS_MIN/MAX. To use the maximum
- // supported number of bits, we start from the theoretical maximum of 33
- // bits and try smaller values until we reach 24 bits which is the
- // Android-specific minimum. Don't go lower even if the configured maximum
- // is smaller than 24.
+ // arm64 supports 14 - 33 rnd bits depending on page size and ARM64_VA_BITS.
+ // The kernel (6.5) still defaults to 39 va bits for 4KiB pages, so shipping
+ // devices are only getting 24 bits of randomness in practice.
if (SetMmapRndBitsMin(33, 24, false) && (!Has32BitAbi() || SetMmapRndBitsMin(16, 16, true))) {
return {};
}
#elif defined(__riscv)
- // TODO: sv48 and sv57 were both added to the kernel this year, so we
- // probably just need some kernel fixes to enable higher ASLR randomization,
- // but for now 24 is the maximum that the kernel supports.
- if (SetMmapRndBitsMin(24, 18, false)) {
+ // TODO: sv48 and sv57 have both been added to the kernel, but the kernel
+ // still doesn't support more than 24 bits.
+ // https://github.com/google/android-riscv64/issues/1
+ if (SetMmapRndBitsMin(24, 24, false)) {
return {};
}
#elif defined(__x86_64__)
diff --git a/init/selinux.cpp b/init/selinux.cpp
index e0ef491..9095b85 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -26,29 +26,26 @@
// 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 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 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 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, /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
+// 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
// 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, 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.
+// /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.
// That function contains even more documentation with the specific implementation details of how
// the SEPolicy is compiled if needed.
@@ -61,25 +58,19 @@
#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"
@@ -103,23 +94,14 @@
enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
EnforcingStatus StatusFromProperty() {
- EnforcingStatus status = SELINUX_ENFORCING;
-
- ImportKernelCmdline([&](const std::string& key, const std::string& value) {
- if (key == "androidboot.selinux" && value == "permissive") {
- status = SELINUX_PERMISSIVE;
- }
- });
-
- if (status == SELINUX_ENFORCING) {
- ImportBootconfig([&](const std::string& key, const std::string& value) {
- if (key == "androidboot.selinux" && value == "permissive") {
- status = SELINUX_PERMISSIVE;
- }
- });
+ std::string value;
+ if (android::fs_mgr::GetKernelCmdline("androidboot.selinux", &value) && value == "permissive") {
+ return SELINUX_PERMISSIVE;
}
-
- return status;
+ if (android::fs_mgr::GetBootconfig("androidboot.selinux", &value) && value == "permissive") {
+ return SELINUX_PERMISSIVE;
+ }
+ return SELINUX_ENFORCING;
}
bool IsEnforcing() {
@@ -256,7 +238,6 @@
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) {
@@ -335,7 +316,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, odm, or apex.
+ // * (optional) policy needed due to logic on product, system_ext, or odm images.
// secilc is invoked to compile the above three policy files into a single monolithic policy
// file. This file is then loaded into the kernel.
@@ -431,12 +412,6 @@
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
@@ -479,9 +454,6 @@
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())) {
@@ -508,228 +480,6 @@
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> CreateTmpfsDir() {
- mode_t mode = 0744;
- struct stat stat_data;
- if (stat(kTmpfsDir.c_str(), &stat_data) != 0) {
- if (errno != ENOENT) {
- return ErrnoError() << "Could not stat " << kTmpfsDir;
- }
- if (mkdir(kTmpfsDir.c_str(), mode) != 0) {
- return ErrnoError() << "Could not mkdir " << kTmpfsDir;
- }
- } else {
- if (!S_ISDIR(stat_data.st_mode)) {
- return Error() << kTmpfsDir << " exists and is not a directory.";
- }
- LOG(WARNING) << "Directory " << kTmpfsDir << " already exists";
- }
-
- // Need to manually call chmod because mkdir will create a folder with
- // permissions mode & ~umask.
- if (chmod(kTmpfsDir.c_str(), mode) != 0) {
- return ErrnoError() << "Could not chmod " << kTmpfsDir;
- }
-
- return {};
-}
-
-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 ErrnoError() << "Failed to open " << dstPath;
- }
-
- ret = ExtractEntryToFile(archive, &entry, fd.get());
- 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); });
-
- auto create = CreateTmpfsDir();
- if (!create.ok()) {
- return create.error();
- }
-
- 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 implement 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;
@@ -780,6 +530,14 @@
TEMP_FAILURE_RETRY(send(fd.get(), &request, sizeof(request), 0));
}
+int RestoreconIfExists(const char* path, unsigned int flags) {
+ if (access(path, F_OK) != 0 && errno == ENOENT) {
+ // Avoid error message for path that is expected to not always exist.
+ return 0;
+ }
+ return selinux_android_restorecon(path, flags);
+}
+
} // namespace
void SelinuxRestoreContext() {
@@ -802,14 +560,14 @@
selinux_android_restorecon("/dev/device-mapper", 0);
selinux_android_restorecon("/apex", 0);
-
+ selinux_android_restorecon("/bootstrap-apex", 0);
selinux_android_restorecon("/linkerconfig", 0);
// adb remount, snapshot-based updates, and DSUs all create files during
// first-stage init.
- selinux_android_restorecon(SnapshotManager::GetGlobalRollbackIndicatorPath().c_str(), 0);
- selinux_android_restorecon("/metadata/gsi", SELINUX_ANDROID_RESTORECON_RECURSE |
- SELINUX_ANDROID_RESTORECON_SKIP_SEHASH);
+ RestoreconIfExists(SnapshotManager::GetGlobalRollbackIndicatorPath().c_str(), 0);
+ RestoreconIfExists("/metadata/gsi",
+ SELINUX_ANDROID_RESTORECON_RECURSE | SELINUX_ANDROID_RESTORECON_SKIP_SEHASH);
}
int SelinuxKlogCallback(int type, const char* fmt, ...) {
@@ -998,12 +756,9 @@
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) {
@@ -1019,13 +774,6 @@
snapuserd_helper->FinishTransition();
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";
- }
}
int SetupSelinux(char** argv) {
diff --git a/init/service.cpp b/init/service.cpp
index a0b3478..5e900ee 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -420,7 +420,7 @@
}
});
- if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
+ if (is_updatable() && !IsDefaultMountNamespaceReady()) {
// Don't delay the service for ExecStart() as the semantic is that
// the caller might depend on the side effect of the execution.
return Error() << "Cannot start an updatable service '" << name_
@@ -581,7 +581,7 @@
}
});
- if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
+ if (is_updatable() && !IsDefaultMountNamespaceReady()) {
ServiceList::GetInstance().DelayService(*this);
return Error() << "Cannot start an updatable service '" << name_
<< "' before configs from APEXes are all loaded. "
diff --git a/init/service.h b/init/service.h
index b858eef..9f09cef 100644
--- a/init/service.h
+++ b/init/service.h
@@ -60,7 +60,7 @@
#define SVC_GENTLE_KILL 0x2000 // This service should be stopped with SIGTERM instead of SIGKILL
// Will still be SIGKILLed after timeout period of 200 ms
-#define NR_SVC_SUPP_GIDS 12 // twelve supplementary groups
+#define NR_SVC_SUPP_GIDS 32 // thirty two supplementary groups
namespace android {
namespace init {
diff --git a/init/service_list.cpp b/init/service_list.cpp
index 937d82e..1c56e8a 100644
--- a/init/service_list.cpp
+++ b/init/service_list.cpp
@@ -76,10 +76,7 @@
return post_data_;
}
-void ServiceList::MarkServicesUpdate() {
- services_update_finished_ = true;
-
- // start the delayed services
+void ServiceList::StartDelayedServices() {
for (const auto& name : delayed_service_names_) {
Service* service = FindService(name);
if (service == nullptr) {
@@ -94,7 +91,7 @@
}
void ServiceList::DelayService(const Service& service) {
- if (services_update_finished_) {
+ if (IsDefaultMountNamespaceReady()) {
LOG(ERROR) << "Cannot delay the start of service '" << service.name()
<< "' because all services are already updated. Ignoring.";
return;
diff --git a/init/service_list.h b/init/service_list.h
index f858bc3..44e8453 100644
--- a/init/service_list.h
+++ b/init/service_list.h
@@ -85,14 +85,10 @@
void MarkPostData();
bool IsPostData();
- void MarkServicesUpdate();
- bool IsServicesUpdated() const { return services_update_finished_; }
void DelayService(const Service& service);
+ void StartDelayedServices();
- void ResetState() {
- post_data_ = false;
- services_update_finished_ = false;
- }
+ void ResetState() { post_data_ = false; }
auto size() const { return services_.size(); }
@@ -100,7 +96,6 @@
std::vector<std::unique_ptr<Service>> services_;
bool post_data_ = false;
- bool services_update_finished_ = false;
std::vector<std::string> delayed_service_names_;
};
diff --git a/init/util.cpp b/init/util.cpp
index d0478e8..e760a59 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -42,6 +42,10 @@
#include <cutils/sockets.h>
#include <selinux/android.h>
+#if defined(__ANDROID__)
+#include <fs_mgr.h>
+#endif
+
#ifdef INIT_FULL_SOURCES
#include <android/api-level.h>
#include <sys/system_properties.h>
@@ -60,8 +64,6 @@
namespace android {
namespace init {
-const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
-
const std::string kDataDirPrefix("/data/");
void (*trigger_shutdown)(const std::string& command) = nullptr;
@@ -240,33 +242,6 @@
return -1;
}
-void ImportKernelCmdline(const std::function<void(const std::string&, const std::string&)>& fn) {
- std::string cmdline;
- android::base::ReadFileToString("/proc/cmdline", &cmdline);
-
- for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
- std::vector<std::string> pieces = android::base::Split(entry, "=");
- if (pieces.size() == 2) {
- fn(pieces[0], pieces[1]);
- }
- }
-}
-
-void ImportBootconfig(const std::function<void(const std::string&, const std::string&)>& fn) {
- std::string bootconfig;
- android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
-
- for (const auto& entry : android::base::Split(bootconfig, "\n")) {
- std::vector<std::string> pieces = android::base::Split(entry, "=");
- if (pieces.size() == 2) {
- // get rid of the extra space between a list of values and remove the quotes.
- std::string value = android::base::StringReplace(pieces[1], "\", \"", ",", true);
- value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
- fn(android::base::Trim(pieces[0]), android::base::Trim(value));
- }
- }
-}
-
bool make_dir(const std::string& path, mode_t mode) {
std::string secontext;
if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {
@@ -375,45 +350,18 @@
return dst;
}
-static std::string init_android_dt_dir() {
- // Use the standard procfs-based path by default
- std::string android_dt_dir = kDefaultAndroidDtDir;
- // The platform may specify a custom Android DT path in kernel cmdline
- ImportKernelCmdline([&](const std::string& key, const std::string& value) {
- if (key == "androidboot.android_dt_dir") {
- android_dt_dir = value;
- }
- });
- // ..Or bootconfig
- if (android_dt_dir == kDefaultAndroidDtDir) {
- ImportBootconfig([&](const std::string& key, const std::string& value) {
- if (key == "androidboot.android_dt_dir") {
- android_dt_dir = value;
- }
- });
- }
-
- LOG(INFO) << "Using Android DT directory " << android_dt_dir;
- return android_dt_dir;
-}
-
-// FIXME: The same logic is duplicated in system/core/fs_mgr/
-const std::string& get_android_dt_dir() {
- // Set once and saves time for subsequent calls to this function
- static const std::string kAndroidDtDir = init_android_dt_dir();
- return kAndroidDtDir;
-}
-
// Reads the content of device tree file under the platform's Android DT directory.
// Returns true if the read is success, false otherwise.
bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {
- const std::string file_name = get_android_dt_dir() + sub_path;
+#if defined(__ANDROID__)
+ const std::string file_name = android::fs_mgr::GetAndroidDtDir() + sub_path;
if (android::base::ReadFileToString(file_name, dt_content)) {
if (!dt_content->empty()) {
dt_content->pop_back(); // Trims the trailing '\0' out.
return true;
}
}
+#endif
return false;
}
diff --git a/init/util.h b/init/util.h
index 3f0a4e0..2d02182 100644
--- a/init/util.h
+++ b/init/util.h
@@ -53,16 +53,11 @@
Result<uid_t> DecodeUid(const std::string& name);
bool mkdir_recursive(const std::string& pathname, mode_t mode);
-int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
-void ImportKernelCmdline(const std::function<void(const std::string&, const std::string&)>&);
-void ImportBootconfig(const std::function<void(const std::string&, const std::string&)>&);
+int wait_for_file(const char* filename, std::chrono::nanoseconds timeout);
bool make_dir(const std::string& path, mode_t mode);
bool is_dir(const char* pathname);
Result<std::string> ExpandProps(const std::string& src);
-// Returns the platform's Android DT directory as specified in the kernel cmdline.
-// If the platform does not configure a custom DT path, returns the standard one (based in procfs).
-const std::string& get_android_dt_dir();
// Reads or compares the content of device tree file under the platform's Android DT directory.
bool read_android_dt_file(const std::string& sub_path, std::string* dt_content);
bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 92486e3..8ae7d9e 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -162,7 +162,6 @@
"properties.cpp",
"record_stream.cpp",
"strlcpy.c",
- "threads.cpp",
],
target: {
@@ -172,11 +171,15 @@
"libasync_safe",
],
},
+ linux: {
+ srcs: [
+ "canned_fs_config.cpp",
+ "fs_config.cpp",
+ ],
+ },
not_windows: {
srcs: libcutils_nonwindows_sources + [
"ashmem-host.cpp",
- "canned_fs_config.cpp",
- "fs_config.cpp",
"trace-host.cpp",
],
},
@@ -202,8 +205,6 @@
srcs: libcutils_nonwindows_sources + [
"android_reboot.cpp",
"ashmem-dev.cpp",
- "canned_fs_config.cpp",
- "fs_config.cpp",
"klog.cpp",
"partition_utils.cpp",
"qtaguid.cpp",
diff --git a/libcutils/OWNERS b/libcutils/OWNERS
index 7529cb9..e1cbe4a 100644
--- a/libcutils/OWNERS
+++ b/libcutils/OWNERS
@@ -1 +1,2 @@
+# Bug component: 128577
include platform/system/core:/janitors/OWNERS
diff --git a/libcutils/arch-x86/cache.h b/libcutils/arch-x86/cache.h
deleted file mode 100644
index 1c22fea..0000000
--- a/libcutils/arch-x86/cache.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if defined(__slm__)
-/* Values are optimized for Silvermont */
-#define SHARED_CACHE_SIZE (1024*1024) /* Silvermont L2 Cache */
-#define DATA_CACHE_SIZE (24*1024) /* Silvermont L1 Data Cache */
-#else
-/* Values are optimized for Atom */
-#define SHARED_CACHE_SIZE (512*1024) /* Atom L2 Cache */
-#define DATA_CACHE_SIZE (24*1024) /* Atom L1 Data Cache */
-#endif
-
-#define SHARED_CACHE_SIZE_HALF (SHARED_CACHE_SIZE / 2)
-#define DATA_CACHE_SIZE_HALF (DATA_CACHE_SIZE / 2)
diff --git a/libcutils/arch-x86_64/cache.h b/libcutils/arch-x86_64/cache.h
deleted file mode 100644
index f144309..0000000
--- a/libcutils/arch-x86_64/cache.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-/* Values are optimized for Silvermont */
-#define SHARED_CACHE_SIZE (1024*1024) /* Silvermont L2 Cache */
-#define DATA_CACHE_SIZE (24*1024) /* Silvermont L1 Data Cache */
-
-#define SHARED_CACHE_SIZE_HALF (SHARED_CACHE_SIZE / 2)
-#define DATA_CACHE_SIZE_HALF (DATA_CACHE_SIZE / 2)
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 6a27f9a..410dbfd 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -44,16 +44,6 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-/* Will be added to UAPI once upstream change is merged */
-#define F_SEAL_FUTURE_WRITE 0x0010
-
-/*
- * The minimum vendor API level at and after which it is safe to use memfd.
- * This is to facilitate deprecation of ashmem.
- */
-#define MIN_MEMFD_VENDOR_API_LEVEL 29
-#define MIN_MEMFD_VENDOR_API_LEVEL_CHAR 'Q'
-
/* ashmem identity */
static dev_t __ashmem_rdev;
/*
@@ -91,55 +81,17 @@
/* Determine if vendor processes would be ok with memfd in the system:
*
- * If VNDK is using older libcutils, don't use memfd. This is so that the
- * same shared memory mechanism is used across binder transactions between
- * vendor partition processes and system partition processes.
+ * Previously this function checked if memfd is supported by checking if
+ * vendor VNDK version is greater than Q. As we can assume all treblelized
+ * device using this code is up to date enough to use memfd, memfd is allowed
+ * if the device is treblelized.
*/
static bool check_vendor_memfd_allowed() {
- std::string vndk_version = android::base::GetProperty("ro.vndk.version", "");
+ static bool is_treblelized = android::base::GetBoolProperty("ro.treble.enabled", false);
- if (vndk_version == "") {
- ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
- vndk_version.c_str());
- return false;
- }
-
- /* No issues if vendor is targetting current Dessert */
- if (vndk_version == "current") {
- return false;
- }
-
- /* Check if VNDK version is a number and act on it */
- char* p;
- long int vers = strtol(vndk_version.c_str(), &p, 10);
- if (*p == 0) {
- if (vers < MIN_MEMFD_VENDOR_API_LEVEL) {
- ALOGI("memfd: device VNDK version (%s) is < Q so using ashmem.\n",
- vndk_version.c_str());
- return false;
- }
-
- return true;
- }
-
- // Non-numeric should be a single ASCII character. Characters after the
- // first are ignored.
- if (tolower(vndk_version[0]) < 'a' || tolower(vndk_version[0]) > 'z') {
- ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
- vndk_version.c_str());
- return false;
- }
-
- if (tolower(vndk_version[0]) < tolower(MIN_MEMFD_VENDOR_API_LEVEL_CHAR)) {
- ALOGI("memfd: device is using VNDK version (%s) which is less than Q. Use ashmem only.\n",
- vndk_version.c_str());
- return false;
- }
-
- return true;
+ return is_treblelized;
}
-
/* Determine if memfd can be supported. This is just one-time hardwork
* which will be cached by the caller.
*/
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index f90a1bc..919be2f 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -41,10 +41,6 @@
#include "fs_config.h"
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
using android::base::EndsWith;
using android::base::StartsWith;
@@ -214,6 +210,7 @@
#endif
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/resize2fs" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/snapuserd" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/snapuserd_ramdisk" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/tune2fs" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/fsck.f2fs" },
// generic defaults
@@ -256,12 +253,12 @@
len = strip(target_out_path, len, "/");
len = strip(target_out_path, len, "/system");
if (asprintf(&name, "%.*s%s", (int)len, target_out_path, conf[which][dir]) != -1) {
- fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY));
+ fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY));
free(name);
}
}
if (fd < 0) {
- fd = TEMP_FAILURE_RETRY(open(conf[which][dir], O_RDONLY | O_BINARY));
+ fd = TEMP_FAILURE_RETRY(open(conf[which][dir], O_RDONLY));
}
return fd;
}
diff --git a/libcutils/include/cutils/threads.h b/libcutils/include/cutils/threads.h
deleted file mode 100644
index 0082c6c..0000000
--- a/libcutils/include/cutils/threads.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2007 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 <sys/types.h>
-
-#if defined(_WIN32)
-#include <windows.h>
-#else
-#include <pthread.h>
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-//
-// Deprecated: use android::base::GetThreadId instead, which doesn't truncate on Mac/Windows.
-//
-#if !defined(__GLIBC__) || __GLIBC__ >= 2 && __GLIBC_MINOR__ < 32
-extern pid_t gettid();
-#endif
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/libcutils/include/private/android_filesystem_capability.h b/libcutils/include/private/android_filesystem_capability.h
deleted file mode 100644
index 0227b1d..0000000
--- a/libcutils/include/private/android_filesystem_capability.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-/*
- * Taken from linux/capability.h, with minor modifications
- */
-
-#ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H
-#define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H
-
-#include <stdint.h>
-
-#define __user
-#define __u32 uint32_t
-#define __le32 uint32_t
-
-#define _LINUX_CAPABILITY_VERSION_1 0x19980330
-#define _LINUX_CAPABILITY_U32S_1 1
-#define _LINUX_CAPABILITY_VERSION_2 0x20071026
-#define _LINUX_CAPABILITY_U32S_2 2
-#define _LINUX_CAPABILITY_VERSION_3 0x20080522
-#define _LINUX_CAPABILITY_U32S_3 2
-
-typedef struct __user_cap_header_struct {
- __u32 version;
- int pid;
-} __user* cap_user_header_t;
-
-typedef struct __user_cap_data_struct {
- __u32 effective;
- __u32 permitted;
- __u32 inheritable;
-} __user* cap_user_data_t;
-
-#define VFS_CAP_REVISION_MASK 0xFF000000
-#define VFS_CAP_REVISION_SHIFT 24
-#define VFS_CAP_FLAGS_MASK ~VFS_CAP_REVISION_MASK
-#define VFS_CAP_FLAGS_EFFECTIVE 0x000001
-#define VFS_CAP_REVISION_1 0x01000000
-#define VFS_CAP_U32_1 1
-#define XATTR_CAPS_SZ_1 (sizeof(__le32) * (1 + 2 * VFS_CAP_U32_1))
-#define VFS_CAP_REVISION_2 0x02000000
-#define VFS_CAP_U32_2 2
-#define XATTR_CAPS_SZ_2 (sizeof(__le32) * (1 + 2 * VFS_CAP_U32_2))
-#define XATTR_CAPS_SZ XATTR_CAPS_SZ_2
-#define VFS_CAP_U32 VFS_CAP_U32_2
-#define VFS_CAP_REVISION VFS_CAP_REVISION_2
-
-struct vfs_cap_data {
- __le32 magic_etc;
- struct {
- __le32 permitted;
- __le32 inheritable;
- } data[VFS_CAP_U32];
-};
-
-#define _LINUX_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_1
-#define _LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_1
-#define CAP_CHOWN 0
-#define CAP_DAC_OVERRIDE 1
-#define CAP_DAC_READ_SEARCH 2
-#define CAP_FOWNER 3
-#define CAP_FSETID 4
-#define CAP_KILL 5
-#define CAP_SETGID 6
-#define CAP_SETUID 7
-#define CAP_SETPCAP 8
-#define CAP_LINUX_IMMUTABLE 9
-#define CAP_NET_BIND_SERVICE 10
-#define CAP_NET_BROADCAST 11
-#define CAP_NET_ADMIN 12
-#define CAP_NET_RAW 13
-#define CAP_IPC_LOCK 14
-#define CAP_IPC_OWNER 15
-#define CAP_SYS_MODULE 16
-#define CAP_SYS_RAWIO 17
-#define CAP_SYS_CHROOT 18
-#define CAP_SYS_PTRACE 19
-#define CAP_SYS_PACCT 20
-#define CAP_SYS_ADMIN 21
-#define CAP_SYS_BOOT 22
-#define CAP_SYS_NICE 23
-#define CAP_SYS_RESOURCE 24
-#define CAP_SYS_TIME 25
-#define CAP_SYS_TTY_CONFIG 26
-#define CAP_MKNOD 27
-#define CAP_LEASE 28
-#define CAP_AUDIT_WRITE 29
-#define CAP_AUDIT_CONTROL 30
-#define CAP_SETFCAP 31
-#define CAP_MAC_OVERRIDE 32
-#define CAP_MAC_ADMIN 33
-#define CAP_SYSLOG 34
-#define CAP_WAKE_ALARM 35
-#define CAP_BLOCK_SUSPEND 36
-#define CAP_AUDIT_READ 37
-#define CAP_LAST_CAP CAP_AUDIT_READ
-#define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
-#define CAP_TO_INDEX(x) ((x) >> 5)
-#define CAP_TO_MASK(x) (1 << ((x)&31))
-
-#undef __user
-#undef __u32
-#undef __le32
-
-#endif
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
index 8a9a1ff..45f46e5 100644
--- a/libcutils/include/private/fs_config.h
+++ b/libcutils/include/private/fs_config.h
@@ -24,11 +24,7 @@
#include <stdint.h>
#include <sys/cdefs.h>
-#if defined(__BIONIC__)
#include <linux/capability.h>
-#else // defined(__BIONIC__)
-#include <private/android_filesystem_capability.h>
-#endif // defined(__BIONIC__)
/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
diff --git a/libcutils/include_outside_system/cutils/threads.h b/libcutils/include_outside_system/cutils/threads.h
deleted file mode 120000
index 99330ff..0000000
--- a/libcutils/include_outside_system/cutils/threads.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/cutils/threads.h
\ No newline at end of file
diff --git a/libcutils/iosched_policy.cpp b/libcutils/iosched_policy.cpp
index 012c537..f7c724d 100644
--- a/libcutils/iosched_policy.cpp
+++ b/libcutils/iosched_policy.cpp
@@ -24,8 +24,7 @@
#include <unistd.h>
#if defined(__ANDROID__)
-#define IOPRIO_WHO_PROCESS (1)
-#define IOPRIO_CLASS_SHIFT (13)
+#include <linux/ioprio.h>
#include <sys/syscall.h>
#define __android_unused
#else
diff --git a/libcutils/socket_local_unix.h b/libcutils/socket_local_unix.h
index 45b9856..ea98c08 100644
--- a/libcutils/socket_local_unix.h
+++ b/libcutils/socket_local_unix.h
@@ -17,6 +17,8 @@
#ifndef __SOCKET_LOCAL_H
#define __SOCKET_LOCAL_H
+#include <sys/socket.h>
+
#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
diff --git a/libcutils/sockets_windows.cpp b/libcutils/sockets_windows.cpp
index 4adb796..99a2e2d 100644
--- a/libcutils/sockets_windows.cpp
+++ b/libcutils/sockets_windows.cpp
@@ -35,7 +35,7 @@
// can be extremely tricky and cause deadlock when using threads or atexit().
//
// Both adb (1) and Chrome (2) purposefully avoid WSACleanup() with no issues.
-// (1) https://android.googlesource.com/platform/system/core.git/+/master/adb/sysdeps_win32.cpp
+// (1) https://android.googlesource.com/platform/packages/modules/adb.git/+/main/sysdeps_win32.cpp
// (2) https://code.google.com/p/chromium/codesearch#chromium/src/net/base/winsock_init.cc
bool initialize_windows_sockets() {
// There's no harm in calling WSAStartup() multiple times but no benefit
diff --git a/libcutils/threads.cpp b/libcutils/threads.cpp
deleted file mode 100644
index 6ece7a3..0000000
--- a/libcutils/threads.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-** Copyright (C) 2007, 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 <cutils/threads.h>
-
-#if defined(__APPLE__)
-#include <stdint.h>
-#elif defined(__linux__)
-#include <syscall.h>
-#include <unistd.h>
-#elif defined(_WIN32)
-#include <windows.h>
-#endif
-
-#if defined(__BIONIC__) || defined(__GLIBC__) && __GLIBC_MINOR__ >= 32
-// No definition needed for Android because we'll just pick up bionic's copy.
-// No definition needed for Glibc >= 2.32 because it exposes its own copy.
-#else
-pid_t gettid() {
-#if defined(__APPLE__)
- uint64_t tid;
- pthread_threadid_np(NULL, &tid);
- return tid;
-#elif defined(__linux__)
- return syscall(__NR_gettid);
-#elif defined(_WIN32)
- return GetCurrentThreadId();
-#endif
-}
-#endif
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 06d386f..cc2565f 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -206,11 +206,11 @@
}
static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
- return StringPrintf("%s/uid_%d", cgroup, uid);
+ return StringPrintf("%s/uid_%u", cgroup, uid);
}
static std::string ConvertUidPidToPath(const char* cgroup, uid_t uid, int pid) {
- return StringPrintf("%s/uid_%d/pid_%d", cgroup, uid, pid);
+ return StringPrintf("%s/uid_%u/pid_%d", cgroup, uid, pid);
}
static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid, unsigned int retries) {
@@ -219,7 +219,7 @@
while (retries--) {
ret = rmdir(uid_pid_path.c_str());
- if (!ret || errno != EBUSY) break;
+ if (!ret || errno != EBUSY || !retries) break;
std::this_thread::sleep_for(5ms);
}
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index d013ec8..3e4393d 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -1,6 +1,13 @@
{
"Cgroups": [
{
+ "Controller": "blkio",
+ "Path": "/dev/blkio",
+ "Mode": "0775",
+ "UID": "system",
+ "GID": "system"
+ },
+ {
"Controller": "cpu",
"Path": "/dev/cpuctl",
"Mode": "0755",
@@ -32,12 +39,6 @@
{
"Controller": "freezer",
"Path": "."
- },
- {
- "Controller": "io",
- "Path": ".",
- "NeedsActivation": true,
- "Optional": true
}
]
}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 12f7b44..1fc66ba 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -76,24 +76,6 @@
"Name": "FreezerState",
"Controller": "freezer",
"File": "cgroup.freeze"
- },
- {
- "Name": "BfqWeight",
- "Controller": "io",
- "File": "blkio.bfq.weight",
- "FileV2": "io.bfq.weight"
- },
- {
- "Name": "CfqGroupIdle",
- "Controller": "io",
- "File": "blkio.group_idle",
- "FileV2": "io.group_idle"
- },
- {
- "Name": "CfqWeight",
- "Controller": "io",
- "File": "blkio.weight",
- "FileV2": "io.weight"
}
],
@@ -457,30 +439,11 @@
"Name": "LowIoPriority",
"Actions": [
{
- "Name": "SetAttribute",
+ "Name": "JoinCgroup",
"Params":
{
- "Name": "BfqWeight",
- "Value": "10",
- "Optional": "true"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "CfqGroupIdle",
- "Value": "0",
- "Optional": "true"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "CfqWeight",
- "Value": "200",
- "Optional": "true"
+ "Controller": "blkio",
+ "Path": "background"
}
}
]
@@ -489,30 +452,11 @@
"Name": "NormalIoPriority",
"Actions": [
{
- "Name": "SetAttribute",
+ "Name": "JoinCgroup",
"Params":
{
- "Name": "BfqWeight",
- "Value": "100",
- "Optional": "true"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "CfqGroupIdle",
- "Value": "0",
- "Optional": "true"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "CfqWeight",
- "Value": "1000",
- "Optional": "true"
+ "Controller": "blkio",
+ "Path": ""
}
}
]
@@ -521,30 +465,11 @@
"Name": "HighIoPriority",
"Actions": [
{
- "Name": "SetAttribute",
+ "Name": "JoinCgroup",
"Params":
{
- "Name": "BfqWeight",
- "Value": "100",
- "Optional": "true"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "CfqGroupIdle",
- "Value": "0",
- "Optional": "true"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "CfqWeight",
- "Value": "1000",
- "Optional": "true"
+ "Controller": "blkio",
+ "Path": ""
}
}
]
@@ -553,30 +478,11 @@
"Name": "MaxIoPriority",
"Actions": [
{
- "Name": "SetAttribute",
+ "Name": "JoinCgroup",
"Params":
{
- "Name": "BfqWeight",
- "Value": "100",
- "Optional": "true"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "CfqGroupIdle",
- "Value": "0",
- "Optional": "true"
- }
- },
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "CfqWeight",
- "Value": "1000",
- "Optional": "true"
+ "Controller": "blkio",
+ "Path": ""
}
}
]
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 44dba2a..f51b076 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -114,9 +114,26 @@
IProfileAttribute::~IProfileAttribute() = default;
-void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) {
+const std::string& ProfileAttribute::file_name() const {
+ if (controller()->version() == 2 && !file_v2_name_.empty()) return file_v2_name_;
+ return file_name_;
+}
+
+void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name,
+ const std::string& file_v2_name) {
controller_ = controller;
file_name_ = file_name;
+ file_v2_name_ = file_v2_name;
+}
+
+bool ProfileAttribute::GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const {
+ if (controller()->version() == 2) {
+ // all cgroup v2 attributes use the same process group hierarchy
+ *path = StringPrintf("%s/uid_%u/pid_%d/%s", controller()->path(), uid, pid,
+ file_name().c_str());
+ return true;
+ }
+ return GetPathForTask(pid, path);
}
bool ProfileAttribute::GetPathForTask(int tid, std::string* path) const {
@@ -129,12 +146,11 @@
return true;
}
- const std::string& file_name =
- controller()->version() == 2 && !file_v2_name_.empty() ? file_v2_name_ : file_name_;
if (subgroup.empty()) {
- *path = StringPrintf("%s/%s", controller()->path(), file_name.c_str());
+ *path = StringPrintf("%s/%s", controller()->path(), file_name().c_str());
} else {
- *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(), file_name.c_str());
+ *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(),
+ file_name().c_str());
}
return true;
}
@@ -144,9 +160,7 @@
return true;
}
- const std::string& file_name =
- controller()->version() == 2 && !file_v2_name_.empty() ? file_v2_name_ : file_name_;
- *path = StringPrintf("%s/uid_%d/%s", controller()->path(), uid, file_name.c_str());
+ *path = StringPrintf("%s/uid_%u/%s", controller()->path(), uid, file_name().c_str());
return true;
}
@@ -205,18 +219,7 @@
#endif
-bool SetAttributeAction::ExecuteForProcess(uid_t, pid_t pid) const {
- return ExecuteForTask(pid);
-}
-
-bool SetAttributeAction::ExecuteForTask(int tid) const {
- std::string path;
-
- if (!attribute_->GetPathForTask(tid, &path)) {
- LOG(ERROR) << "Failed to find cgroup for tid " << tid;
- return false;
- }
-
+bool SetAttributeAction::WriteValueToFile(const std::string& path) const {
if (!WriteStringToFile(value_, path)) {
if (access(path.c_str(), F_OK) < 0) {
if (optional_) {
@@ -236,6 +239,28 @@
return true;
}
+bool SetAttributeAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+ std::string path;
+
+ if (!attribute_->GetPathForProcess(uid, pid, &path)) {
+ LOG(ERROR) << "Failed to find cgroup for uid " << uid << " pid " << pid;
+ return false;
+ }
+
+ return WriteValueToFile(path);
+}
+
+bool SetAttributeAction::ExecuteForTask(int tid) const {
+ std::string path;
+
+ if (!attribute_->GetPathForTask(tid, &path)) {
+ LOG(ERROR) << "Failed to find cgroup for tid " << tid;
+ return false;
+ }
+
+ return WriteValueToFile(path);
+}
+
bool SetAttributeAction::ExecuteForUID(uid_t uid) const {
std::string path;
@@ -816,7 +841,7 @@
attributes_[name] =
std::make_unique<ProfileAttribute>(controller, file_attr, file_v2_attr);
} else {
- iter->second->Reset(controller, file_attr);
+ iter->second->Reset(controller, file_attr, file_v2_attr);
}
} else {
LOG(WARNING) << "Controller " << controller_name << " is not found";
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index a62c5b0..4663f64 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -32,9 +32,11 @@
class IProfileAttribute {
public:
virtual ~IProfileAttribute() = 0;
- virtual void Reset(const CgroupController& controller, const std::string& file_name) = 0;
+ virtual void Reset(const CgroupController& controller, const std::string& file_name,
+ const std::string& file_v2_name) = 0;
virtual const CgroupController* controller() const = 0;
virtual const std::string& file_name() const = 0;
+ virtual bool GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const = 0;
virtual bool GetPathForTask(int tid, std::string* path) const = 0;
virtual bool GetPathForUID(uid_t uid, std::string* path) const = 0;
};
@@ -50,9 +52,11 @@
~ProfileAttribute() = default;
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;
+ const std::string& file_name() const override;
+ void Reset(const CgroupController& controller, const std::string& file_name,
+ const std::string& file_v2_name) override;
+ bool GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const override;
bool GetPathForTask(int tid, std::string* path) const override;
bool GetPathForUID(uid_t uid, std::string* path) const override;
@@ -131,6 +135,8 @@
const IProfileAttribute* attribute_;
std::string value_;
bool optional_;
+
+ bool WriteValueToFile(const std::string& path) const;
};
// Set cgroup profile element
diff --git a/libprocessgroup/task_profiles_test.cpp b/libprocessgroup/task_profiles_test.cpp
index eadbe76..99d819a 100644
--- a/libprocessgroup/task_profiles_test.cpp
+++ b/libprocessgroup/task_profiles_test.cpp
@@ -102,7 +102,8 @@
public:
ProfileAttributeMock(const std::string& file_name) : file_name_(file_name) {}
~ProfileAttributeMock() override = default;
- void Reset(const CgroupController& controller, const std::string& file_name) override {
+ void Reset(const CgroupController& controller, const std::string& file_name,
+ const std::string& file_v2_name) override {
CHECK(false);
}
const CgroupController* controller() const override {
@@ -110,6 +111,9 @@
return {};
}
const std::string& file_name() const override { return file_name_; }
+ bool GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const override {
+ return GetPathForTask(pid, path);
+ }
bool GetPathForTask(int tid, std::string* path) const override {
#ifdef __ANDROID__
CHECK(CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, path));
@@ -125,9 +129,7 @@
return true;
};
- bool GetPathForUID(uid_t, std::string*) const override {
- return false;
- }
+ bool GetPathForUID(uid_t, std::string*) const override { return false; }
private:
const std::string file_name_;
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 5a7d0fc..44907a1 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -41,9 +41,8 @@
],
}
-cc_binary {
+cc_binary_host {
name: "simg2img",
- host_supported: true,
srcs: [
"simg2img.cpp",
"sparse_crc32.cpp",
@@ -62,9 +61,8 @@
},
}
-cc_binary {
+cc_binary_host {
name: "img2simg",
- host_supported: true,
srcs: ["img2simg.cpp"],
static_libs: [
"libsparse",
diff --git a/libstats/pull_rust/stats_pull.rs b/libstats/pull_rust/stats_pull.rs
index 09b2623..d188b5f 100644
--- a/libstats/pull_rust/stats_pull.rs
+++ b/libstats/pull_rust/stats_pull.rs
@@ -111,7 +111,9 @@
static ref COOKIES: Mutex<HashMap<i32, fn() -> StatsPullResult>> = Mutex::new(HashMap::new());
}
-// Safety: We store our callbacks in the global so they are valid.
+/// # Safety
+///
+/// `data` must be a valid pointer with no aliases.
unsafe extern "C" fn callback_wrapper(
atom_tag: i32,
data: *mut AStatsEventList,
@@ -126,7 +128,8 @@
let stats = cb();
let result = stats
.iter()
- .map(|stat| stat.add_astats_event(&mut *data))
+ // Safety: The caller promises that `data` is valid and unaliased.
+ .map(|stat| stat.add_astats_event(unsafe { &mut *data }))
.collect::<Result<Vec<()>, StatsError>>();
match result {
Ok(_) => {
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 162f0f4..2c05fbc 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -185,10 +185,21 @@
support_system_process: true,
},
- header_abi_checker: {
- // AFDO affects weak symbols.
- diff_flags: ["-allow-adding-removing-weak-symbols"],
- ref_dump_dirs: ["abi-dumps"],
+ target: {
+ product: {
+ header_abi_checker: {
+ // AFDO affects weak symbols.
+ diff_flags: ["-allow-adding-removing-weak-symbols"],
+ ref_dump_dirs: ["abi-dumps"],
+ },
+ },
+ vendor: {
+ header_abi_checker: {
+ // AFDO affects weak symbols.
+ diff_flags: ["-allow-adding-removing-weak-symbols"],
+ ref_dump_dirs: ["abi-dumps"],
+ },
+ },
},
}
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index 4dcb35b..fe827eb 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -18,7 +18,7 @@
#include <utils/Printer.h>
#include <utils/Errors.h>
-#include <utils/Log.h>
+#include <log/log.h>
#include <unwindstack/AndroidUnwinder.h>
@@ -82,7 +82,7 @@
void CallStack::print(Printer& printer) const {
for (size_t i = 0; i < mFrameLines.size(); i++) {
- printer.printLine(mFrameLines[i]);
+ printer.printLine(mFrameLines[i].c_str());
}
}
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 0abb861..3acbce9 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -21,7 +21,7 @@
#define LOG_TAG "filemap"
#include <utils/FileMap.h>
-#include <utils/Log.h>
+#include <log/log.h>
#if defined(__MINGW32__) && !defined(__USE_MINGW_ANSI_STDIO)
# define PRId32 "I32d"
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
index c9ae210..4bd49f1 100644
--- a/libutils/Printer.cpp
+++ b/libutils/Printer.cpp
@@ -19,7 +19,7 @@
#include <utils/Printer.h>
#include <utils/String8.h>
-#include <utils/Log.h>
+#include <log/log.h>
#include <stdlib.h>
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
index f054de9..4b27bdd 100644
--- a/libutils/ProcessCallStack.cpp
+++ b/libutils/ProcessCallStack.cpp
@@ -205,8 +205,7 @@
}
void ProcessCallStack::printInternal(Printer& printer, Printer& csPrinter) const {
- dumpProcessHeader(printer, getpid(),
- getTimeString(mTimeUpdated).string());
+ dumpProcessHeader(printer, getpid(), getTimeString(mTimeUpdated).c_str());
for (size_t i = 0; i < mThreadMap.size(); ++i) {
pid_t tid = mThreadMap.keyAt(i);
@@ -214,7 +213,7 @@
const String8& threadName = threadInfo.threadName;
printer.printLine("");
- printer.printFormatLine("\"%s\" sysTid=%d", threadName.string(), tid);
+ printer.printFormatLine("\"%s\" sysTid=%d", threadName.c_str(), tid);
threadInfo.callStack.print(csPrinter);
}
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index ab122c7..e0a2846 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -330,7 +330,7 @@
this);
int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 0644);
if (rc >= 0) {
- (void)write(rc, text.string(), text.length());
+ (void)write(rc, text.c_str(), text.length());
close(rc);
ALOGI("STACK TRACE for %p saved in %s", this, name);
}
diff --git a/libutils/RefBase_fuzz.cpp b/libutils/RefBase_fuzz.cpp
index 8291be9..05f47a0 100644
--- a/libutils/RefBase_fuzz.cpp
+++ b/libutils/RefBase_fuzz.cpp
@@ -19,7 +19,7 @@
#include <thread>
#include "fuzzer/FuzzedDataProvider.h"
-#include "utils/Log.h"
+#include "log/log.h"
#include "utils/RWLock.h"
#include "utils/RefBase.h"
#include "utils/StrongPointer.h"
diff --git a/libutils/StopWatch.cpp b/libutils/StopWatch.cpp
index 28e2d76..c88d60f 100644
--- a/libutils/StopWatch.cpp
+++ b/libutils/StopWatch.cpp
@@ -24,7 +24,7 @@
#endif
#include <inttypes.h>
-#include <utils/Log.h>
+#include <log/log.h>
namespace android {
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 68642d8..07a3d23 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -16,7 +16,7 @@
#include <utils/String16.h>
-#include <utils/Log.h>
+#include <log/log.h>
#include <ctype.h>
@@ -26,7 +26,7 @@
static const StaticString16 emptyString(u"");
static inline char16_t* getEmptyString() {
- return const_cast<char16_t*>(emptyString.string());
+ return const_cast<char16_t*>(emptyString.c_str());
}
// ---------------------------------------------------------------------------
@@ -112,10 +112,7 @@
String16::String16(const char16_t* o, size_t len) : mString(allocFromUTF16(o, len)) {}
-String16::String16(const String8& o)
- : mString(allocFromUTF8(o.string(), o.size()))
-{
-}
+String16::String16(const String8& o) : mString(allocFromUTF8(o.c_str(), o.size())) {}
String16::String16(const char* o)
: mString(allocFromUTF8(o, strlen(o)))
@@ -173,7 +170,7 @@
LOG_ALWAYS_FATAL("Not implemented");
}
- return setTo(other.string()+begin, len);
+ return setTo(other.c_str() + begin, len);
}
status_t String16::setTo(const char16_t* other)
@@ -200,7 +197,7 @@
}
status_t String16::append(const String16& other) {
- return append(other.string(), other.size());
+ return append(other.c_str(), other.size());
}
status_t String16::append(const char16_t* chrs, size_t otherLen) {
@@ -286,7 +283,7 @@
{
const size_t ps = prefix.size();
if (ps > size()) return false;
- return strzcmp16(mString, ps, prefix.string(), ps) == 0;
+ return strzcmp16(mString, ps, prefix.c_str(), ps) == 0;
}
bool String16::startsWith(const char16_t* prefix) const
diff --git a/libutils/String16_fuzz.cpp b/libutils/String16_fuzz.cpp
index d7e5ec7..8f9781b 100644
--- a/libutils/String16_fuzz.cpp
+++ b/libutils/String16_fuzz.cpp
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <functional>
#include <iostream>
+#include <vector>
#include "fuzzer/FuzzedDataProvider.h"
#include "utils/String16.h"
@@ -25,7 +27,7 @@
// Bytes and size
([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
- str1.string();
+ str1.c_str();
}),
([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
str1.isStaticString();
@@ -39,7 +41,7 @@
str1.startsWith(str2);
}),
([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
- str1.contains(str2.string());
+ str1.contains(str2.c_str());
}),
([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
str1.compare(str2);
@@ -52,7 +54,7 @@
([](FuzzedDataProvider& dataProvider, android::String16 str1,
android::String16 str2) -> void {
int pos = dataProvider.ConsumeIntegralInRange<int>(0, str1.size());
- str1.insert(pos, str2.string());
+ str1.insert(pos, str2.c_str());
}),
// Find and replace operations
diff --git a/libutils/String16_test.cpp b/libutils/String16_test.cpp
index c6e6f74..6f4642e 100644
--- a/libutils/String16_test.cpp
+++ b/libutils/String16_test.cpp
@@ -33,50 +33,50 @@
TEST(String16Test, FromChar16_t) {
String16 tmp(u"Verify me");
- EXPECT_STR16EQ(u"Verify me", tmp);
+ EXPECT_STR16EQ(u"Verify me", tmp.c_str());
}
TEST(String16Test, FromChar16_tSized) {
String16 tmp(u"Verify me", 7);
- EXPECT_STR16EQ(u"Verify ", tmp);
+ EXPECT_STR16EQ(u"Verify ", tmp.c_str());
}
TEST(String16Test, FromChar) {
String16 tmp("Verify me");
- EXPECT_STR16EQ(u"Verify me", tmp);
+ EXPECT_STR16EQ(u"Verify me", tmp.c_str());
}
TEST(String16Test, FromCharSized) {
String16 tmp("Verify me", 7);
- EXPECT_STR16EQ(u"Verify ", tmp);
+ EXPECT_STR16EQ(u"Verify ", tmp.c_str());
}
TEST(String16Test, Copy) {
String16 tmp("Verify me");
String16 another = tmp;
- EXPECT_STR16EQ(u"Verify me", tmp);
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", tmp.c_str());
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
}
TEST(String16Test, CopyAssign) {
String16 tmp("Verify me");
String16 another;
another = tmp;
- EXPECT_STR16EQ(u"Verify me", tmp);
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", tmp.c_str());
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
}
TEST(String16Test, Move) {
String16 tmp("Verify me");
String16 another(std::move(tmp));
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
}
TEST(String16Test, MoveAssign) {
String16 tmp("Verify me");
String16 another;
another = std::move(tmp);
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
}
TEST(String16Test, Size) {
@@ -88,27 +88,27 @@
String16 tmp("Verify me");
tmp.setTo(u"New content");
EXPECT_EQ(11U, tmp.size());
- EXPECT_STR16EQ(u"New content", tmp);
+ EXPECT_STR16EQ(u"New content", tmp.c_str());
}
TEST(String16Test, Append) {
String16 tmp("Verify me");
tmp.append(String16("Hello"));
EXPECT_EQ(14U, tmp.size());
- EXPECT_STR16EQ(u"Verify meHello", tmp);
+ EXPECT_STR16EQ(u"Verify meHello", tmp.c_str());
}
TEST(String16Test, Insert) {
String16 tmp("Verify me");
tmp.insert(6, u"Insert");
EXPECT_EQ(15U, tmp.size());
- EXPECT_STR16EQ(u"VerifyInsert me", tmp);
+ EXPECT_STR16EQ(u"VerifyInsert me", tmp.c_str());
}
TEST(String16Test, ReplaceAll) {
String16 tmp("Verify verify Verify");
tmp.replaceAll(u'r', u'!');
- EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp);
+ EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp.c_str());
}
TEST(String16Test, Compare) {
@@ -127,8 +127,8 @@
TEST(String16Test, StaticStringCopy) {
StaticString16 tmp(u"Verify me");
String16 another = tmp;
- EXPECT_STR16EQ(u"Verify me", tmp);
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", tmp.c_str());
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
EXPECT_TRUE(tmp.isStaticString());
EXPECT_TRUE(another.isStaticString());
}
@@ -136,7 +136,7 @@
TEST(String16Test, StaticStringMove) {
StaticString16 tmp(u"Verify me");
String16 another(std::move(tmp));
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
EXPECT_TRUE(another.isStaticString());
}
@@ -157,7 +157,7 @@
StaticString16 tmp(u"Verify me");
tmp.append(String16("Hello"));
EXPECT_EQ(14U, tmp.size());
- EXPECT_STR16EQ(u"Verify meHello", tmp);
+ EXPECT_STR16EQ(u"Verify meHello", tmp.c_str());
EXPECT_FALSE(tmp.isStaticString());
}
@@ -165,14 +165,14 @@
StaticString16 tmp(u"Verify me");
tmp.insert(6, u"Insert");
EXPECT_EQ(15U, tmp.size());
- EXPECT_STR16EQ(u"VerifyInsert me", tmp);
+ EXPECT_STR16EQ(u"VerifyInsert me", tmp.c_str());
EXPECT_FALSE(tmp.isStaticString());
}
TEST(String16Test, StaticStringReplaceAll) {
StaticString16 tmp(u"Verify verify Verify");
tmp.replaceAll(u'r', u'!');
- EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp);
+ EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp.c_str());
EXPECT_FALSE(tmp.isStaticString());
}
@@ -185,17 +185,17 @@
StaticString16 tmp(u"Verify me");
String16 another(u"nonstatic");
another = tmp;
- EXPECT_STR16EQ(u"Verify me", tmp);
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", tmp.c_str());
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
}
TEST(String16Test, StringCopyAssignFromStaticString) {
StaticString16 tmp(u"Verify me");
String16 another(u"nonstatic");
another = tmp;
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
EXPECT_TRUE(another.isStaticString());
- EXPECT_STR16EQ(u"Verify me", tmp);
+ EXPECT_STR16EQ(u"Verify me", tmp.c_str());
EXPECT_TRUE(tmp.isStaticString());
}
@@ -203,7 +203,7 @@
StaticString16 tmp(u"Verify me");
String16 another(u"nonstatic");
another = std::move(tmp);
- EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_STR16EQ(u"Verify me", another.c_str());
EXPECT_TRUE(another.isStaticString());
}
@@ -221,19 +221,19 @@
TEST(String16Test, ValidUtf8Conversion) {
String16 another("abcdef");
EXPECT_EQ(6U, another.size());
- EXPECT_STR16EQ(another, u"abcdef");
+ EXPECT_STR16EQ(another.c_str(), u"abcdef");
}
TEST(String16Test, append) {
String16 s;
EXPECT_EQ(OK, s.append(String16(u"foo")));
- EXPECT_STR16EQ(u"foo", s);
+ EXPECT_STR16EQ(u"foo", s.c_str());
EXPECT_EQ(OK, s.append(String16(u"bar")));
- EXPECT_STR16EQ(u"foobar", s);
+ EXPECT_STR16EQ(u"foobar", s.c_str());
EXPECT_EQ(OK, s.append(u"baz", 0));
- EXPECT_STR16EQ(u"foobar", s);
+ EXPECT_STR16EQ(u"foobar", s.c_str());
EXPECT_EQ(NO_MEMORY, s.append(u"baz", SIZE_MAX));
- EXPECT_STR16EQ(u"foobar", s);
+ EXPECT_STR16EQ(u"foobar", s.c_str());
}
TEST(String16Test, insert) {
@@ -241,19 +241,19 @@
// Inserting into the empty string inserts at the start.
EXPECT_EQ(OK, s.insert(123, u"foo"));
- EXPECT_STR16EQ(u"foo", s);
+ EXPECT_STR16EQ(u"foo", s.c_str());
// Inserting zero characters at any position is okay, but won't expand the string.
EXPECT_EQ(OK, s.insert(123, u"foo", 0));
- EXPECT_STR16EQ(u"foo", s);
+ EXPECT_STR16EQ(u"foo", s.c_str());
// Inserting past the end of a non-empty string appends.
EXPECT_EQ(OK, s.insert(123, u"bar"));
- EXPECT_STR16EQ(u"foobar", s);
+ EXPECT_STR16EQ(u"foobar", s.c_str());
EXPECT_EQ(OK, s.insert(3, u"!"));
- EXPECT_STR16EQ(u"foo!bar", s);
+ EXPECT_STR16EQ(u"foo!bar", s.c_str());
EXPECT_EQ(NO_MEMORY, s.insert(3, u"", SIZE_MAX));
- EXPECT_STR16EQ(u"foo!bar", s);
+ EXPECT_STR16EQ(u"foo!bar", s.c_str());
}
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 82f5cb6..6a75484 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -20,7 +20,7 @@
#include <utils/String8.h>
#include <utils/Compat.h>
-#include <utils/Log.h>
+#include <log/log.h>
#include <utils/String16.h>
#include <ctype.h>
@@ -39,10 +39,6 @@
namespace android {
-// Separator used by resource paths. This is not platform dependent contrary
-// to OS_PATH_SEPARATOR.
-#define RES_PATH_SEPARATOR '/'
-
static inline char* getEmptyString() {
static SharedBuffer* gEmptyStringBuf = [] {
SharedBuffer* buf = SharedBuffer::alloc(1);
@@ -150,10 +146,7 @@
}
}
-String8::String8(const String16& o)
- : mString(allocFromUTF16(o.string(), o.size()))
-{
-}
+String8::String8(const String16& o) : mString(allocFromUTF16(o.c_str(), o.size())) {}
String8::String8(const char16_t* o)
: mString(allocFromUTF16(o, strlen16(o)))
@@ -267,7 +260,7 @@
return OK;
}
- return real_append(other.string(), otherLen);
+ return real_append(other.c_str(), otherLen);
}
status_t String8::append(const char* other)
@@ -393,6 +386,11 @@
}
bool String8::removeAll(const char* other) {
+ ALOG_ASSERT(other, "String8::removeAll() requires a non-NULL string");
+
+ if (*other == '\0')
+ return true;
+
ssize_t index = find(other);
if (index < 0) return false;
@@ -432,31 +430,6 @@
// ---------------------------------------------------------------------------
// Path functions
-static void setPathName(String8& s, const char* name) {
- size_t len = strlen(name);
- char* buf = s.lockBuffer(len);
-
- memcpy(buf, name, len);
-
- // remove trailing path separator, if present
- if (len > 0 && buf[len - 1] == OS_PATH_SEPARATOR) len--;
- buf[len] = '\0';
-
- s.unlockBuffer(len);
-}
-
-String8 String8::getPathLeaf(void) const
-{
- const char* cp;
- const char*const buf = mString;
-
- cp = strrchr(buf, OS_PATH_SEPARATOR);
- if (cp == nullptr)
- return String8(*this);
- else
- return String8(cp+1);
-}
-
String8 String8::getPathDir(void) const
{
const char* cp;
@@ -469,40 +442,14 @@
return String8(str, cp - str);
}
-String8 String8::walkPath(String8* outRemains) const
-{
- const char* cp;
- const char*const str = mString;
- const char* buf = str;
-
- cp = strchr(buf, OS_PATH_SEPARATOR);
- if (cp == buf) {
- // don't include a leading '/'.
- buf = buf+1;
- cp = strchr(buf, OS_PATH_SEPARATOR);
- }
-
- if (cp == nullptr) {
- String8 res = buf != str ? String8(buf) : *this;
- if (outRemains) *outRemains = String8("");
- return res;
- }
-
- String8 res(buf, cp-buf);
- if (outRemains) *outRemains = String8(cp+1);
- return res;
-}
-
/*
* Helper function for finding the start of an extension in a pathname.
*
* Returns a pointer inside mString, or NULL if no extension was found.
*/
-char* String8::find_extension(void) const
-{
+static const char* find_extension(const char* str) {
const char* lastSlash;
const char* lastDot;
- const char* const str = mString;
// only look at the filename
lastSlash = strrchr(str, OS_PATH_SEPARATOR);
@@ -517,83 +464,16 @@
return nullptr;
// looks good, ship it
- return const_cast<char*>(lastDot);
+ return lastDot;
}
String8 String8::getPathExtension(void) const
{
- char* ext;
-
- ext = find_extension();
+ auto ext = find_extension(mString);
if (ext != nullptr)
return String8(ext);
else
return String8("");
}
-String8 String8::getBasePath(void) const
-{
- char* ext;
- const char* const str = mString;
-
- ext = find_extension();
- if (ext == nullptr)
- return String8(*this);
- else
- return String8(str, ext - str);
-}
-
-String8& String8::appendPath(const char* name)
-{
- // TODO: The test below will fail for Win32 paths. Fix later or ignore.
- if (name[0] != OS_PATH_SEPARATOR) {
- if (*name == '\0') {
- // nothing to do
- return *this;
- }
-
- size_t len = length();
- if (len == 0) {
- // no existing filename, just use the new one
- setPathName(*this, name);
- return *this;
- }
-
- // make room for oldPath + '/' + newPath
- int newlen = strlen(name);
-
- char* buf = lockBuffer(len+1+newlen);
-
- // insert a '/' if needed
- if (buf[len-1] != OS_PATH_SEPARATOR)
- buf[len++] = OS_PATH_SEPARATOR;
-
- memcpy(buf+len, name, newlen+1);
- len += newlen;
-
- unlockBuffer(len);
-
- return *this;
- } else {
- setPathName(*this, name);
- return *this;
- }
-}
-
-String8& String8::convertToResPath()
-{
-#if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR
- size_t len = length();
- if (len > 0) {
- char * buf = lockBuffer(len);
- for (char * end = buf + len; buf < end; ++buf) {
- if (*buf == OS_PATH_SEPARATOR)
- *buf = RES_PATH_SEPARATOR;
- }
- unlockBuffer(len);
- }
-#endif
- return *this;
-}
-
}; // namespace android
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
index faf49b6..cbce050 100644
--- a/libutils/String8_fuzz.cpp
+++ b/libutils/String8_fuzz.cpp
@@ -34,7 +34,7 @@
str1->bytes();
},
[](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->isEmpty();
+ str1->empty();
},
[](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
str1->length();
@@ -45,6 +45,8 @@
str1->toLower();
},
[](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
+ if (str2->size() == 0) return;
+
str1->removeAll(str2->c_str());
},
[](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
@@ -66,33 +68,6 @@
int start_index = dataProvider->ConsumeIntegralInRange<int>(0, str1->size());
str1->find(str2->c_str(), start_index);
},
-
- // Path handling
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->getBasePath();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->getPathExtension();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->getPathLeaf();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->getPathDir();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->convertToResPath();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- std::shared_ptr<android::String8> path_out_str =
- std::make_shared<android::String8>();
- str1->walkPath(path_out_str.get());
- path_out_str->clear();
- },
- [](FuzzedDataProvider* dataProvider, android::String8* str1,
- android::String8*) -> void {
- str1->appendPath(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
- },
};
void fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend) {
diff --git a/libutils/String8_test.cpp b/libutils/String8_test.cpp
index 1356cd0..6f7882a 100644
--- a/libutils/String8_test.cpp
+++ b/libutils/String8_test.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "String8_test"
-#include <utils/Log.h>
+#include <log/log.h>
#include <utils/String8.h>
#include <utils/String16.h>
@@ -36,7 +36,7 @@
TEST_F(String8Test, Cstr) {
String8 tmp("Hello, world!");
- EXPECT_STREQ(tmp.string(), "Hello, world!");
+ EXPECT_STREQ(tmp.c_str(), "Hello, world!");
}
TEST_F(String8Test, OperatorPlus) {
@@ -45,16 +45,16 @@
// Test adding String8 + const char*
const char* ccsrc2 = "world!";
String8 dst1 = src1 + ccsrc2;
- EXPECT_STREQ(dst1.string(), "Hello, world!");
- EXPECT_STREQ(src1.string(), "Hello, ");
+ EXPECT_STREQ(dst1.c_str(), "Hello, world!");
+ EXPECT_STREQ(src1.c_str(), "Hello, ");
EXPECT_STREQ(ccsrc2, "world!");
// Test adding String8 + String8
String8 ssrc2("world!");
String8 dst2 = src1 + ssrc2;
- EXPECT_STREQ(dst2.string(), "Hello, world!");
- EXPECT_STREQ(src1.string(), "Hello, ");
- EXPECT_STREQ(ssrc2.string(), "world!");
+ EXPECT_STREQ(dst2.c_str(), "Hello, world!");
+ EXPECT_STREQ(src1.c_str(), "Hello, ");
+ EXPECT_STREQ(ssrc2.c_str(), "world!");
}
TEST_F(String8Test, OperatorPlusEquals) {
@@ -63,14 +63,14 @@
// Testing String8 += String8
String8 src2(" is my passport.");
src1 += src2;
- EXPECT_STREQ(src1.string(), "My voice is my passport.");
- EXPECT_STREQ(src2.string(), " is my passport.");
+ EXPECT_STREQ(src1.c_str(), "My voice is my passport.");
+ EXPECT_STREQ(src2.c_str(), " is my passport.");
// Adding const char* to the previous string.
const char* src3 = " Verify me.";
src1 += src3;
- EXPECT_STREQ(src1.string(), "My voice is my passport. Verify me.");
- EXPECT_STREQ(src2.string(), " is my passport.");
+ EXPECT_STREQ(src1.c_str(), "My voice is my passport. Verify me.");
+ EXPECT_STREQ(src2.c_str(), " is my passport.");
EXPECT_STREQ(src3, " Verify me.");
}
@@ -100,17 +100,35 @@
TEST_F(String8Test, ValidUtf16Conversion) {
char16_t tmp[] = u"abcdef";
String8 valid = String8(String16(tmp));
- EXPECT_STREQ(valid, "abcdef");
+ EXPECT_STREQ(valid.c_str(), "abcdef");
}
TEST_F(String8Test, append) {
String8 s;
EXPECT_EQ(OK, s.append("foo"));
- EXPECT_STREQ("foo", s);
+ EXPECT_STREQ("foo", s.c_str());
EXPECT_EQ(OK, s.append("bar"));
- EXPECT_STREQ("foobar", s);
+ EXPECT_STREQ("foobar", s.c_str());
EXPECT_EQ(OK, s.append("baz", 0));
- EXPECT_STREQ("foobar", s);
+ EXPECT_STREQ("foobar", s.c_str());
EXPECT_EQ(NO_MEMORY, s.append("baz", SIZE_MAX));
- EXPECT_STREQ("foobar", s);
+ EXPECT_STREQ("foobar", s.c_str());
+}
+
+TEST_F(String8Test, removeAll) {
+ String8 s("Hello, world!");
+
+ // NULL input should cause an assertion failure and error message in logcat
+ EXPECT_DEATH(s.removeAll(NULL), "");
+
+ // expect to return true and string content should remain unchanged
+ EXPECT_TRUE(s.removeAll(""));
+ EXPECT_STREQ("Hello, world!", s.c_str());
+
+ // expect to return false
+ EXPECT_FALSE(s.removeAll("x"));
+ EXPECT_STREQ("Hello, world!", s.c_str());
+
+ EXPECT_TRUE(s.removeAll("o"));
+ EXPECT_STREQ("Hell, wrld!", s.c_str());
}
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index 9c71141..df3a898 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -30,7 +30,7 @@
#include <cutils/compiler.h>
#include <utils/Timers.h>
-#include <utils/Log.h>
+#include <log/log.h>
namespace android {
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index e756fec..90ea29b 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -34,7 +34,7 @@
#include <sys/prctl.h>
#endif
-#include <utils/Log.h>
+#include <log/log.h>
#if defined(__ANDROID__)
#include <processgroup/processgroup.h>
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index 4cfac57..98082c9 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -21,7 +21,7 @@
#include <time.h>
#include <android-base/macros.h>
-#include <utils/Log.h>
+#include <log/log.h>
static constexpr size_t clock_id_max = 5;
diff --git a/libutils/Tokenizer.cpp b/libutils/Tokenizer.cpp
index c3ec165..aa097ae 100644
--- a/libutils/Tokenizer.cpp
+++ b/libutils/Tokenizer.cpp
@@ -19,7 +19,7 @@
#include <utils/Tokenizer.h>
#include <fcntl.h>
#include <sys/stat.h>
-#include <utils/Log.h>
+#include <log/log.h>
#ifndef DEBUG_TOKENIZER
// Enables debug output for the tokenizer.
@@ -50,15 +50,15 @@
*outTokenizer = nullptr;
int result = OK;
- int fd = ::open(filename.string(), O_RDONLY);
+ int fd = ::open(filename.c_str(), O_RDONLY);
if (fd < 0) {
result = -errno;
- ALOGE("Error opening file '%s': %s", filename.string(), strerror(errno));
+ ALOGE("Error opening file '%s': %s", filename.c_str(), strerror(errno));
} else {
struct stat stat;
if (fstat(fd, &stat)) {
result = -errno;
- ALOGE("Error getting size of file '%s': %s", filename.string(), strerror(errno));
+ ALOGE("Error getting size of file '%s': %s", filename.c_str(), strerror(errno));
} else {
size_t length = size_t(stat.st_size);
@@ -80,7 +80,7 @@
ssize_t nrd = read(fd, buffer, length);
if (nrd < 0) {
result = -errno;
- ALOGE("Error reading file '%s': %s", filename.string(), strerror(errno));
+ ALOGE("Error reading file '%s': %s", filename.c_str(), strerror(errno));
delete[] buffer;
buffer = nullptr;
} else {
@@ -106,7 +106,7 @@
String8 Tokenizer::getLocation() const {
String8 result;
- result.appendFormat("%s:%d", mFilename.string(), mLineNumber);
+ result.appendFormat("%s:%d", mFilename.c_str(), mLineNumber);
return result;
}
diff --git a/libutils/Vector_fuzz.cpp b/libutils/Vector_fuzz.cpp
index 6fd2baf..3cef487 100644
--- a/libutils/Vector_fuzz.cpp
+++ b/libutils/Vector_fuzz.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
#include <fuzzer/FuzzedDataProvider.h>
-#include <utils/Log.h>
+#include <log/log.h>
#include <utils/Vector.h>
#include <functional>
diff --git a/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump b/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
index c89af9e..8881b44 100644
--- a/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
+++ b/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
@@ -704,9 +704,6 @@
"name" : "_ZN7android7RefBaseD2Ev"
},
{
- "name" : "_ZN7android7String810appendPathEPKc"
- },
- {
"name" : "_ZN7android7String810lockBufferEm"
},
{
@@ -725,9 +722,6 @@
"name" : "_ZN7android7String813appendFormatVEPKcSt9__va_list"
},
{
- "name" : "_ZN7android7String816convertToResPathEv"
- },
- {
"name" : "_ZN7android7String85clearEv"
},
{
@@ -1148,15 +1142,6 @@
"name" : "_ZNK7android7String810getPathDirEv"
},
{
- "name" : "_ZNK7android7String811getBasePathEv"
- },
- {
- "name" : "_ZNK7android7String811getPathLeafEv"
- },
- {
- "name" : "_ZNK7android7String814find_extensionEv"
- },
- {
"name" : "_ZNK7android7String816getPathExtensionEv"
},
{
@@ -1166,9 +1151,6 @@
"name" : "_ZNK7android7String86lengthEv"
},
{
- "name" : "_ZNK7android7String88walkPathEPS0_"
- },
- {
"name" : "_ZNK7android8String1610startsWithEPKDs"
},
{
@@ -6812,22 +6794,6 @@
"source_file" : "system/core/libutils/include/utils/RefBase.h"
},
{
- "function_name" : "android::String8::appendPath",
- "linker_set_key" : "_ZN7android7String810appendPathEPKc",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPN7android7String8E"
- },
- {
- "referenced_type" : "_ZTIPKc"
- }
- ],
- "return_type" : "_ZTIRN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String8::lockBuffer",
"linker_set_key" : "_ZN7android7String810lockBufferEm",
"parameters" :
@@ -6928,19 +6894,6 @@
"source_file" : "system/core/libutils/include/utils/String8.h"
},
{
- "function_name" : "android::String8::convertToResPath",
- "linker_set_key" : "_ZN7android7String816convertToResPathEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPN7android7String8E"
- }
- ],
- "return_type" : "_ZTIRN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String8::clear",
"linker_set_key" : "_ZN7android7String85clearEv",
"parameters" :
@@ -9120,6 +9073,7 @@
"source_file" : "system/core/libutils/include/utils/RefBase.h"
},
{
+ "access" : "private",
"function_name" : "android::String8::getPathDir",
"linker_set_key" : "_ZNK7android7String810getPathDirEv",
"parameters" :
@@ -9133,46 +9087,7 @@
"source_file" : "system/core/libutils/include/utils/String8.h"
},
{
- "function_name" : "android::String8::getBasePath",
- "linker_set_key" : "_ZNK7android7String811getBasePathEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
- "function_name" : "android::String8::getPathLeaf",
- "linker_set_key" : "_ZNK7android7String811getPathLeafEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"access" : "private",
- "function_name" : "android::String8::find_extension",
- "linker_set_key" : "_ZNK7android7String814find_extensionEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIPc",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String8::getPathExtension",
"linker_set_key" : "_ZNK7android7String816getPathExtensionEv",
"parameters" :
@@ -9219,23 +9134,6 @@
"source_file" : "system/core/libutils/include/utils/String8.h"
},
{
- "function_name" : "android::String8::walkPath",
- "linker_set_key" : "_ZNK7android7String88walkPathEPS0_",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- },
- {
- "default_arg" : true,
- "referenced_type" : "_ZTIPN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String16::startsWith",
"linker_set_key" : "_ZNK7android8String1610startsWithEPKDs",
"parameters" :
diff --git a/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump b/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
index f88da15..e8236ea 100644
--- a/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
+++ b/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
@@ -704,9 +704,6 @@
"name" : "_ZN7android7RefBaseD2Ev"
},
{
- "name" : "_ZN7android7String810appendPathEPKc"
- },
- {
"name" : "_ZN7android7String810lockBufferEj"
},
{
@@ -725,9 +722,6 @@
"name" : "_ZN7android7String813appendFormatVEPKcSt9__va_list"
},
{
- "name" : "_ZN7android7String816convertToResPathEv"
- },
- {
"name" : "_ZN7android7String85clearEv"
},
{
@@ -1148,15 +1142,6 @@
"name" : "_ZNK7android7String810getPathDirEv"
},
{
- "name" : "_ZNK7android7String811getBasePathEv"
- },
- {
- "name" : "_ZNK7android7String811getPathLeafEv"
- },
- {
- "name" : "_ZNK7android7String814find_extensionEv"
- },
- {
"name" : "_ZNK7android7String816getPathExtensionEv"
},
{
@@ -1166,9 +1151,6 @@
"name" : "_ZNK7android7String86lengthEv"
},
{
- "name" : "_ZNK7android7String88walkPathEPS0_"
- },
- {
"name" : "_ZNK7android8String1610startsWithEPKDs"
},
{
@@ -6808,22 +6790,6 @@
"source_file" : "system/core/libutils/include/utils/RefBase.h"
},
{
- "function_name" : "android::String8::appendPath",
- "linker_set_key" : "_ZN7android7String810appendPathEPKc",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPN7android7String8E"
- },
- {
- "referenced_type" : "_ZTIPKc"
- }
- ],
- "return_type" : "_ZTIRN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String8::lockBuffer",
"linker_set_key" : "_ZN7android7String810lockBufferEj",
"parameters" :
@@ -6924,19 +6890,6 @@
"source_file" : "system/core/libutils/include/utils/String8.h"
},
{
- "function_name" : "android::String8::convertToResPath",
- "linker_set_key" : "_ZN7android7String816convertToResPathEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPN7android7String8E"
- }
- ],
- "return_type" : "_ZTIRN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String8::clear",
"linker_set_key" : "_ZN7android7String85clearEv",
"parameters" :
@@ -9116,6 +9069,7 @@
"source_file" : "system/core/libutils/include/utils/RefBase.h"
},
{
+ "access" : "private",
"function_name" : "android::String8::getPathDir",
"linker_set_key" : "_ZNK7android7String810getPathDirEv",
"parameters" :
@@ -9129,46 +9083,7 @@
"source_file" : "system/core/libutils/include/utils/String8.h"
},
{
- "function_name" : "android::String8::getBasePath",
- "linker_set_key" : "_ZNK7android7String811getBasePathEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
- "function_name" : "android::String8::getPathLeaf",
- "linker_set_key" : "_ZNK7android7String811getPathLeafEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"access" : "private",
- "function_name" : "android::String8::find_extension",
- "linker_set_key" : "_ZNK7android7String814find_extensionEv",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- }
- ],
- "return_type" : "_ZTIPc",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String8::getPathExtension",
"linker_set_key" : "_ZNK7android7String816getPathExtensionEv",
"parameters" :
@@ -9215,23 +9130,6 @@
"source_file" : "system/core/libutils/include/utils/String8.h"
},
{
- "function_name" : "android::String8::walkPath",
- "linker_set_key" : "_ZNK7android7String88walkPathEPS0_",
- "parameters" :
- [
- {
- "is_this_ptr" : true,
- "referenced_type" : "_ZTIPKN7android7String8E"
- },
- {
- "default_arg" : true,
- "referenced_type" : "_ZTIPN7android7String8E"
- }
- ],
- "return_type" : "_ZTIN7android7String8E",
- "source_file" : "system/core/libutils/include/utils/String8.h"
- },
- {
"function_name" : "android::String16::startsWith",
"linker_set_key" : "_ZNK7android8String1610startsWithEPKDs",
"parameters" :
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index 3ef56a3..c713576 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -24,6 +24,11 @@
#include <utils/String8.h>
#include <utils/TypeHelpers.h>
+#if __has_include(<string_view>)
+#include <string_view>
+#define HAS_STRING_VIEW
+#endif
+
// ---------------------------------------------------------------------------
namespace android {
@@ -53,12 +58,13 @@
~String16();
- inline const char16_t* string() const;
+ inline const char16_t* c_str() const;
-private:
- static inline std::string std_string(const String16& str);
-public:
size_t size() const;
+ inline bool empty() const;
+
+ inline size_t length() const;
+
void setTo(const String16& other);
status_t setTo(const char16_t* other);
status_t setTo(const char16_t* other, size_t len);
@@ -86,6 +92,7 @@
bool startsWith(const char16_t* prefix) const;
bool contains(const char16_t* chrs) const;
+ inline bool contains(const String16& other) const;
status_t replaceAll(char16_t replaceThis,
char16_t withThis);
@@ -108,6 +115,12 @@
inline operator const char16_t*() const;
+#ifdef HAS_STRING_VIEW
+ // Implicit cast to std::u16string is not implemented on purpose - u16string_view is much
+ // lighter and if one needs, they can still create u16string from u16string_view.
+ inline operator std::u16string_view() const;
+#endif
+
// Static and non-static String16 behave the same for the users, so
// this method isn't of much use for the users. It is public for testing.
bool isStaticString() const;
@@ -174,6 +187,14 @@
template <size_t N>
explicit constexpr String16(const StaticData<N>& s) : mString(s.data) {}
+
+// These symbols are for potential backward compatibility with prebuilts. To be removed.
+#ifdef ENABLE_STRING16_OBSOLETE_METHODS
+public:
+#else
+private:
+#endif
+ inline const char16_t* string() const;
};
// String16 can be trivially moved using memcpy() because moving does not
@@ -234,14 +255,29 @@
return compare_type(lhs, rhs) < 0;
}
+inline const char16_t* String16::c_str() const
+{
+ return mString;
+}
+
inline const char16_t* String16::string() const
{
return mString;
}
-inline std::string String16::std_string(const String16& str)
+inline bool String16::empty() const
{
- return std::string(String8(str).string());
+ return length() == 0;
+}
+
+inline size_t String16::length() const
+{
+ return size();
+}
+
+inline bool String16::contains(const String16& other) const
+{
+ return contains(other.c_str());
}
inline String16& String16::operator=(const String16& other)
@@ -333,8 +369,15 @@
return mString;
}
+inline String16::operator std::u16string_view() const
+{
+ return {mString, length()};
+}
+
} // namespace android
// ---------------------------------------------------------------------------
+#undef HAS_STRING_VIEW
+
#endif // ANDROID_STRING16_H
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index 8b2dcf9..6d25072 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -18,7 +18,6 @@
#define ANDROID_STRING8_H
#include <iostream>
-#include <string>
#include <utils/Errors.h>
#include <utils/Unicode.h>
@@ -27,6 +26,16 @@
#include <string.h> // for strcmp
#include <stdarg.h>
+#if __has_include(<string>)
+#include <string>
+#define HAS_STRING
+#endif
+
+#if __has_include(<string_view>)
+#include <string_view>
+#define HAS_STRING_VIEW
+#endif
+
// ---------------------------------------------------------------------------
namespace android {
@@ -52,21 +61,14 @@
explicit String8(const char32_t* o, size_t numChars);
~String8();
- static inline const String8 empty();
-
static String8 format(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
static String8 formatV(const char* fmt, va_list args);
inline const char* c_str() const;
- inline const char* string() const;
-
-private:
- static inline std::string std_string(const String8& str);
-public:
inline size_t size() const;
inline size_t bytes() const;
- inline bool isEmpty() const;
+ inline bool empty() const;
size_t length() const;
@@ -114,6 +116,10 @@
inline operator const char*() const;
+#ifdef HAS_STRING_VIEW
+ inline explicit operator std::string_view() const;
+#endif
+
char* lockBuffer(size_t size);
void unlockBuffer();
status_t unlockBuffer(size_t size);
@@ -121,101 +127,35 @@
// return the index of the first byte of other in this at or after
// start, or -1 if not found
ssize_t find(const char* other, size_t start = 0) const;
+ inline ssize_t find(const String8& other, size_t start = 0) const;
// return true if this string contains the specified substring
inline bool contains(const char* other) const;
+ inline bool contains(const String8& other) const;
// removes all occurrence of the specified substring
// returns true if any were found and removed
bool removeAll(const char* other);
+ inline bool removeAll(const String8& other);
void toLower();
-
- /*
- * These methods operate on the string as if it were a path name.
- */
-
- /*
- * Get just the filename component.
- *
- * "/tmp/foo/bar.c" --> "bar.c"
- */
- String8 getPathLeaf(void) const;
-
- /*
- * Remove the last (file name) component, leaving just the directory
- * name.
- *
- * "/tmp/foo/bar.c" --> "/tmp/foo"
- * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX
- * "bar.c" --> ""
- */
- String8 getPathDir(void) const;
-
- /*
- * Retrieve the front (root dir) component. Optionally also return the
- * remaining components.
- *
- * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c")
- * "/tmp" --> "tmp" (remain = "")
- * "bar.c" --> "bar.c" (remain = "")
- */
- String8 walkPath(String8* outRemains = nullptr) const;
-
- /*
- * Return the filename extension. This is the last '.' and any number
- * of characters that follow it. The '.' is included in case we
- * decide to expand our definition of what constitutes an extension.
- *
- * "/tmp/foo/bar.c" --> ".c"
- * "/tmp" --> ""
- * "/tmp/foo.bar/baz" --> ""
- * "foo.jpeg" --> ".jpeg"
- * "foo." --> ""
- */
- String8 getPathExtension(void) const;
-
- /*
- * Return the path without the extension. Rules for what constitutes
- * an extension are described in the comment for getPathExtension().
- *
- * "/tmp/foo/bar.c" --> "/tmp/foo/bar"
- */
- String8 getBasePath(void) const;
-
- /*
- * Add a component to the pathname. We guarantee that there is
- * exactly one path separator between the old path and the new.
- * If there is no existing name, we just copy the new name in.
- *
- * If leaf is a fully qualified path (i.e. starts with '/', it
- * replaces whatever was there before.
- */
- String8& appendPath(const char* leaf);
- String8& appendPath(const String8& leaf) { return appendPath(leaf.string()); }
-
- /*
- * Like appendPath(), but does not affect this string. Returns a new one instead.
- */
- String8 appendPathCopy(const char* leaf) const
- { String8 p(*this); p.appendPath(leaf); return p; }
- String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.string()); }
-
- /*
- * Converts all separators in this string to /, the default path separator.
- *
- * If the default OS separator is backslash, this converts all
- * backslashes to slashes, in-place. Otherwise it does nothing.
- * Returns self.
- */
- String8& convertToResPath();
-
private:
+ String8 getPathDir(void) const;
+ String8 getPathExtension(void) const;
+
status_t real_append(const char* other, size_t numChars);
- char* find_extension(void) const;
const char* mString;
+
+// These symbols are for potential backward compatibility with prebuilts. To be removed.
+#ifdef ENABLE_STRING8_OBSOLETE_METHODS
+public:
+#else
+private:
+#endif
+ inline const char* string() const;
+ inline bool isEmpty() const;
};
// String8 can be trivially moved using memcpy() because moving does not
@@ -240,10 +180,6 @@
return compare_type(lhs, rhs) < 0;
}
-inline const String8 String8::empty() {
- return String8();
-}
-
inline const char* String8::c_str() const
{
return mString;
@@ -253,16 +189,16 @@
return mString;
}
-inline std::string String8::std_string(const String8& str)
-{
- return std::string(str.string());
-}
-
inline size_t String8::size() const
{
return length();
}
+inline bool String8::empty() const
+{
+ return length() == 0;
+}
+
inline bool String8::isEmpty() const
{
return length() == 0;
@@ -273,11 +209,26 @@
return length();
}
+inline ssize_t String8::find(const String8& other, size_t start) const
+{
+ return find(other.c_str(), start);
+}
+
inline bool String8::contains(const char* other) const
{
return find(other) >= 0;
}
+inline bool String8::contains(const String8& other) const
+{
+ return contains(other.c_str());
+}
+
+inline bool String8::removeAll(const String8& other)
+{
+ return removeAll(other.c_str());
+}
+
inline String8& String8::operator=(const String8& other)
{
setTo(other);
@@ -386,8 +337,18 @@
return mString;
}
+#ifdef HAS_STRING_VIEW
+inline String8::operator std::string_view() const
+{
+ return {mString, length()};
+}
+#endif
+
} // namespace android
// ---------------------------------------------------------------------------
+#undef HAS_STRING
+#undef HAS_STRING_VIEW
+
#endif // ANDROID_STRING8_H
diff --git a/libutils/misc.cpp b/libutils/misc.cpp
index f77e189..e1b5f01 100644
--- a/libutils/misc.cpp
+++ b/libutils/misc.cpp
@@ -20,7 +20,7 @@
#include <pthread.h>
-#include <utils/Log.h>
+#include <log/log.h>
#include <utils/Vector.h>
#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
diff --git a/rootdir/Android.bp b/rootdir/Android.bp
index e98733a..c8a3cd6 100644
--- a/rootdir/Android.bp
+++ b/rootdir/Android.bp
@@ -17,12 +17,30 @@
}
prebuilt_etc {
+ name: "init.boringssl.zygote64_32.rc",
+ src: "init.boringssl.zygote64_32.rc",
+ sub_dir: "init/hw",
+ symlinks: [
+ "init.boringssl.zygote32.rc",
+ "init.boringssl.no_zygote.rc",
+ ],
+}
+
+prebuilt_etc {
+ name: "init.boringssl.zygote64.rc",
+ src: "init.boringssl.zygote64.rc",
+ sub_dir: "init/hw",
+}
+
+prebuilt_etc {
name: "init.rc",
src: "init.rc",
sub_dir: "init/hw",
required: [
"fsverity_init",
"platform-bootclasspath",
+ "init.boringssl.zygote64.rc",
+ "init.boringssl.zygote64_32.rc",
],
}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 3362872..cc6b64a 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -91,27 +91,38 @@
#
# create some directories (some are mount points) and symlinks
LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
- dev proc sys system data data_mirror odm oem acct config storage mnt apex debug_ramdisk \
+ dev proc sys system data data_mirror odm oem acct config storage mnt apex bootstrap-apex debug_ramdisk \
linkerconfig second_stage_resources postinstall $(BOARD_ROOT_EXTRA_FOLDERS)); \
ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
ln -sfn /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
ln -sf /storage/self/primary $(TARGET_ROOT_OUT)/sdcard
+
+ALL_ROOTDIR_SYMLINKS := \
+ $(TARGET_ROOT_OUT)/bin \
+ $(TARGET_ROOT_OUT)/etc \
+ $(TARGET_ROOT_OUT)/bugreports \
+ $(TARGET_ROOT_OUT)/d \
+ $(TARGET_ROOT_OUT)/sdcard
+
ifdef BOARD_USES_VENDORIMAGE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor
else
LOCAL_POST_INSTALL_CMD += ; ln -sf /system/vendor $(TARGET_ROOT_OUT)/vendor
+ ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/vendor
endif
ifdef BOARD_USES_PRODUCTIMAGE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/product
else
LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product $(TARGET_ROOT_OUT)/product
+ ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/product
endif
ifdef BOARD_USES_SYSTEM_EXTIMAGE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_ext
else
LOCAL_POST_INSTALL_CMD += ; ln -sf /system/system_ext $(TARGET_ROOT_OUT)/system_ext
+ ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/system_ext
endif
ifdef BOARD_USES_METADATA_PARTITION
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/metadata
@@ -134,6 +145,18 @@
LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/priv-app $(TARGET_ROOT_OUT)/odm/priv-app
LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/usr $(TARGET_ROOT_OUT)/odm/usr
+ALL_ROOTDIR_SYMLINKS += \
+ $(TARGET_ROOT_OUT)/odm/app \
+ $(TARGET_ROOT_OUT)/odm/bin \
+ $(TARGET_ROOT_OUT)/odm/etc \
+ $(TARGET_ROOT_OUT)/odm/firmware \
+ $(TARGET_ROOT_OUT)/odm/framework \
+ $(TARGET_ROOT_OUT)/odm/lib \
+ $(TARGET_ROOT_OUT)/odm/lib64 \
+ $(TARGET_ROOT_OUT)/odm/overlay \
+ $(TARGET_ROOT_OUT)/odm/priv-app \
+ $(TARGET_ROOT_OUT)/odm/usr
+
# For /vendor_dlkm partition.
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor_dlkm
@@ -144,6 +167,7 @@
# Note that /vendor_dlkm/lib is omitted because vendor DLKMs should be accessed
# via /vendor/lib/modules directly.
LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/vendor_dlkm/etc $(TARGET_ROOT_OUT)/vendor_dlkm/etc
+ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/vendor_dlkm/etc
# For /odm_dlkm partition.
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm_dlkm
@@ -154,6 +178,7 @@
# Note that /odm_dlkm/lib is omitted because odm DLKMs should be accessed
# via /odm/lib/modules directly.
LOCAL_POST_INSTALL_CMD += ; ln -sf /odm/odm_dlkm/etc $(TARGET_ROOT_OUT)/odm_dlkm/etc
+ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/odm_dlkm/etc
# For /system_dlkm partition
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_dlkm
@@ -162,6 +187,7 @@
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
else
LOCAL_POST_INSTALL_CMD += ; ln -sf /data/cache $(TARGET_ROOT_OUT)/cache
+ ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/cache
endif
ifdef BOARD_ROOT_EXTRA_SYMLINKS
# BOARD_ROOT_EXTRA_SYMLINKS is a list of <target>:<link_name>.
@@ -169,14 +195,19 @@
$(eval p := $(subst :,$(space),$(s)))\
; mkdir -p $(dir $(TARGET_ROOT_OUT)/$(word 2,$(p))) \
; ln -sf $(word 1,$(p)) $(TARGET_ROOT_OUT)/$(word 2,$(p)))
+ ALL_ROOTDIR_SYMLINKS += $(foreach s,$(BOARD_ROOT_EXTRA_SYMLINKS),$(TARGET_ROOT_OUT)/$(call word-colon,2,$s))
endif
# The init symlink must be a post install command of a file that is to TARGET_ROOT_OUT.
# Since init.environ.rc is required for init and satisfies that requirement, we hijack it to create the symlink.
LOCAL_POST_INSTALL_CMD += ; ln -sf /system/bin/init $(TARGET_ROOT_OUT)/init
+ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/init
+
+ALL_DEFAULT_INSTALLED_MODULES += $(ALL_ROOTDIR_SYMLINKS)
include $(BUILD_SYSTEM)/base_rules.mk
+$(ALL_ROOTDIR_SYMLINKS): $(LOCAL_BUILT_MODULE)
$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in
@echo "Generate: $< -> $@"
@mkdir -p $(dir $@)
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
index 47f77b1..d72ac66 100644
--- a/rootdir/etc/linker.config.json
+++ b/rootdir/etc/linker.config.json
@@ -11,9 +11,6 @@
"libsigchain.so",
// TODO(b/122876336): Remove libpac.so once it's migrated to Webview
"libpac.so",
- // TODO(b/184872979): Remove libbinder_rpc_unstable.so once stablized and
- // added to libbinder_ndk.
- "libbinder_rpc_unstable.so",
// TODO(b/120786417 or b/134659294): libicuuc.so
// and libicui18n.so are kept for app compat.
"libicui18n.so",
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index cacc47c..9be2ef6 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -1,4 +1,4 @@
-# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
+# See https://android.googlesource.com/platform/ndk/+/main/docs/PlatformApis.md
libandroid.so
libaaudio.so
libamidi.so
diff --git a/rootdir/etc/public.libraries.iot.txt b/rootdir/etc/public.libraries.iot.txt
index 77f8bb8..0513968 100644
--- a/rootdir/etc/public.libraries.iot.txt
+++ b/rootdir/etc/public.libraries.iot.txt
@@ -1,4 +1,4 @@
-# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
+# See https://android.googlesource.com/platform/ndk/+/main/docs/PlatformApis.md
libandroid.so
libandroidthings.so
libaaudio.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index ea1e234..5915dcb 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -1,4 +1,4 @@
-# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
+# See https://android.googlesource.com/platform/ndk/+/main/docs/PlatformApis.md
libandroid.so
libaaudio.so
libamidi.so
diff --git a/rootdir/init.boringssl.zygote64.rc b/rootdir/init.boringssl.zygote64.rc
new file mode 100644
index 0000000..3f49fea
--- /dev/null
+++ b/rootdir/init.boringssl.zygote64.rc
@@ -0,0 +1,4 @@
+on init && property:ro.product.cpu.abilist64=*
+ exec_start boringssl_self_test64
+on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
+ exec_start boringssl_self_test_apex64
diff --git a/rootdir/init.boringssl.zygote64_32.rc b/rootdir/init.boringssl.zygote64_32.rc
new file mode 100644
index 0000000..c0be42d
--- /dev/null
+++ b/rootdir/init.boringssl.zygote64_32.rc
@@ -0,0 +1,8 @@
+on init && property:ro.product.cpu.abilist32=*
+ exec_start boringssl_self_test32
+on init && property:ro.product.cpu.abilist64=*
+ exec_start boringssl_self_test64
+on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
+ exec_start boringssl_self_test_apex32
+on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
+ exec_start boringssl_self_test_apex64
diff --git a/rootdir/init.rc b/rootdir/init.rc
index cdf67b9..317f809 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -74,9 +74,7 @@
# become available. Note that this is executed as exec_start to ensure that
# the libraries are available to the processes started after this statement.
exec_start apexd-bootstrap
-
- # Generate linker config based on apex mounted in bootstrap namespace
- update_linker_config
+ perform_apex_config --bootstrap
# These must already exist by the time boringssl_self_test32 / boringssl_self_test64 run.
mkdir /dev/boringssl 0755 root root
@@ -221,6 +219,26 @@
write /dev/stune/nnapi-hal/schedtune.boost 1
write /dev/stune/nnapi-hal/schedtune.prefer_idle 1
+ # Create blkio group and apply initial settings.
+ # This feature needs kernel to support it, and the
+ # device's init.rc must actually set the correct values.
+ mkdir /dev/blkio/background
+ chown system system /dev/blkio
+ 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
+ write /dev/blkio/blkio.group_idle 0
+ write /dev/blkio/background/blkio.group_idle 0
+
restorecon_recursive /mnt
mount configfs none /config nodev noexec nosuid
@@ -461,14 +479,7 @@
start vndservicemanager
# Run boringssl self test for each ABI. Any failures trigger reboot to firmware.
-on init && property:ro.product.cpu.abilist32=*
- exec_start boringssl_self_test32
-on init && property:ro.product.cpu.abilist64=*
- exec_start boringssl_self_test64
-on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
- exec_start boringssl_self_test_apex32
-on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
- exec_start boringssl_self_test_apex64
+import /system/etc/init/hw/init.boringssl.${ro.zygote}.rc
service boringssl_self_test32 /system/bin/boringssl_self_test32
reboot_on_failure reboot,boringssl-self-check-failed
@@ -604,8 +615,8 @@
chmod 0700 /metadata/vold
mkdir /metadata/password_slots 0771 root system
mkdir /metadata/bootstat 0750 system log
- mkdir /metadata/ota 0700 root system
- mkdir /metadata/ota/snapshots 0700 root system
+ mkdir /metadata/ota 0750 root system
+ mkdir /metadata/ota/snapshots 0750 root system
mkdir /metadata/userspacereboot 0770 root system
mkdir /metadata/watchdog 0770 root system
@@ -619,7 +630,6 @@
restorecon_recursive /metadata/apex
mkdir /metadata/staged-install 0770 root system
- mkdir /metadata/sepolicy 0700 root root
on late-fs
# Ensure that tracefs has the correct permissions.
# This does not work correctly if it is called in post-fs.
@@ -997,7 +1007,7 @@
perform_apex_config
# Create directories for boot animation.
- mkdir /data/bootanim 0755 system system encryption=DeleteIfNecessary
+ mkdir /data/misc/bootanim 0755 system system encryption=DeleteIfNecessary
exec_start derive_sdk
@@ -1021,13 +1031,9 @@
# Must start after 'derive_classpath' to have *CLASSPATH variables set.
start odsign
- # Before we can lock keys and proceed to the next boot stage, wait for
- # odsign to be done with the key
+ # Wait for odsign to be done with the key.
wait_for_prop odsign.key.done 1
- # Lock the fs-verity keyring, so no more keys can be added
- exec -- /system/bin/fsverity_init --lock
-
# Bump the boot level to 1000000000; this prevents further on-device signing.
# This is a special value that shuts down the thread which listens for
# further updates.
@@ -1070,6 +1076,9 @@
write /proc/sys/vm/dirty_expire_centisecs 200
write /proc/sys/vm/dirty_background_ratio 5
+on boot && property:suspend.disable_sync_on_suspend=true
+ write /sys/power/sync_on_suspend 0
+
on boot
# basic network init
ifup lo
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 2f0ec8a..442bd15 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -13,7 +13,7 @@
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
- onrestart restart media.tuner
+ onrestart restart --only-if-running media.tuner
onrestart restart netd
onrestart restart wificond
task_profiles ProcessCapacityHigh
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 74a64c8..3422121 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -13,7 +13,7 @@
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
- onrestart restart media.tuner
+ onrestart restart --only-if-running media.tuner
onrestart restart netd
onrestart restart wificond
task_profiles ProcessCapacityHigh MaxPerformance
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 0b7ffb8..60dcc2a 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -23,6 +23,11 @@
subsystem dma_heap
devname uevent_devpath
dirname /dev/dma_heap
+
+subsystem vfio
+ devname uevent_devpath
+ dirname /dev/vfio
+
# ueventd can only set permissions on device nodes and their associated
# sysfs attributes, not on arbitrary paths.
#
@@ -43,6 +48,7 @@
/dev/binder 0666 root root
/dev/hwbinder 0666 root root
/dev/vndbinder 0666 root root
+/dev/vfio/* 0666 root root
/dev/pmsg0 0222 root log
/dev/dma_heap/system 0444 system system
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
index e2c77c3..f65bb20 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -111,8 +111,7 @@
break;
bits_size = res + 16;
bits = realloc(bits, bits_size * 2);
- if(bits == NULL)
- err(1, "failed to allocate buffer of size %d\n", (int)bits_size);
+ if (bits == NULL) err(1, "failed to allocate buffer of size %zd", bits_size);
}
res2 = 0;
switch(i) {
diff --git a/trusty/OWNERS b/trusty/OWNERS
index 61b97c6..bf16912 100644
--- a/trusty/OWNERS
+++ b/trusty/OWNERS
@@ -3,6 +3,7 @@
danielangell@google.com
gmar@google.com
marcone@google.com
+mikemcternan@google.com
mmaurer@google.com
ncbray@google.com
swillden@google.com
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index 17d083c..f782d2a 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -19,6 +19,7 @@
#include <BufferAllocator/BufferAllocator.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
+#include <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdbool.h>
diff --git a/trusty/fuzz/tipc_fuzzer.cpp b/trusty/fuzz/tipc_fuzzer.cpp
index f265ced..edc2a79 100644
--- a/trusty/fuzz/tipc_fuzzer.cpp
+++ b/trusty/fuzz/tipc_fuzzer.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <android-base/result.h>
+#include <fuzzer/FuzzedDataProvider.h>
#include <stdlib.h>
#include <trusty/coverage/coverage.h>
#include <trusty/coverage/uuid.h>
@@ -23,6 +25,7 @@
#include <iostream>
#include <memory>
+using android::base::Result;
using android::trusty::coverage::CoverageRecord;
using android::trusty::fuzz::ExtraCounters;
using android::trusty::fuzz::TrustyApp;
@@ -41,7 +44,14 @@
#error "Binary file name must be parameterized using -DTRUSTY_APP_FILENAME."
#endif
-static TrustyApp kTrustyApp(TIPC_DEV, TRUSTY_APP_PORT);
+#ifdef TRUSTY_APP_MAX_CONNECTIONS
+constexpr size_t MAX_CONNECTIONS = TRUSTY_APP_MAX_CONNECTIONS;
+#else
+constexpr size_t MAX_CONNECTIONS = 1;
+#endif
+
+static_assert(MAX_CONNECTIONS >= 1);
+
static std::unique_ptr<CoverageRecord> record;
extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
@@ -53,7 +63,8 @@
}
/* Make sure lazy-loaded TAs have started and connected to coverage service. */
- auto ret = kTrustyApp.Connect();
+ TrustyApp ta(TIPC_DEV, TRUSTY_APP_PORT);
+ auto ret = ta.Connect();
if (!ret.ok()) {
std::cerr << ret.error() << std::endl;
exit(-1);
@@ -73,24 +84,56 @@
return 0;
}
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- static uint8_t buf[TIPC_MAX_MSG_SIZE];
+Result<void> testOneInput(FuzzedDataProvider& provider) {
+ std::vector<TrustyApp> trustyApps;
+ while (provider.remaining_bytes() > 0) {
+ if (trustyApps.size() < MAX_CONNECTIONS && provider.ConsumeBool()) {
+ auto& ta = trustyApps.emplace_back(TIPC_DEV, TRUSTY_APP_PORT);
+ const auto result = ta.Connect();
+ if (!result.ok()) {
+ return result;
+ }
+ } else {
+ const auto i = provider.ConsumeIntegralInRange<size_t>(0, trustyApps.size());
+ std::swap(trustyApps[i], trustyApps.back());
+
+ if (provider.ConsumeBool()) {
+ auto& ta = trustyApps.back();
+
+ const auto data = provider.ConsumeRandomLengthString();
+ auto result = ta.Write(data.data(), data.size());
+ if (!result.ok()) {
+ return result;
+ }
+
+ std::array<uint8_t, TIPC_MAX_MSG_SIZE> buf;
+ result = ta.Read(buf.data(), buf.size());
+ if (!result.ok()) {
+ return result;
+ }
+
+ // Reconnect to ensure that the service is still up.
+ ta.Disconnect();
+ result = ta.Connect();
+ if (!result.ok()) {
+ std::cerr << result.error() << std::endl;
+ android::trusty::fuzz::Abort();
+ return result;
+ }
+ } else {
+ trustyApps.pop_back();
+ }
+ }
+ }
+ return {};
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
ExtraCounters counters(record.get());
counters.Reset();
- auto ret = kTrustyApp.Write(data, size);
- if (ret.ok()) {
- ret = kTrustyApp.Read(&buf, sizeof(buf));
- }
-
- // Reconnect to ensure that the service is still up
- kTrustyApp.Disconnect();
- ret = kTrustyApp.Connect();
- if (!ret.ok()) {
- std::cerr << ret.error() << std::endl;
- android::trusty::fuzz::Abort();
- }
-
- return ret.ok() ? 0 : -1;
+ FuzzedDataProvider provider(data, size);
+ const auto result = testOneInput(provider);
+ return result.ok() ? 0 : -1;
}
diff --git a/trusty/keymaster/TEST_MAPPING b/trusty/keymaster/TEST_MAPPING
index 0475e04..4f082d8 100644
--- a/trusty/keymaster/TEST_MAPPING
+++ b/trusty/keymaster/TEST_MAPPING
@@ -10,13 +10,15 @@
"name": "RkpdAppUnitTests"
},
{
- "name": "RkpdAppGoogleUnitTests"
+ "name": "RkpdAppGoogleUnitTests",
+ "keywords": ["internal"]
},
{
"name": "RkpdAppIntegrationTests"
},
{
- "name": "RkpdAppGoogleIntegrationTests"
+ "name": "RkpdAppGoogleIntegrationTests",
+ "keywords": ["internal"]
}
]
}
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index ac98695..b118a20 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -218,6 +218,11 @@
ForwardCommand(KM_DELETE_ALL_KEYS, request, response);
}
+void TrustyKeymaster::DestroyAttestationIds(const DestroyAttestationIdsRequest& request,
+ DestroyAttestationIdsResponse* response) {
+ ForwardCommand(KM_DESTROY_ATTESTATION_IDS, request, response);
+}
+
void TrustyKeymaster::BeginOperation(const BeginOperationRequest& request,
BeginOperationResponse* response) {
ForwardCommand(KM_BEGIN_OPERATION, request, response);
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
index 60d3f87..c50178b 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
@@ -55,6 +55,8 @@
void UpgradeKey(const UpgradeKeyRequest& request, UpgradeKeyResponse* response);
void DeleteKey(const DeleteKeyRequest& request, DeleteKeyResponse* response);
void DeleteAllKeys(const DeleteAllKeysRequest& request, DeleteAllKeysResponse* response);
+ void DestroyAttestationIds(const DestroyAttestationIdsRequest& request,
+ DestroyAttestationIdsResponse* response);
void BeginOperation(const BeginOperationRequest& request, BeginOperationResponse* response);
void UpdateOperation(const UpdateOperationRequest& request, UpdateOperationResponse* response);
void FinishOperation(const FinishOperationRequest& request, FinishOperationResponse* response);
diff --git a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
index b696ff9..fec4c60 100644
--- a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
+++ b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
@@ -258,7 +258,11 @@
}
ScopedAStatus TrustyKeyMintDevice::destroyAttestationIds() {
- return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+ keymaster::DestroyAttestationIdsRequest request(impl_->message_version());
+ keymaster::DestroyAttestationIdsResponse response(impl_->message_version());
+ impl_->DestroyAttestationIds(request, &response);
+
+ return kmError2ScopedAStatus(response.error);
}
ScopedAStatus TrustyKeyMintDevice::begin(KeyPurpose purpose, const vector<uint8_t>& keyBlob,
diff --git a/trusty/keymint/Android.bp b/trusty/keymint/Android.bp
index c19ebbd..19dcc98 100644
--- a/trusty/keymint/Android.bp
+++ b/trusty/keymint/Android.bp
@@ -35,6 +35,7 @@
"liblibc",
"liblog_rust",
],
+ prefer_rlib: true,
required: [
"android.hardware.hardware_keystore.xml",
],
diff --git a/trusty/libtrusty-rs/src/lib.rs b/trusty/libtrusty-rs/src/lib.rs
index 28ea075..22b894a 100644
--- a/trusty/libtrusty-rs/src/lib.rs
+++ b/trusty/libtrusty-rs/src/lib.rs
@@ -102,6 +102,8 @@
let file = File::options().read(true).write(true).open(device)?;
let srv_name = CString::new(service).expect("Service name contained null bytes");
+ // SAFETY: The file descriptor is valid because it came from a `File`, and the name is a
+ // valid C string because it came from a `CString`.
unsafe {
tipc_connect(file.as_raw_fd(), srv_name.as_ptr())?;
}
diff --git a/trusty/line-coverage/Android.bp b/trusty/line-coverage/Android.bp
new file mode 100644
index 0000000..36a73aa
--- /dev/null
+++ b/trusty/line-coverage/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library {
+ name: "libtrusty_line_coverage",
+ vendor_available: true,
+ srcs: [
+ "coverage.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ shared_libs: [
+ "libbase",
+ "libext2_uuid",
+ "liblog",
+ "libdmabufheap",
+ "libtrusty",
+ ],
+}
+
diff --git a/trusty/line-coverage/coverage.cpp b/trusty/line-coverage/coverage.cpp
new file mode 100644
index 0000000..57b7025
--- /dev/null
+++ b/trusty/line-coverage/coverage.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2023 The Android Open Sourete 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.
+ */
+
+#define LOG_TAG "line-coverage"
+
+#include <BufferAllocator/BufferAllocator.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <assert.h>
+#include <log/log.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+#include <trusty/line-coverage/coverage.h>
+#include <trusty/line-coverage/tipc.h>
+#include <trusty/tipc.h>
+#include <iostream>
+
+#define LINE_COVERAGE_CLIENT_PORT "com.android.trusty.linecoverage.client"
+
+struct control {
+ /* Written by controller, read by instrumented TA */
+ uint64_t cntrl_flags;
+
+ /* Written by instrumented TA, read by controller */
+ uint64_t oper_flags;
+ uint64_t write_buffer_start_count;
+ uint64_t write_buffer_complete_count;
+};
+
+namespace android {
+namespace trusty {
+namespace line_coverage {
+
+using ::android::base::ErrnoError;
+using ::android::base::Error;
+using ::std::string;
+
+static inline uintptr_t RoundPageUp(uintptr_t val) {
+ return (val + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
+}
+
+CoverageRecord::CoverageRecord(string tipc_dev, struct uuid* uuid)
+ : tipc_dev_(std::move(tipc_dev)),
+ coverage_srv_fd_(-1),
+ uuid_(*uuid),
+ record_len_(0),
+ shm_(NULL),
+ shm_len_(0) {}
+
+CoverageRecord::~CoverageRecord() {
+ if (shm_) {
+ munmap((void*)shm_, shm_len_);
+ }
+}
+
+volatile void *CoverageRecord::getShm() {
+ if(!IsOpen()) {
+ fprintf(stderr, "Warning! SHM is NULL!\n");
+ }
+ return shm_;
+}
+
+Result<void> CoverageRecord::Rpc(struct line_coverage_client_req* req, \
+ int req_fd, \
+ struct line_coverage_client_resp* resp) {
+ int rc;
+
+ if (req_fd < 0) {
+ rc = write(coverage_srv_fd_, req, sizeof(*req));
+ } else {
+ iovec iov = {
+ .iov_base = req,
+ .iov_len = sizeof(*req),
+ };
+
+ trusty_shm shm = {
+ .fd = req_fd,
+ .transfer = TRUSTY_SHARE,
+ };
+
+ rc = tipc_send(coverage_srv_fd_, &iov, 1, &shm, 1);
+ }
+
+ if (rc != (int)sizeof(*req)) {
+ return ErrnoError() << "failed to send request to coverage server: ";
+ }
+
+ rc = read(coverage_srv_fd_, resp, sizeof(*resp));
+ if (rc != (int)sizeof(*resp)) {
+ return ErrnoError() << "failed to read reply from coverage server: ";
+ }
+
+ if (resp->hdr.cmd != (req->hdr.cmd | LINE_COVERAGE_CLIENT_CMD_RESP_BIT)) {
+ return ErrnoError() << "unknown response cmd: " << resp->hdr.cmd;
+ }
+
+ return {};
+}
+
+Result<void> CoverageRecord::Open(int fd) {
+ struct line_coverage_client_req req;
+ struct line_coverage_client_resp resp;
+
+ if (shm_) {
+ return {}; /* already initialized */
+ }
+
+ coverage_srv_fd_= fd;
+
+ req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_OPEN;
+ req.open_args.uuid = uuid_;
+ auto ret = Rpc(&req, -1, &resp);
+ if (!ret.ok()) {
+ return Error() << "failed to open coverage client: " << ret.error();
+ }
+ record_len_ = resp.open_args.record_len;
+ shm_len_ = RoundPageUp(record_len_);
+
+ BufferAllocator allocator;
+
+ fd = allocator.Alloc("system", shm_len_);
+ if (fd < 0) {
+ return ErrnoError() << "failed to create dmabuf of size " << shm_len_
+ << " err code: " << fd;
+ }
+ unique_fd dma_buf(fd);
+
+ void* shm = mmap(0, shm_len_, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
+ if (shm == MAP_FAILED) {
+ return ErrnoError() << "failed to map memfd: ";
+ }
+
+ req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_SHARE_RECORD;
+ req.share_record_args.shm_len = shm_len_;
+ ret = Rpc(&req, dma_buf, &resp);
+ if (!ret.ok()) {
+ return Error() << "failed to send shared memory: " << ret.error();
+ }
+
+ shm_ = shm;
+
+ req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_OPEN;
+ req.open_args.uuid = uuid_;
+ ret = Rpc(&req, -1, &resp);
+ if (!ret.ok()) {
+ return Error() << "failed to open coverage client: " << ret.error();
+ }
+
+ return {};
+}
+
+bool CoverageRecord::IsOpen() {
+ return shm_;
+}
+
+Result<void> CoverageRecord::SaveFile(const std::string& filename) {
+ if(!IsOpen()) {
+ return ErrnoError() << "Warning! SHM is NULL!";
+ }
+ android::base::unique_fd output_fd(TEMP_FAILURE_RETRY(creat(filename.c_str(), 00644)));
+ if (!output_fd.ok()) {
+ return ErrnoError() << "Could not open output file";
+ }
+
+ uintptr_t* begin = (uintptr_t*)((char *)shm_ + sizeof(struct control));
+ bool ret = WriteFully(output_fd, begin, record_len_);
+ if(!ret) {
+ fprintf(stderr, "Coverage write to file failed\n");
+ }
+
+ return {};
+}
+
+} // namespace line_coverage
+} // namespace trusty
+} // namespace android
diff --git a/trusty/line-coverage/include/trusty/line-coverage/coverage.h b/trusty/line-coverage/include/trusty/line-coverage/coverage.h
new file mode 100644
index 0000000..53901be
--- /dev/null
+++ b/trusty/line-coverage/include/trusty/line-coverage/coverage.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2023 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 <optional>
+#include <string>
+
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+#include <stdint.h>
+#include <trusty/line-coverage/tipc.h>
+
+namespace android {
+namespace trusty {
+namespace line_coverage {
+
+using android::base::Result;
+using android::base::unique_fd;
+
+class CoverageRecord {
+ public:
+ CoverageRecord(std::string tipc_dev, struct uuid* uuid);
+
+ ~CoverageRecord();
+ Result<void> Open(int fd);
+ bool IsOpen();
+ Result<void> SaveFile(const std::string& filename);
+ volatile void* getShm();
+
+ private:
+ Result<void> Rpc(struct line_coverage_client_req* req, \
+ int req_fd, \
+ struct line_coverage_client_resp* resp);
+
+ Result<std::pair<size_t, size_t>> GetRegionBounds(uint32_t region_type);
+
+ std::string tipc_dev_;
+ int coverage_srv_fd_;
+ struct uuid uuid_;
+ size_t record_len_;
+ volatile void* shm_;
+ size_t shm_len_;
+};
+
+} // namespace line_coverage
+} // namespace trusty
+} // namespace android
diff --git a/trusty/line-coverage/include/trusty/line-coverage/tipc.h b/trusty/line-coverage/include/trusty/line-coverage/tipc.h
new file mode 100644
index 0000000..20e855c
--- /dev/null
+++ b/trusty/line-coverage/include/trusty/line-coverage/tipc.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+/* This file needs to be kept in-sync with its counterpart on Trusty side */
+
+#pragma once
+
+#include <stdint.h>
+#include <trusty/line-coverage/uuid.h>
+
+
+#define LINE_COVERAGE_CLIENT_PORT "com.android.trusty.linecoverage.client"
+
+/**
+ * enum line_coverage_client_cmd - command identifiers for coverage client interface
+ * @LINE_COVERAGE_CLIENT_CMD_RESP_BIT: response bit set as part of response
+ * @LINE_COVERAGE_CLIENT_CMD_SHIFT: number of bits used by response bit
+ * @LINE_COVERAGE_CLIENT_CMD_OPEN: command to open coverage record
+ * @LINE_COVERAGE_CLIENT_CMD_SHARE_RECORD: command to register a shared memory region
+ * where coverage record will be written to
+ */
+enum line_coverage_client_cmd {
+ LINE_COVERAGE_CLIENT_CMD_RESP_BIT = 1U,
+ LINE_COVERAGE_CLIENT_CMD_SHIFT = 1U,
+ LINE_COVERAGE_CLIENT_CMD_OPEN = (1U << LINE_COVERAGE_CLIENT_CMD_SHIFT),
+ LINE_COVERAGE_CLIENT_CMD_SHARE_RECORD = (2U << LINE_COVERAGE_CLIENT_CMD_SHIFT),
+ LINE_COVERAGE_CLIENT_CMD_SEND_LIST = (3U << LINE_COVERAGE_CLIENT_CMD_SHIFT),
+};
+
+/**
+ * struct line_coverage_client_hdr - header for coverage client messages
+ * @cmd: command identifier
+ *
+ * Note that no messages return a status code. Any error on the server side
+ * results in the connection being closed. So, operations can be assumed to be
+ * successful if they return a response.
+ */
+struct line_coverage_client_hdr {
+ uint32_t cmd;
+};
+
+/**
+ * struct line_coverage_client_open_req - arguments for request to open coverage
+ * record
+ * @uuid: UUID of target TA
+ *
+ * There is one coverage record per TA. @uuid is used to identify both the TA
+ * and corresponding coverage record.
+ */
+struct line_coverage_client_open_req {
+ struct uuid uuid;
+};
+
+/**
+ * struct line_coverage_client_open_resp - arguments for response to open coverage
+ * record
+ * @record_len: length of coverage record that will be emitted by target TA
+ *
+ * Shared memory allocated for this coverage record must larger than
+ * @record_len.
+ */
+struct line_coverage_client_open_resp {
+ uint32_t record_len;
+};
+
+/**
+ * struct line_coverage_client_send_list_resp - arguments for response to send list
+ * record
+ * @uuid: UUID of TA that connected to aggregator
+ */
+struct line_coverage_client_send_list_resp {
+ struct uuid uuid;
+};
+
+/**
+ * struct line_coverage_client_send_list_resp - arguments for response to send list
+ * record
+ * @index: index of the list being requested
+ */
+struct line_coverage_client_send_list_req {
+ uint32_t index;
+};
+
+/**
+ * struct line_coverage_client_share_record_req - arguments for request to share
+ * memory for coverage record
+ * @shm_len: length of memory region being shared
+ *
+ * A handle to a memory region must be sent along with this message. This memory
+ * is used to store coverage record.
+ *
+ * Upon success, this memory region can be assumed to be shared between the
+ * client and target TA.
+ */
+struct line_coverage_client_share_record_req {
+ uint32_t shm_len;
+};
+
+/**
+ * struct line_coverage_client_req - structure for a coverage client request
+ * @hdr: message header
+ * @open_args: arguments for %COVERAGE_CLIENT_CMD_OPEN request
+ * @share_record_args: arguments for %COVERAGE_CLIENT_CMD_SHARE_RECORD request
+ * @index: arguments for %COVERAGE_CLIENT_CMD_SHARE_RECORD request
+ */
+struct line_coverage_client_req {
+ struct line_coverage_client_hdr hdr;
+ union {
+ struct line_coverage_client_open_req open_args;
+ struct line_coverage_client_share_record_req share_record_args;
+ struct line_coverage_client_send_list_req send_list_args;
+ };
+};
+
+/**
+ * struct line_coverage_client_resp - structure for a coverage client response
+ * @hdr: message header
+ * @open_args: arguments for %COVERAGE_CLIENT_CMD_OPEN response
+ * @send_list_args: arguments for %COVERAGE_CLIENT_CMD_SHARE_RECORD response
+ */
+struct line_coverage_client_resp {
+ struct line_coverage_client_hdr hdr;
+ union {
+ struct line_coverage_client_open_resp open_args;
+ struct line_coverage_client_send_list_resp send_list_args;
+ };
+};
diff --git a/trusty/line-coverage/include/trusty/line-coverage/uuid.h b/trusty/line-coverage/include/trusty/line-coverage/uuid.h
new file mode 100644
index 0000000..1873980
--- /dev/null
+++ b/trusty/line-coverage/include/trusty/line-coverage/uuid.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 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 <stdint.h>
+#include <string.h>
+
+#define UUCMP(u1, u2) if (u1 != u2) return u1 < u2
+
+struct uuid {
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_hi_and_version;
+ uint8_t clock_seq_and_node[8];
+
+ bool operator<(const struct uuid& rhs) const
+ {
+ UUCMP(time_low, rhs.time_low);
+ UUCMP(time_mid, rhs.time_mid);
+ UUCMP(time_hi_and_version, rhs.time_hi_and_version);
+ return memcmp(clock_seq_and_node, rhs.clock_seq_and_node, 8);
+ }
+};
diff --git a/trusty/stats/test/README.md b/trusty/stats/test/README.md
index 45e6af8..175409e 100644
--- a/trusty/stats/test/README.md
+++ b/trusty/stats/test/README.md
@@ -1,8 +1,8 @@
# Development Notes
-* First get [repo_pull.py and gerrit.py](https://android.googlesource.com/platform/development/+/master/tools/repo_pull/) from aosp.
+* First get [repo_pull.py and gerrit.py](https://android.googlesource.com/platform/development/+/main/tools/repo_pull/) from aosp.
-* Although this repo is not currently in Trusty’s manifest, it’s sufficient to copy these two python scripts to the root of the Trusty project and run them from there. Make sure to follow the [repo_pull installation](https://android.googlesource.com/platform/development/+/master/tools/repo_pull/#installation) steps if necessary.
+* Although this repo is not currently in Trusty’s manifest, it’s sufficient to copy these two python scripts to the root of the Trusty project and run them from there. Make sure to follow the [repo_pull installation](https://android.googlesource.com/platform/development/+/main/tools/repo_pull/#installation) steps if necessary.
## Build
diff --git a/trusty/stats/test/stats_test.cpp b/trusty/stats/test/stats_test.cpp
index 1edddeb..1d6eb34 100644
--- a/trusty/stats/test/stats_test.cpp
+++ b/trusty/stats/test/stats_test.cpp
@@ -252,20 +252,20 @@
::testing::AnyOf(::testing::Eq(TrustyAtoms::TrustyAppCrashed),
::testing::Eq(TrustyAtoms::TrustyError),
::testing::Eq(TrustyAtoms::TrustyStorageError)));
- ASSERT_STREQ(String8(vendorAtom.reverseDomainName), "google.android.trusty");
+ ASSERT_EQ(String8(vendorAtom.reverseDomainName), "google.android.trusty");
switch (vendorAtom.atomId) {
case TrustyAtoms::TrustyAppCrashed:
++atomAppCrashedCnt;
- ASSERT_STREQ(String8(vendorAtom.values[0].get<VendorAtomValue::stringValue>()),
- "5247d19b-cf09-4272-a450-3ef20dbefc14");
+ ASSERT_EQ(String8(vendorAtom.values[0].get<VendorAtomValue::stringValue>()),
+ "5247d19b-cf09-4272-a450-3ef20dbefc14");
break;
case TrustyAtoms::TrustyStorageError:
++atomStorageErrorCnt;
ASSERT_EQ(vendorAtom.values[0].get<VendorAtomValue::intValue>(), 5);
- ASSERT_STREQ(String8(vendorAtom.values[1].get<VendorAtomValue::stringValue>()),
- "5247d19b-cf09-4272-a450-3ef20dbefc14");
- ASSERT_STREQ(String8(vendorAtom.values[2].get<VendorAtomValue::stringValue>()),
- "5247d19b-cf09-4272-a450-3ef20dbefc14");
+ ASSERT_EQ(String8(vendorAtom.values[1].get<VendorAtomValue::stringValue>()),
+ "5247d19b-cf09-4272-a450-3ef20dbefc14");
+ ASSERT_EQ(String8(vendorAtom.values[2].get<VendorAtomValue::stringValue>()),
+ "5247d19b-cf09-4272-a450-3ef20dbefc14");
ASSERT_EQ(vendorAtom.values[3].get<VendorAtomValue::intValue>(), 1);
ASSERT_EQ(vendorAtom.values[4].get<VendorAtomValue::intValue>(), 3);
ASSERT_EQ(vendorAtom.values[5].get<VendorAtomValue::longValue>(),
@@ -330,13 +330,13 @@
::testing::AnyOf(::testing::Eq(TrustyAtoms::TrustyAppCrashed),
::testing::Eq(TrustyAtoms::TrustyError),
::testing::Eq(TrustyAtoms::TrustyStorageError)));
- ASSERT_STREQ(String8(vendorAtom.reverseDomainName), "google.android.trusty");
+ ASSERT_EQ(String8(vendorAtom.reverseDomainName), "google.android.trusty");
switch (vendorAtom.atomId) {
case TrustyAtoms::TrustyAppCrashed:
++atomAppCrashedCnt;
- ASSERT_STREQ(String8(vendorAtom.values[0].get<VendorAtomValue::stringValue>()),
- kTrustyCrasherUuid);
+ ASSERT_EQ(String8(vendorAtom.values[0].get<VendorAtomValue::stringValue>()),
+ kTrustyCrasherUuid);
atomCrashReasons.push_back(vendorAtom.values[1].get<VendorAtomValue::intValue>());
break;
case TrustyAtoms::TrustyStorageError:
@@ -344,7 +344,7 @@
break;
case TrustyAtoms::TrustyError:
++atomTrustyErrorCnt;
- ASSERT_STREQ(String8(vendorAtom.values[1].get<VendorAtomValue::stringValue>()), "");
+ ASSERT_EQ(String8(vendorAtom.values[1].get<VendorAtomValue::stringValue>()), "");
break;
default:
FAIL() << "Unknown vendor atom ID: " << vendorAtom.atomId;
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index 2e97ee0..e362b8b 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -33,6 +33,7 @@
shared_libs: [
"libbase",
+ "libbinder_ndk",
"libcutils",
"liblog",
"libhardware_legacy",
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index c89c5b6..67e935e 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -24,6 +24,7 @@
#include <sys/stat.h>
#include <unistd.h>
+#include <android/binder_process.h>
#include <cutils/android_filesystem_config.h>
#include "checkpoint_handling.h"
@@ -238,6 +239,18 @@
/* parse arguments */
parse_args(argc, argv);
+ /*
+ * Start binder threadpool. At least one extra binder thread is needed to
+ * connect to the wakelock service without relying on polling. If we poll on
+ * the main thread we end up pausing for at least 1s even if the service
+ * starts faster. We set the max thread count to 0 because startThreadPool
+ * "Starts one thread, PLUS those requested in setThreadPoolMaxThreadCount,
+ * PLUS those manually requested in joinThreadPool." We only need a single
+ * binder thread to receive notifications on.
+ */
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ ABinderProcess_startThreadPool();
+
/* initialize secure storage directory */
rc = storage_init(ss_data_root);
if (rc < 0) return EXIT_FAILURE;
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 22a85a7..1f5d107 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -399,6 +399,14 @@
bool is_request_write = req->reliable_write_size > 0;
+ /*
+ * Internally this call connects to the suspend service, which will cause
+ * this service to start if not already running. If the binder thread pool
+ * has not been started at this point, this call will block and poll for the
+ * service every 1s. We need to make sure the thread pool is started to
+ * receive an async notification that the service is started to avoid
+ * blocking (see main).
+ */
wl_rc = acquire_wake_lock(PARTIAL_WAKE_LOCK, UFS_WAKE_LOCK_NAME);
if (wl_rc < 0) {
ALOGE("%s: failed to acquire wakelock: %d, %s\n", __func__, wl_rc, strerror(errno));
diff --git a/trusty/utils/acvp/trusty_modulewrapper.cpp b/trusty/utils/acvp/trusty_modulewrapper.cpp
index 70ffb52..85b7159 100644
--- a/trusty/utils/acvp/trusty_modulewrapper.cpp
+++ b/trusty/utils/acvp/trusty_modulewrapper.cpp
@@ -21,15 +21,16 @@
#include <android-base/result.h>
#include <android-base/unique_fd.h>
#include <errno.h>
+#include <iostream>
#include <log/log.h>
#include <modulewrapper.h>
#include <openssl/span.h>
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/mman.h>
#include <trusty/tipc.h>
#include <unistd.h>
-#include <iostream>
#include "acvp_ipc.h"
@@ -208,6 +209,11 @@
return {};
}
+static bool EqString(bssl::Span<const uint8_t> cmd, const char *str) {
+ return cmd.size() == strlen(str) &&
+ memcmp(str, cmd.data(), cmd.size()) == 0;
+}
+
int main() {
for (;;) {
auto buffer = bssl::acvp::RequestBuffer::New();
@@ -217,17 +223,24 @@
return EXIT_FAILURE;
}
- ModuleWrapper wrapper;
- auto res = wrapper.SendMessage(args);
- if (!res.ok()) {
- std::cerr << res.error() << std::endl;
- return EXIT_FAILURE;
- }
+ if (EqString(args[0], "flush")) {
+ if (!bssl::acvp::FlushBuffer(STDOUT_FILENO)) {
+ ALOGE("Could not flush the buffer to stdout\n");
+ return EXIT_FAILURE;
+ }
+ } else {
+ ModuleWrapper wrapper;
+ auto res = wrapper.SendMessage(args);
+ if (!res.ok()) {
+ std::cerr << res.error() << std::endl;
+ return EXIT_FAILURE;
+ }
- res = wrapper.ForwardResponse();
- if (!res.ok()) {
- std::cerr << res.error() << std::endl;
- return EXIT_FAILURE;
+ res = wrapper.ForwardResponse();
+ if (!res.ok()) {
+ std::cerr << res.error() << std::endl;
+ return EXIT_FAILURE;
+ }
}
}
diff --git a/trusty/utils/coverage-controller/Android.bp b/trusty/utils/coverage-controller/Android.bp
new file mode 100644
index 0000000..e6d30d9
--- /dev/null
+++ b/trusty/utils/coverage-controller/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+ name: "trusty-coverage-controller",
+ vendor: true,
+
+ srcs: ["controller.cpp"],
+ shared_libs: [
+ "libc",
+ "liblog",
+ "libbase",
+ "libdmabufheap",
+ ],
+ static_libs: [
+ "libtrusty",
+ "libtrusty_line_coverage",
+ ],
+}
diff --git a/trusty/utils/coverage-controller/controller.cpp b/trusty/utils/coverage-controller/controller.cpp
new file mode 100644
index 0000000..381a452
--- /dev/null
+++ b/trusty/utils/coverage-controller/controller.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2023 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/stringprintf.h>
+#include <array>
+#include <getopt.h>
+#include <inttypes.h>
+#include <memory>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <trusty/line-coverage/coverage.h>
+#include <trusty/tipc.h>
+#include <vector>
+
+#include "controller.h"
+
+#define READ_ONCE(x) (*((volatile __typeof__(x) *) &(x)))
+#define WRITE_ONCE(x, val) (*((volatile __typeof__(val) *) &(x)) = (val))
+
+namespace android {
+namespace trusty {
+namespace controller {
+
+using ::android::trusty::line_coverage::CoverageRecord;
+
+void Controller::run(std::string output_dir) {
+ connectCoverageServer();
+ struct control *control;
+ uint64_t complete_cnt = 0, start_cnt = 0, flags;
+
+ while(1) {
+ setUpShm();
+
+ for (int index = 0; index < record_list_.size(); index++) {
+ control = (struct control *)record_list_[index]->getShm();
+ start_cnt = READ_ONCE((control->write_buffer_start_count));
+ complete_cnt = READ_ONCE(control->write_buffer_complete_count);
+ flags = READ_ONCE(control->cntrl_flags);
+
+ if (complete_cnt != counters[index] && start_cnt == complete_cnt) {
+ WRITE_ONCE(control->cntrl_flags, FLAG_NONE);
+ std::string filename;
+ filename = android::base::StringPrintf("/%s.%" PRIu64 ".profraw",
+ uuid_list_[index].c_str(),
+ counters[index]);
+ filename.insert(0, output_dir);
+ android::base::Result<void> res = record_list_[index]->SaveFile(filename);
+ counters[index]++;
+ }
+ if(complete_cnt == counters[index] &&
+ !(flags & FLAG_RUN)) {
+ flags |= FLAG_RUN;
+ WRITE_ONCE(control->cntrl_flags, flags);
+ }
+ }
+ }
+}
+
+void Controller::connectCoverageServer() {
+ coverage_srv_fd = tipc_connect(TIPC_DEV, LINE_COVERAGE_CLIENT_PORT);
+ if (coverage_srv_fd < 0) {
+ fprintf(stderr, \
+ "Error: Failed to connect to Trusty coverage server: %d\n", coverage_srv_fd);
+ return;
+ }
+}
+
+void Controller::setUpShm() {
+ struct line_coverage_client_req req;
+ struct line_coverage_client_resp resp;
+ uint32_t cur_index = record_list_.size();
+ struct uuid zero_uuid = {0, 0, 0, { 0 }};
+ char uuid_str[UUID_STR_SIZE];
+ req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_SEND_LIST;
+ int rc = write(coverage_srv_fd, &req, sizeof(req));
+ if (rc != (int)sizeof(req)) {
+ fprintf(stderr, "failed to send request to coverage server: %d\n", rc);
+ return;
+ }
+
+ while(1) {
+ rc = read(coverage_srv_fd, &resp, sizeof(resp));
+ if (rc != (int)sizeof(resp)) {
+ fprintf(stderr, "failed to read reply from coverage server:: %d\n", rc);
+ }
+
+ if (resp.hdr.cmd == (req.hdr.cmd | LINE_COVERAGE_CLIENT_CMD_RESP_BIT)) {
+ if (!memcmp(&resp.send_list_args.uuid, &zero_uuid, sizeof(struct uuid))) {
+ break;
+ }
+ if(uuid_set_.find(resp.send_list_args.uuid) == uuid_set_.end()) {
+ uuid_set_.insert(resp.send_list_args.uuid);
+ sprintf(uuid_str,
+ "%08" PRIx32 "-%04" PRIx16 "-%04" PRIx16 "-%02" PRIx8 "%02" PRIx8
+ "-%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8,
+ resp.send_list_args.uuid.time_low,
+ resp.send_list_args.uuid.time_mid,
+ resp.send_list_args.uuid.time_hi_and_version,
+ resp.send_list_args.uuid.clock_seq_and_node[0],
+ resp.send_list_args.uuid.clock_seq_and_node[1],
+ resp.send_list_args.uuid.clock_seq_and_node[2],
+ resp.send_list_args.uuid.clock_seq_and_node[3],
+ resp.send_list_args.uuid.clock_seq_and_node[4],
+ resp.send_list_args.uuid.clock_seq_and_node[5],
+ resp.send_list_args.uuid.clock_seq_and_node[6],
+ resp.send_list_args.uuid.clock_seq_and_node[7]);
+ uuid_list_.push_back(uuid_str);
+ record_list_.push_back(std::make_unique<CoverageRecord>(TIPC_DEV,
+ &resp.send_list_args.uuid));
+ counters.push_back(0);
+ }
+ }
+ else {
+ fprintf(stderr, "Unknown response header\n");
+ }
+ cur_index++;
+ req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_SEND_LIST;
+ req.send_list_args.index = cur_index;
+ int rc = write(coverage_srv_fd, &req, sizeof(req));
+ if (rc != (int)sizeof(req)) {
+ fprintf(stderr, "failed to send request to coverage server: %d\n", rc);
+ }
+ }
+
+ for(int ind = 0 ; ind < record_list_.size() ; ind++) {
+ record_list_[ind]->Open(coverage_srv_fd);
+ }
+}
+
+
+} // namespace controller
+} // namespace trusty
+} // namespace android
+
+int main(int argc, char* argv[]) {
+
+ std::string optarg = "";
+ do {
+ int c;
+ c = getopt(argc, argv, "o");
+
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'o':
+ break;
+ default:
+ fprintf(stderr, "usage: %s -o [output_directory]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ } while (1);
+
+ if (argc > optind + 1) {
+ fprintf(stderr, "%s: too many arguments\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (argc > optind) {
+ optarg = argv[optind];
+ }
+ if (optarg.size()==0) {
+ optarg = "data/local/tmp";
+ }
+
+ android::trusty::controller::Controller cur;
+ cur.run(optarg);
+
+ return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/trusty/utils/coverage-controller/controller.h b/trusty/utils/coverage-controller/controller.h
new file mode 100644
index 0000000..f7789bf
--- /dev/null
+++ b/trusty/utils/coverage-controller/controller.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 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 <trusty/line-coverage/coverage.h>
+#include <array>
+#include <memory>
+#include <vector>
+#include <set>
+
+#define TIPC_DEV "/dev/trusty-ipc-dev0"
+#define TEST_SRV_PORT "com.android.trusty.sancov.test.srv"
+#define TEST_SRV_MODULE "srv.syms.elf"
+
+#define UUID_STR_SIZE (37)
+
+#define FLAG_NONE 0x0
+#define FLAG_RUN 0x1
+#define FLAG_TOGGLE_CLEAR 0x2
+
+struct control {
+ /* Written by controller, read by instrumented TA */
+ uint64_t cntrl_flags;
+
+ /* Written by instrumented TA, read by controller */
+ uint64_t oper_flags;
+ uint64_t write_buffer_start_count;
+ uint64_t write_buffer_complete_count;
+};
+
+namespace android {
+namespace trusty {
+namespace controller {
+
+class Controller {
+ public:
+ public:
+ void run(std::string output_dir);
+
+ private:
+ std::vector<std::unique_ptr<line_coverage::CoverageRecord>>record_list_;
+ std::set<struct uuid>uuid_set_;
+ std::vector<std::string>uuid_list_;
+ std::vector<uint64_t> counters;
+ int coverage_srv_fd;
+
+ void connectCoverageServer();
+ void setUpShm();
+};
+
+} // namespace controller
+} // namespace trusty
+} // namespace android