Merge "Use scudo_stack_depot_size from process_info" into main
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 267571b..31e284d 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -186,6 +186,41 @@
export_include_dirs: ["include"],
}
+cc_library {
+ name: "libdebuggerd_tombstone_proto_to_text",
+ defaults: ["debuggerd_defaults"],
+ ramdisk_available: true,
+ recovery_available: true,
+ vendor_ramdisk_available: true,
+
+ local_include_dirs: ["libdebuggerd/include"],
+ export_include_dirs: ["libdebuggerd/include"],
+
+ srcs: [
+ "libdebuggerd/tombstone_proto_to_text.cpp",
+ ],
+
+ header_libs: [
+ "bionic_libc_platform_headers",
+ ],
+
+ static_libs: [
+ "libbase",
+ "liblog_for_runtime_apex",
+ "libunwindstack",
+ ],
+
+ whole_static_libs: [
+ "libtombstone_proto",
+ "libprotobuf-cpp-lite",
+ ],
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.runtime",
+ ],
+}
+
cc_library_static {
name: "libdebuggerd",
defaults: ["debuggerd_defaults"],
@@ -199,7 +234,6 @@
"libdebuggerd/open_files_list.cpp",
"libdebuggerd/tombstone.cpp",
"libdebuggerd/tombstone_proto.cpp",
- "libdebuggerd/tombstone_proto_to_text.cpp",
"libdebuggerd/utility.cpp",
],
@@ -225,6 +259,7 @@
],
whole_static_libs: [
+ "libdebuggerd_tombstone_proto_to_text",
"libasync_safe",
"gwp_asan_crash_handler",
"libtombstone_proto",
@@ -309,7 +344,7 @@
"libdebuggerd/test/elf_fake.cpp",
"libdebuggerd/test/log_fake.cpp",
"libdebuggerd/test/open_files_list_test.cpp",
- "libdebuggerd/test/utility_test.cpp",
+ "libdebuggerd/test/tombstone_proto_to_text_test.cpp",
],
target: {
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index c3e1302..a6d8226 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -94,7 +94,7 @@
if (sigaction(SIGALRM, &new_sigaction, &old_sigaction) != 0) { \
err(1, "sigaction failed"); \
} \
- alarm(seconds); \
+ alarm(seconds * android::base::HwTimeoutMultiplier()); \
auto value = expr; \
int saved_errno = errno; \
if (sigaction(SIGALRM, &old_sigaction, nullptr) != 0) { \
@@ -2825,7 +2825,8 @@
}
prev_file = match[1];
- unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(prev_file, 0).release());
+ auto elf_memory = unwindstack::Memory::CreateFileMemory(prev_file, 0);
+ unwindstack::Elf elf(elf_memory);
if (!elf.Init() || !elf.valid()) {
// Skipping invalid elf files.
continue;
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 198de37..26c2cd4 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -91,8 +91,6 @@
void get_signal_sender(char* buf, size_t n, const siginfo_t*);
const char* get_signame(const siginfo_t*);
const char* get_sigcode(const siginfo_t*);
-std::string describe_tagged_addr_ctrl(long ctrl);
-std::string describe_pac_enabled_keys(long keys);
// Number of bytes per MTE granule.
constexpr size_t kTagGranuleSize = 16;
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index c15bbea..e0ef515 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -78,7 +78,8 @@
}
__scudo_get_error_info(&error_info_, process_info.maybe_tagged_fault_address, stack_depot.get(),
- region_info.get(), ring_buffer.get(), memory.get(), memory_tags.get(),
+ __scudo_get_stack_depot_size(), region_info.get(), ring_buffer.get(),
+ process_info.scudo_ring_buffer_size, memory.get(), memory_tags.get(),
memory_begin, memory_end - memory_begin);
}
diff --git a/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp b/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp
new file mode 100644
index 0000000..ac92ac0
--- /dev/null
+++ b/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <sys/prctl.h>
+
+#include <string>
+
+#include <android-base/test_utils.h>
+
+#include "libdebuggerd/tombstone.h"
+#include "tombstone.pb.h"
+
+using CallbackType = std::function<void(const std::string& line, bool should_log)>;
+
+class TombstoneProtoToTextTest : public ::testing::Test {
+ public:
+ void SetUp() {
+ tombstone_.reset(new Tombstone);
+
+ tombstone_->set_arch(Architecture::ARM64);
+ tombstone_->set_build_fingerprint("Test fingerprint");
+ tombstone_->set_timestamp("1970-01-01 00:00:00");
+ tombstone_->set_pid(100);
+ tombstone_->set_tid(100);
+ tombstone_->set_uid(0);
+ tombstone_->set_selinux_label("none");
+
+ Signal signal;
+ signal.set_number(SIGSEGV);
+ signal.set_name("SIGSEGV");
+ signal.set_code(0);
+ signal.set_code_name("none");
+
+ *tombstone_->mutable_signal_info() = signal;
+
+ Thread thread;
+ thread.set_id(100);
+ thread.set_name("main");
+ thread.set_tagged_addr_ctrl(0);
+ thread.set_pac_enabled_keys(0);
+
+ auto& threads = *tombstone_->mutable_threads();
+ threads[100] = thread;
+ main_thread_ = &threads[100];
+ }
+
+ void ProtoToString() {
+ text_ = "";
+ EXPECT_TRUE(
+ tombstone_proto_to_text(*tombstone_, [this](const std::string& line, bool should_log) {
+ if (should_log) {
+ text_ += "LOG ";
+ }
+ text_ += line + '\n';
+ }));
+ }
+
+ Thread* main_thread_;
+ std::string text_;
+ std::unique_ptr<Tombstone> tombstone_;
+};
+
+TEST_F(TombstoneProtoToTextTest, tagged_addr_ctrl) {
+ main_thread_->set_tagged_addr_ctrl(0);
+ ProtoToString();
+ EXPECT_MATCH(text_, "LOG tagged_addr_ctrl: 0000000000000000\\n");
+
+ main_thread_->set_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE);
+ ProtoToString();
+ EXPECT_MATCH(text_, "LOG tagged_addr_ctrl: 0000000000000001 \\(PR_TAGGED_ADDR_ENABLE\\)\\n");
+
+ main_thread_->set_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
+ (0xfffe << PR_MTE_TAG_SHIFT));
+ ProtoToString();
+ EXPECT_MATCH(text_,
+ "LOG tagged_addr_ctrl: 000000000007fff3 \\(PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, "
+ "mask 0xfffe\\)\\n");
+
+ main_thread_->set_tagged_addr_ctrl(0xf0000000 | PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
+ PR_MTE_TCF_ASYNC | (0xfffe << PR_MTE_TAG_SHIFT));
+ ProtoToString();
+ EXPECT_MATCH(text_,
+ "LOG tagged_addr_ctrl: 00000000f007fff7 \\(PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, "
+ "PR_MTE_TCF_ASYNC, mask 0xfffe, unknown 0xf0000000\\)\\n");
+}
+
+TEST_F(TombstoneProtoToTextTest, pac_enabled_keys) {
+ main_thread_->set_pac_enabled_keys(0);
+ ProtoToString();
+ EXPECT_MATCH(text_, "LOG pac_enabled_keys: 0000000000000000\\n");
+
+ main_thread_->set_pac_enabled_keys(PR_PAC_APIAKEY);
+ ProtoToString();
+ EXPECT_MATCH(text_, "LOG pac_enabled_keys: 0000000000000001 \\(PR_PAC_APIAKEY\\)\\n");
+
+ main_thread_->set_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY);
+ ProtoToString();
+ EXPECT_MATCH(text_,
+ "LOG pac_enabled_keys: 0000000000000009 \\(PR_PAC_APIAKEY, PR_PAC_APDBKEY\\)\\n");
+
+ main_thread_->set_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY | 0x1000);
+ ProtoToString();
+ EXPECT_MATCH(text_,
+ "LOG pac_enabled_keys: 0000000000001009 \\(PR_PAC_APIAKEY, PR_PAC_APDBKEY, unknown "
+ "0x1000\\)\\n");
+}
diff --git a/debuggerd/libdebuggerd/test/utility_test.cpp b/debuggerd/libdebuggerd/test/utility_test.cpp
deleted file mode 100644
index dad3380..0000000
--- a/debuggerd/libdebuggerd/test/utility_test.cpp
+++ /dev/null
@@ -1,42 +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 <gtest/gtest.h>
-#include <sys/prctl.h>
-
-#include "libdebuggerd/utility.h"
-
-TEST(UtilityTest, describe_tagged_addr_ctrl) {
- EXPECT_EQ("", describe_tagged_addr_ctrl(0));
- EXPECT_EQ(" (PR_TAGGED_ADDR_ENABLE)", describe_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE));
- EXPECT_EQ(" (PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, mask 0xfffe)",
- describe_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
- (0xfffe << PR_MTE_TAG_SHIFT)));
- EXPECT_EQ(
- " (PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, PR_MTE_TCF_ASYNC, mask 0xfffe, unknown "
- "0xf0000000)",
- describe_tagged_addr_ctrl(0xf0000000 | PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
- PR_MTE_TCF_ASYNC | (0xfffe << PR_MTE_TAG_SHIFT)));
-}
-
-TEST(UtilityTest, describe_pac_enabled_keys) {
- EXPECT_EQ("", describe_pac_enabled_keys(0));
- EXPECT_EQ(" (PR_PAC_APIAKEY)", describe_pac_enabled_keys(PR_PAC_APIAKEY));
- EXPECT_EQ(" (PR_PAC_APIAKEY, PR_PAC_APDBKEY)",
- describe_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY));
- EXPECT_EQ(" (PR_PAC_APIAKEY, PR_PAC_APDBKEY, unknown 0x1000)",
- describe_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY | 0x1000));
-}
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index eed81fc..ad91320 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -28,8 +28,8 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <async_safe/log.h>
#include <bionic/macros.h>
+#include <sys/prctl.h>
#include "tombstone.pb.h"
@@ -41,6 +41,42 @@
#define CBS(...) CB(false, __VA_ARGS__)
using CallbackType = std::function<void(const std::string& line, bool should_log)>;
+#define DESCRIBE_FLAG(flag) \
+ if (value & flag) { \
+ desc += ", "; \
+ desc += #flag; \
+ value &= ~flag; \
+ }
+
+static std::string describe_end(long value, std::string& desc) {
+ if (value) {
+ desc += StringPrintf(", unknown 0x%lx", value);
+ }
+ return desc.empty() ? "" : " (" + desc.substr(2) + ")";
+}
+
+static std::string describe_tagged_addr_ctrl(long value) {
+ std::string desc;
+ DESCRIBE_FLAG(PR_TAGGED_ADDR_ENABLE);
+ DESCRIBE_FLAG(PR_MTE_TCF_SYNC);
+ DESCRIBE_FLAG(PR_MTE_TCF_ASYNC);
+ if (value & PR_MTE_TAG_MASK) {
+ desc += StringPrintf(", mask 0x%04lx", (value & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT);
+ value &= ~PR_MTE_TAG_MASK;
+ }
+ return describe_end(value, desc);
+}
+
+static std::string describe_pac_enabled_keys(long value) {
+ std::string desc;
+ DESCRIBE_FLAG(PR_PAC_APIAKEY);
+ DESCRIBE_FLAG(PR_PAC_APIBKEY);
+ DESCRIBE_FLAG(PR_PAC_APDAKEY);
+ DESCRIBE_FLAG(PR_PAC_APDBKEY);
+ DESCRIBE_FLAG(PR_PAC_APGAKEY);
+ return describe_end(value, desc);
+}
+
static const char* abi_string(const Tombstone& tombstone) {
switch (tombstone.arch()) {
case Architecture::ARM32:
@@ -138,7 +174,8 @@
break;
default:
- async_safe_fatal("unknown architecture");
+ CBL("Unknown architecture %d printing thread registers", tombstone.arch());
+ return;
}
for (const auto& reg : thread.registers()) {
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 15f09b3..742ac7c 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -445,42 +445,6 @@
return "?";
}
-#define DESCRIBE_FLAG(flag) \
- if (value & flag) { \
- desc += ", "; \
- desc += #flag; \
- value &= ~flag; \
- }
-
-static std::string describe_end(long value, std::string& desc) {
- if (value) {
- desc += StringPrintf(", unknown 0x%lx", value);
- }
- return desc.empty() ? "" : " (" + desc.substr(2) + ")";
-}
-
-std::string describe_tagged_addr_ctrl(long value) {
- std::string desc;
- DESCRIBE_FLAG(PR_TAGGED_ADDR_ENABLE);
- DESCRIBE_FLAG(PR_MTE_TCF_SYNC);
- DESCRIBE_FLAG(PR_MTE_TCF_ASYNC);
- if (value & PR_MTE_TAG_MASK) {
- desc += StringPrintf(", mask 0x%04lx", (value & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT);
- value &= ~PR_MTE_TAG_MASK;
- }
- return describe_end(value, desc);
-}
-
-std::string describe_pac_enabled_keys(long value) {
- std::string desc;
- DESCRIBE_FLAG(PR_PAC_APIAKEY);
- DESCRIBE_FLAG(PR_PAC_APIBKEY);
- DESCRIBE_FLAG(PR_PAC_APDAKEY);
- DESCRIBE_FLAG(PR_PAC_APDBKEY);
- DESCRIBE_FLAG(PR_PAC_APGAKEY);
- return describe_end(value, desc);
-}
-
void log_backtrace(log_t* log, unwindstack::AndroidUnwinder* unwinder,
unwindstack::AndroidUnwinderData& data, const char* prefix) {
std::set<std::string> unreadable_elf_files;
diff --git a/debuggerd/proto/Android.bp b/debuggerd/proto/Android.bp
index 804f805..7be5d61 100644
--- a/debuggerd/proto/Android.bp
+++ b/debuggerd/proto/Android.bp
@@ -39,3 +39,17 @@
recovery_available: true,
vendor_ramdisk_available: true,
}
+
+java_library_static {
+ name: "libtombstone_proto_java",
+ proto: {
+ type: "lite",
+ },
+ srcs: [
+ "tombstone.proto",
+ ],
+ jarjar_rules: "jarjar-rules.txt",
+ sdk_version: "current",
+ static_libs: ["libprotobuf-java-lite"],
+}
+
diff --git a/debuggerd/proto/jarjar-rules.txt b/debuggerd/proto/jarjar-rules.txt
new file mode 100644
index 0000000..66878a9
--- /dev/null
+++ b/debuggerd/proto/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.google.protobuf.** com.android.server.os.protobuf.@1
diff --git a/fastboot/task.cpp b/fastboot/task.cpp
index 25c5a6e..0947ff9 100644
--- a/fastboot/task.cpp
+++ b/fastboot/task.cpp
@@ -224,7 +224,7 @@
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()) {
+ } else if (task->AsUpdateSuperTask()) {
return true;
} else if (auto reboot_task = task->AsRebootTask()) {
if (reboot_task->GetTarget() == "fastboot") {
diff --git a/fastboot/task_test.cpp b/fastboot/task_test.cpp
index 9cde1a8..81154c6 100644
--- a/fastboot/task_test.cpp
+++ b/fastboot/task_test.cpp
@@ -354,7 +354,7 @@
const std::vector<std::unique_ptr<Task>>& tasks) {
bool contains_optimized_task = false;
for (auto& task : tasks) {
- if (auto optimized_task = task->AsOptimizedFlashSuperTask()) {
+ if (task->AsOptimizedFlashSuperTask()) {
contains_optimized_task = true;
}
if (auto flash_task = task->AsFlashTask()) {
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 324f50a..edecd7c 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -25,7 +25,7 @@
{
"name": "vab_legacy_tests"
},
- // TODO: b/279009697
+ // TODO(b/279009697):
//{"name": "vabc_legacy_tests"},
{
"name": "cow_api_test"
@@ -47,7 +47,7 @@
{
"name": "vab_legacy_tests"
},
- // TODO: b/279009697
+ // 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 d55f8d3..af2b35a 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -701,6 +701,29 @@
}
//
+// Mechanism to allow fsck to be triggered by setting ro.preventative_fsck
+// Introduced to address b/305658663
+// If the property value is not equal to the flag file contents, trigger
+// fsck and store the property value in the flag file
+// If we want to trigger again, simply change the property value
+//
+static bool check_if_preventative_fsck_needed(const FstabEntry& entry) {
+ const char* flag_file = "/metadata/vold/preventative_fsck";
+ if (entry.mount_point != "/data") return false;
+
+ // Don't error check - both default to empty string, which is OK
+ std::string prop = android::base::GetProperty("ro.preventative_fsck", "");
+ std::string flag;
+ android::base::ReadFileToString(flag_file, &flag);
+ if (prop == flag) return false;
+ // fsck is run immediately, so assume it runs or there is some deeper problem
+ if (!android::base::WriteStringToFile(prop, flag_file))
+ PERROR << "Failed to write file " << flag_file;
+ LINFO << "Run preventative fsck on /data";
+ return true;
+}
+
+//
// Prepare the filesystem on the given block device to be mounted.
//
// If the "check" option was given in the fstab record, or it seems that the
@@ -750,7 +773,7 @@
}
}
- if (entry.fs_mgr_flags.check ||
+ if (check_if_preventative_fsck_needed(entry) || entry.fs_mgr_flags.check ||
(fs_stat & (FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED))) {
check_fs(blk_device, entry.fs_type, mount_point, &fs_stat);
}
@@ -828,7 +851,14 @@
<< ",type=" << entry.fs_type << ", gc_allowance=" << gc_allowance << "%)=" << ret
<< "(" << save_errno << ")";
}
- ret = mount(source.c_str(), target.c_str(), entry.fs_type.c_str(), mountflags,
+
+ // Let's get the raw dm target, if it's a symlink, since some existing applications
+ // rely on /proc/mounts to find the userdata's dm target path. Don't break that assumption.
+ std::string real_source;
+ if (!android::base::Realpath(source, &real_source)) {
+ real_source = source;
+ }
+ ret = mount(real_source.c_str(), target.c_str(), entry.fs_type.c_str(), mountflags,
opts.c_str());
save_errno = errno;
if (try_f2fs_gc_allowance) gc_allowance += 10;
@@ -1237,17 +1267,27 @@
};
std::string fs_mgr_find_bow_device(const std::string& block_device) {
- if (block_device.substr(0, 5) != "/dev/") {
- LOG(ERROR) << "Expected block device, got " << block_device;
- return std::string();
+ // handle symlink such as "/dev/block/mapper/userdata"
+ std::string real_path;
+ if (!android::base::Realpath(block_device, &real_path)) {
+ real_path = block_device;
}
- std::string sys_dir = std::string("/sys/") + block_device.substr(5);
-
+ struct stat st;
+ if (stat(real_path.c_str(), &st) < 0) {
+ PLOG(ERROR) << "stat failed: " << real_path;
+ return std::string();
+ }
+ if (!S_ISBLK(st.st_mode)) {
+ PLOG(ERROR) << real_path << " is not block device";
+ return std::string();
+ }
+ std::string sys_dir = android::base::StringPrintf("/sys/dev/block/%u:%u", major(st.st_rdev),
+ minor(st.st_rdev));
for (;;) {
std::string name;
if (!android::base::ReadFileToString(sys_dir + "/dm/name", &name)) {
- PLOG(ERROR) << block_device << " is not dm device";
+ PLOG(ERROR) << real_path << " is not dm device";
return std::string();
}
@@ -1381,6 +1421,8 @@
return {FS_MGR_MNTALL_FAIL, userdata_mounted};
}
+ bool scratch_can_be_mounted = true;
+
// Keep i int to prevent unsigned integer overflow from (i = top_idx - 1),
// where top_idx is 0. It will give SIGABRT
for (int i = 0; i < static_cast<int>(fstab->size()); i++) {
@@ -1513,6 +1555,9 @@
if (current_entry.mount_point == "/data") {
userdata_mounted = true;
}
+
+ MountOverlayfs(attempted_entry, &scratch_can_be_mounted);
+
// Success! Go get the next one.
continue;
}
@@ -1597,10 +1642,6 @@
set_type_property(encryptable);
-#if ALLOW_ADBD_DISABLE_VERITY == 1 // "userdebug" build
- fs_mgr_overlayfs_mount_all(fstab);
-#endif
-
if (error_count) {
return {FS_MGR_MNTALL_FAIL, userdata_mounted};
} else {
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 622f181..8e76150 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -136,6 +136,7 @@
/* Format the partition using the calculated length */
const auto size_str = std::to_string(dev_sz / getpagesize());
+ std::string block_size = std::to_string(getpagesize());
std::vector<const char*> args = {"/system/bin/make_f2fs", "-g", "android"};
if (needs_projid) {
@@ -154,6 +155,10 @@
args.push_back("-O");
args.push_back("extra_attr");
}
+ args.push_back("-w");
+ args.push_back(block_size.c_str());
+ args.push_back("-b");
+ args.push_back(block_size.c_str());
if (!zoned_device.empty()) {
args.push_back("-c");
args.push_back(zoned_device.c_str());
diff --git a/fs_mgr/fs_mgr_overlayfs_control.cpp b/fs_mgr/fs_mgr_overlayfs_control.cpp
index 50d8280..06214ef 100644
--- a/fs_mgr/fs_mgr_overlayfs_control.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_control.cpp
@@ -347,33 +347,6 @@
return "";
}
-// This returns the scratch device that was detected during early boot (first-
-// stage init). If the device was created later, for example during setup for
-// the adb remount command, it can return an empty string since it does not
-// query ImageManager. (Note that ImageManager in first-stage init will always
-// use device-mapper, since /data is not available to use loop devices.)
-static std::string GetBootScratchDevice() {
- // Note: fs_mgr_is_dsu_running() always returns false in recovery or fastbootd.
- if (fs_mgr_is_dsu_running()) {
- return GetDsuScratchDevice();
- }
-
- auto& dm = DeviceMapper::Instance();
-
- // If there is a scratch partition allocated in /data or on super, we
- // automatically prioritize that over super_other or system_other.
- // Some devices, for example, have a write-protected eMMC and the
- // super partition cannot be used even if it exists.
- std::string device;
- auto partition_name = android::base::Basename(kScratchMountPoint);
- if (dm.GetState(partition_name) != DmDeviceState::INVALID &&
- dm.GetDmDevicePathByName(partition_name, &device)) {
- return device;
- }
-
- return "";
-}
-
bool MakeScratchFilesystem(const std::string& scratch_device) {
// Force mkfs by design for overlay support of adb remount, simplify and
// thus do not rely on fsck to correct problems that could creep in.
@@ -383,6 +356,8 @@
fs_type = "f2fs";
command = kMkF2fs + " -w "s;
command += std::to_string(getpagesize());
+ command = kMkF2fs + " -b "s;
+ command += std::to_string(getpagesize());
command += " -f -d1 -l" + android::base::Basename(kScratchMountPoint);
} else if (!access(kMkExt4, X_OK) && fs_mgr_filesystem_available("ext4")) {
fs_type = "ext4";
@@ -881,21 +856,6 @@
return;
}
- bool want_scratch = false;
- for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
- if (fs_mgr_is_verity_enabled(entry)) {
- continue;
- }
- if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) {
- continue;
- }
- want_scratch = true;
- break;
- }
- if (!want_scratch) {
- return;
- }
-
if (ScratchIsOnData()) {
if (auto images = IImageManager::Open("remount", 0ms)) {
images->MapAllImages(init);
@@ -922,6 +882,33 @@
}
}
+// This returns the scratch device that was detected during early boot (first-
+// stage init). If the device was created later, for example during setup for
+// the adb remount command, it can return an empty string since it does not
+// query ImageManager. (Note that ImageManager in first-stage init will always
+// use device-mapper, since /data is not available to use loop devices.)
+std::string GetBootScratchDevice() {
+ // Note: fs_mgr_is_dsu_running() always returns false in recovery or fastbootd.
+ if (fs_mgr_is_dsu_running()) {
+ return GetDsuScratchDevice();
+ }
+
+ auto& dm = DeviceMapper::Instance();
+
+ // If there is a scratch partition allocated in /data or on super, we
+ // automatically prioritize that over super_other or system_other.
+ // Some devices, for example, have a write-protected eMMC and the
+ // super partition cannot be used even if it exists.
+ std::string device;
+ auto partition_name = android::base::Basename(kScratchMountPoint);
+ if (dm.GetState(partition_name) != DmDeviceState::INVALID &&
+ dm.GetDmDevicePathByName(partition_name, &device)) {
+ return device;
+ }
+
+ return "";
+}
+
void TeardownAllOverlayForMountPoint(const std::string& mount_point) {
if (!OverlayfsTeardownAllowed()) {
return;
diff --git a/fs_mgr/fs_mgr_overlayfs_control.h b/fs_mgr/fs_mgr_overlayfs_control.h
index b175101..3516c46 100644
--- a/fs_mgr/fs_mgr_overlayfs_control.h
+++ b/fs_mgr/fs_mgr_overlayfs_control.h
@@ -38,5 +38,7 @@
void CleanupOldScratchFiles();
+std::string GetBootScratchDevice();
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/fs_mgr_overlayfs_mount.cpp b/fs_mgr/fs_mgr_overlayfs_mount.cpp
index ae7ed4c..e168436 100644
--- a/fs_mgr/fs_mgr_overlayfs_mount.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_mount.cpp
@@ -34,6 +34,7 @@
#include <android-base/file.h>
#include <android-base/macros.h>
#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <ext4_utils/ext4_utils.h>
@@ -41,15 +42,14 @@
#include <fs_mgr/file_wait.h>
#include <fs_mgr_overlayfs.h>
#include <fstab/fstab.h>
-#include <libdm/dm.h>
#include <libgsi/libgsi.h>
#include <storage_literals/storage_literals.h>
+#include "fs_mgr_overlayfs_control.h"
#include "fs_mgr_overlayfs_mount.h"
#include "fs_mgr_priv.h"
using namespace std::literals;
-using namespace android::dm;
using namespace android::fs_mgr;
using namespace android::storage_literals;
@@ -58,6 +58,9 @@
constexpr char kCacheMountPoint[] = "/cache";
constexpr char kPhysicalDevice[] = "/dev/block/by-name/";
+// Mount tree to temporarily hold references to submounts.
+constexpr char kMoveMountTempDir[] = "/dev/remount";
+
constexpr char kLowerdirOption[] = "lowerdir=";
constexpr char kUpperdirOption[] = "upperdir=";
constexpr char kWorkdirOption[] = "workdir=";
@@ -284,10 +287,6 @@
if (ret) {
PERROR << "__mount(target=" << mount_point
<< ",flag=" << (shared_flag ? "MS_SHARED" : "MS_PRIVATE") << ")=" << ret;
- // If "/system" doesn't look like a mountpoint, retry with "/".
- if (errno == EINVAL && mount_point == "/system") {
- return fs_mgr_overlayfs_set_shared_mount("/", shared_flag);
- }
return false;
}
return true;
@@ -302,6 +301,25 @@
return true;
}
+static bool fs_mgr_overlayfs_mount(const std::string& mount_point, const std::string& options) {
+ auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
+ for (const auto& opt : android::base::Split(options, ",")) {
+ if (android::base::StartsWith(opt, kUpperdirOption)) {
+ report = report + "," + opt;
+ break;
+ }
+ }
+ report = report + ")=";
+ auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME,
+ options.c_str());
+ if (ret) {
+ PERROR << report << ret;
+ } else {
+ LINFO << report << ret;
+ }
+ return !ret;
+}
+
struct mount_info {
std::string mount_point;
bool shared_flag;
@@ -374,24 +392,23 @@
return info;
}
-static bool fs_mgr_overlayfs_mount(const FstabEntry& entry) {
- const auto mount_point = fs_mgr_mount_point(entry.mount_point);
- const auto options = fs_mgr_get_overlayfs_options(entry);
+static bool fs_mgr_overlayfs_mount_one(const FstabEntry& fstab_entry) {
+ const auto mount_point = fs_mgr_mount_point(fstab_entry.mount_point);
+ const auto options = fs_mgr_get_overlayfs_options(fstab_entry);
if (options.empty()) return false;
- auto retval = true;
-
struct MoveEntry {
std::string mount_point;
std::string dir;
bool shared_flag;
};
-
std::vector<MoveEntry> moved_mounts;
- auto parent_private = false;
- auto parent_made_private = false;
- auto dev_private = false;
- auto dev_made_private = false;
+
+ bool retval = true;
+ bool move_dir_shared = true;
+ bool parent_shared = true;
+ bool root_shared = true;
+ bool root_made_private = false;
// There could be multiple mount entries with the same mountpoint.
// Group these entries together with stable_sort, and keep only the last entry of a group.
@@ -411,18 +428,32 @@
// mountinfo is reversed twice, so still is in lexical sorted order.
for (const auto& entry : mountinfo) {
- if ((entry.mount_point == mount_point) && !entry.shared_flag) {
- parent_private = true;
+ if (entry.mount_point == kMoveMountTempDir) {
+ move_dir_shared = entry.shared_flag;
}
- if ((entry.mount_point == "/dev") && !entry.shared_flag) {
- dev_private = true;
+ if (entry.mount_point == mount_point ||
+ (mount_point == "/system" && entry.mount_point == "/")) {
+ parent_shared = entry.shared_flag;
}
+ if (entry.mount_point == "/") {
+ root_shared = entry.shared_flag;
+ }
+ }
+
+ // Precondition is that kMoveMountTempDir is MS_PRIVATE, otherwise don't try to move any
+ // submount in to or out of it.
+ if (move_dir_shared) {
+ mountinfo.clear();
}
// Need to make the original mountpoint MS_PRIVATE, so that the overlayfs can be MS_MOVE.
// This could happen if its parent mount is remounted later.
- if (!parent_private) {
- parent_made_private = fs_mgr_overlayfs_set_shared_mount(mount_point, false);
+ if (!fs_mgr_overlayfs_set_shared_mount(mount_point, false)) {
+ // If failed to set "/system" mount type, it might be due to "/system" not being a valid
+ // mountpoint after switch root. Retry with "/" in this case.
+ if (errno == EINVAL && mount_point == "/system") {
+ root_made_private = fs_mgr_overlayfs_set_shared_mount("/", false);
+ }
}
for (const auto& entry : mountinfo) {
@@ -440,8 +471,8 @@
// mountinfo is in lexical order, so no need to worry about |entry| being a parent mount of
// entries of |moved_mounts|.
- // use as the bound directory in /dev.
- MoveEntry new_entry{entry.mount_point, "/dev/TemporaryDir-XXXXXX", entry.shared_flag};
+ MoveEntry new_entry{entry.mount_point, kMoveMountTempDir + "/TemporaryDir-XXXXXX"s,
+ entry.shared_flag};
{
AutoSetFsCreateCon createcon;
auto new_context = fs_mgr_get_context(entry.mount_point);
@@ -475,32 +506,10 @@
moved_mounts.push_back(std::move(new_entry));
}
- // hijack __mount() report format to help triage
- auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
- const auto opt_list = android::base::Split(options, ",");
- for (const auto& opt : opt_list) {
- if (android::base::StartsWith(opt, kUpperdirOption)) {
- report = report + "," + opt;
- break;
- }
- }
- report = report + ")=";
-
- auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME,
- options.c_str());
- if (ret) {
- retval = false;
- PERROR << report << ret;
- } else {
- LINFO << report << ret;
- }
+ retval &= fs_mgr_overlayfs_mount(mount_point, options);
// Move submounts back.
for (const auto& entry : moved_mounts) {
- if (!dev_private && !dev_made_private) {
- dev_made_private = fs_mgr_overlayfs_set_shared_mount("/dev", false);
- }
-
if (!fs_mgr_overlayfs_move_mount(entry.dir, entry.mount_point)) {
retval = false;
} else if (entry.shared_flag &&
@@ -509,12 +518,13 @@
}
rmdir(entry.dir.c_str());
}
- if (dev_made_private) {
- fs_mgr_overlayfs_set_shared_mount("/dev", true);
- }
- if (parent_made_private) {
+ // If the original (overridden) mount was MS_SHARED, then set the overlayfs mount to MS_SHARED.
+ if (parent_shared) {
fs_mgr_overlayfs_set_shared_mount(mount_point, true);
}
+ if (root_shared && root_made_private) {
+ fs_mgr_overlayfs_set_shared_mount("/", true);
+ }
return retval;
}
@@ -582,45 +592,6 @@
return true;
}
-// 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() {
- auto& dm = DeviceMapper::Instance();
- std::string device;
- if (dm.GetState(android::gsi::kDsuScratch) != DmDeviceState::INVALID &&
- dm.GetDmDevicePathByName(android::gsi::kDsuScratch, &device)) {
- return device;
- }
- return "";
-}
-
-// This returns the scratch device that was detected during early boot (first-
-// stage init). If the device was created later, for example during setup for
-// the adb remount command, it can return an empty string since it does not
-// query ImageManager. (Note that ImageManager in first-stage init will always
-// use device-mapper, since /data is not available to use loop devices.)
-static std::string GetBootScratchDevice() {
- // Note: fs_mgr_is_dsu_running() always returns false in recovery or fastbootd.
- if (fs_mgr_is_dsu_running()) {
- return GetDsuScratchDevice();
- }
-
- auto& dm = DeviceMapper::Instance();
-
- // If there is a scratch partition allocated in /data or on super, we
- // automatically prioritize that over super_other or system_other.
- // Some devices, for example, have a write-protected eMMC and the
- // super partition cannot be used even if it exists.
- std::string device;
- auto partition_name = android::base::Basename(kScratchMountPoint);
- if (dm.GetState(partition_name) != DmDeviceState::INVALID &&
- dm.GetDmDevicePathByName(partition_name, &device)) {
- return device;
- }
-
- return "";
-}
-
// NOTE: OverlayfsSetupAllowed() must be "stricter" than OverlayfsTeardownAllowed().
// Setup is allowed only if teardown is also allowed.
bool OverlayfsSetupAllowed(bool verbose) {
@@ -730,6 +701,25 @@
if (!OverlayfsSetupAllowed()) {
return false;
}
+
+ // Ensure kMoveMountTempDir is standalone mount tree with 'private' propagation by bind mounting
+ // to itself and set to MS_PRIVATE.
+ // Otherwise mounts moved in to it would have their propagation type changed unintentionally.
+ // Section 5d, https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
+ if (!fs_mgr_overlayfs_already_mounted(kMoveMountTempDir, false)) {
+ if (mkdir(kMoveMountTempDir, 0755) && errno != EEXIST) {
+ PERROR << "mkdir " << kMoveMountTempDir;
+ }
+ if (mount(kMoveMountTempDir, kMoveMountTempDir, nullptr, MS_BIND, nullptr)) {
+ PERROR << "bind mount " << kMoveMountTempDir;
+ }
+ }
+ fs_mgr_overlayfs_set_shared_mount(kMoveMountTempDir, false);
+ android::base::ScopeGuard umountDir([]() {
+ umount(kMoveMountTempDir);
+ rmdir(kMoveMountTempDir);
+ });
+
auto ret = true;
auto scratch_can_be_mounted = !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false);
for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
@@ -742,7 +732,7 @@
scratch_can_be_mounted = false;
TryMountScratch();
}
- ret &= fs_mgr_overlayfs_mount(entry);
+ ret &= fs_mgr_overlayfs_mount_one(entry);
}
return ret;
}
@@ -785,3 +775,38 @@
}
return false;
}
+
+namespace android {
+namespace fs_mgr {
+
+void MountOverlayfs(const FstabEntry& fstab_entry, bool* scratch_can_be_mounted) {
+ if (!OverlayfsSetupAllowed()) {
+ return;
+ }
+ const auto candidates = fs_mgr_overlayfs_candidate_list({fstab_entry});
+ if (candidates.empty()) {
+ return;
+ }
+ const auto& entry = candidates.front();
+ if (fs_mgr_is_verity_enabled(entry)) {
+ return;
+ }
+ const auto mount_point = fs_mgr_mount_point(entry.mount_point);
+ if (fs_mgr_overlayfs_already_mounted(mount_point)) {
+ return;
+ }
+ if (*scratch_can_be_mounted) {
+ *scratch_can_be_mounted = false;
+ if (!fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
+ TryMountScratch();
+ }
+ }
+ const auto options = fs_mgr_get_overlayfs_options(entry);
+ if (options.empty()) {
+ return;
+ }
+ fs_mgr_overlayfs_mount(mount_point, options);
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
index 2ad8125..a697fb3 100644
--- a/fs_mgr/fs_mgr_roots.cpp
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -115,8 +115,8 @@
return true;
}
- static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "f2fs", "erofs",
- "none"};
+ static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "exfat", "f2fs",
+ "erofs", "none"};
if (std::find(supported_fs.begin(), supported_fs.end(), rec->fs_type) == supported_fs.end()) {
LERROR << "unknown fs_type \"" << rec->fs_type << "\" for " << mount_point;
return false;
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index bdaabbf..bf68b2c 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -30,6 +30,9 @@
namespace android {
namespace fs_mgr {
+// Mount the overlayfs override for |fstab_entry|.
+void MountOverlayfs(const FstabEntry& fstab_entry, bool* scratch_can_be_mounted);
+
void MapScratchPartitionIfNeeded(Fstab* fstab,
const std::function<bool(const std::set<std::string>&)>& init);
diff --git a/fs_mgr/include_fstab b/fs_mgr/include_fstab
deleted file mode 120000
index 728737f..0000000
--- a/fs_mgr/include_fstab
+++ /dev/null
@@ -1 +0,0 @@
-libfstab/include
\ No newline at end of file
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 3a9ed9b..e261aa3 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -612,8 +612,12 @@
std::optional<std::string> ExtractBlockDeviceName(const std::string& path) {
static constexpr std::string_view kDevBlockPrefix("/dev/block/");
- if (android::base::StartsWith(path, kDevBlockPrefix)) {
- return path.substr(kDevBlockPrefix.length());
+ std::string real_path;
+ if (!android::base::Realpath(path, &real_path)) {
+ real_path = path;
+ }
+ if (android::base::StartsWith(real_path, kDevBlockPrefix)) {
+ return real_path.substr(kDevBlockPrefix.length());
}
return {};
}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 3e7ecc6..22c475f 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -52,8 +52,8 @@
static constexpr uint64_t kSectorSize = 512;
-// Returns `path` without /dev/block prefix if and only if `path` starts with
-// that prefix.
+// Returns `path` without /dev/block prefix if `path` starts with that prefix.
+// Or, if `path` is a symlink, do the same with its real path.
std::optional<std::string> ExtractBlockDeviceName(const std::string& path);
// This interface is for testing purposes. See DeviceMapper proper for what these methods do.
diff --git a/fs_mgr/liblp/fuzzer/README.md b/fs_mgr/liblp/fuzzer/README.md
index f831e2e..015c59b 100644
--- a/fs_mgr/liblp/fuzzer/README.md
+++ b/fs_mgr/liblp/fuzzer/README.md
@@ -18,15 +18,14 @@
9. alignmentOffset (parameter name: "alignment_offset")
10. logicalBlockSize (parameter name: "logical_block_size")
11. maxMetadataSize (parameter name: "max_metadata_size")
-12. numSlots (parameter name: "metadata_slot_count")
-13. deviceIndex (parameter name: "device_index")
-14. start (parameter name: "start")
-15. end (parameter name: "end")
-16. addedGroupName (parameter name: "group_name")
-17. partitionGroupName (parameter name: "partition_name")
-18. numSectors (parameter name: "num_sectors")
-19. physicalSector (parameter name: "physical_sector")
-20. resizedPartitionSize (parameter name: "requested_size")
+12. deviceIndex (parameter name: "device_index")
+13. start (parameter name: "start")
+14. end (parameter name: "end")
+15. addedGroupName (parameter name: "group_name")
+16. partitionGroupName (parameter name: "partition_name")
+17. numSectors (parameter name: "num_sectors")
+18. physicalSector (parameter name: "physical_sector")
+19. resizedPartitionSize (parameter name: "requested_size")
| Parameter| Valid Values| Configured Value|
|------------- |-------------| ----- |
@@ -41,7 +40,6 @@
|`alignmentOffset`| Integer |Value obtained from FuzzedDataProvider|
|`logicalBlockSize`| Integer |Value obtained from FuzzedDataProvider|
|`maxMetadataSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|
-|`numSlots`| Integer value from `0` to `2` |Value obtained from FuzzedDataProvider|
|`deviceIndex`| Integer |Value obtained from FuzzedDataProvider|
|`start`| Integer |Value obtained from FuzzedDataProvider|
|`end`| Integer |Value obtained from FuzzedDataProvider|
@@ -67,17 +65,15 @@
1. kAttributeTypes (parameter name: "attribute")
2. blockDevSize (parameter name: "blockdev_size")
3. metadataMaxSize (parameter name: "metadata_max_size")
-4. metadataSlotCount (parameter name: "metadata_slot_count")
-5. partitionName (parameter name: "partition_name")
-6. data (parameter name: "data")
-7. imageName (parameter name: "image_name")
+4. partitionName (parameter name: "partition_name")
+5. data (parameter name: "data")
+6. imageName (parameter name: "image_name")
| Parameter| Valid Values| Configured Value|
|------------- |-------------| ----- |
|`kAttributeTypes`| 1.`LP_PARTITION_ATTR_NONE`,<br/> 2.`LP_PARTITION_ATTR_READONLY`,<br/> 3.`LP_PARTITION_ATTR_SLOT_SUFFIXED`,<br/> 4.`LP_PARTITION_ATTR_UPDATED`,<br/> 5.`LP_PARTITION_ATTR_DISABLED`|Value obtained from FuzzedDataProvider|
|`blockDevSize`| Integer value from `0` to `100000`|Value obtained from FuzzedDataProvider|
|`metadataMaxSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|
-|`metadataSlotCount`| Integer value from `0` to `2` |Value obtained from FuzzedDataProvider|
|`partitionName`| String |Value obtained from FuzzedDataProvider|
|`data`| String |Value obtained from FuzzedDataProvider|
|`imageName`| String |Value obtained from FuzzedDataProvider|
@@ -102,12 +98,11 @@
4. logicalBlockSize (parameter name: "logical_block_size")
5. blockDevSize (parameter name: "blockdev_size")
6. metadataMaxSize (parameter name: "metadata_max_size")
-7. metadataSlotCount (parameter name: "metadata_slot_count")
-8. blockDeviceInfoName (parameter name: "block_device_info_name")
-9. numSectors (parameter name: "num_sectors")
-10. physicalSector (parameter name: "physical_sector")
-11. sparsify (parameter name: "sparsify")
-12. buffer (parameter name: "data")
+7. blockDeviceInfoName (parameter name: "block_device_info_name")
+8. numSectors (parameter name: "num_sectors")
+9. physicalSector (parameter name: "physical_sector")
+10. sparsify (parameter name: "sparsify")
+11. buffer (parameter name: "data")
| Parameter| Valid Values| Configured Value|
|------------- |-------------| ----- |
@@ -117,7 +112,6 @@
|`logicalBlockSize`| Integer |Value obtained from FuzzedDataProvider|
|`blockDevSize`| Integer value in multiples of `LP_SECTOR_SIZE`|Value obtained from FuzzedDataProvider|
|`metadataMaxSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|
-|`metadataSlotCount`| Integer value from `0` to `2` |Value obtained from FuzzedDataProvider|
|`blockDeviceInfoName`| String |Value obtained from FuzzedDataProvider|
|`numSectors`| Integer value from `1` to `1000000` |Value obtained from FuzzedDataProvider|
|`physicalSector`| Integer value from `1` to `1000000` |Value obtained from FuzzedDataProvider|
diff --git a/fs_mgr/liblp/fuzzer/liblp_apis_fuzzer.cpp b/fs_mgr/liblp/fuzzer/liblp_apis_fuzzer.cpp
index a15bc89..cb192ea 100644
--- a/fs_mgr/liblp/fuzzer/liblp_apis_fuzzer.cpp
+++ b/fs_mgr/liblp/fuzzer/liblp_apis_fuzzer.cpp
@@ -39,8 +39,6 @@
static constexpr uint32_t kValidLogicalBlockSize = 4096;
static constexpr uint32_t kMinMetadataSize = 0;
static constexpr uint32_t kMaxMetadataSize = 10000;
-static constexpr uint32_t kMinSlot = 0;
-static constexpr uint32_t kMaxSlot = 10;
static constexpr uint32_t kMinFactor = 0;
static constexpr uint32_t kMaxFactor = 10;
static constexpr uint32_t kMetadataGeometrySize = 4096;
@@ -108,7 +106,7 @@
uint32_t randomMetadataMaxSize =
mFdp.ConsumeIntegralInRange<uint32_t>(kMinMetadataSize, kMaxMetadataSize);
uint32_t metadataMaxSize = mFdp.ConsumeBool() ? kMetadataSize : randomMetadataMaxSize;
- uint32_t metadataSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(kMinSlot, kMaxSlot);
+ uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;
mBuilder = MetadataBuilder::New(blockDevSize, metadataMaxSize, metadataSlotCount);
if (mBuilder.get()) {
@@ -128,14 +126,15 @@
Partition* super = mBuilder->AddPartition(mSuperPartitionName, LP_PARTITION_ATTR_READONLY);
mBuilder->AddPartition(mPartitionName, LP_PARTITION_ATTR_READONLY);
- int64_t numSectors = mFdp.ConsumeBool() ? mFdp.ConsumeIntegralInRange<uint64_t>(
- kMinSectorValue, kMaxSectorValue)
- : kValidNumSectors;
- int64_t physicalSector = mFdp.ConsumeBool() ? mFdp.ConsumeIntegralInRange<uint64_t>(
+ if (super) {
+ int64_t numSectors = mFdp.ConsumeBool() ? mFdp.ConsumeIntegralInRange<uint64_t>(
kMinSectorValue, kMaxSectorValue)
- : kValidPhysicalSector;
-
- mBuilder->AddLinearExtent(super, mBlockDeviceInfoName, numSectors, physicalSector);
+ : kValidNumSectors;
+ int64_t physicalSector = mFdp.ConsumeBool() ? mFdp.ConsumeIntegralInRange<uint64_t>(
+ kMinSectorValue, kMaxSectorValue)
+ : kValidPhysicalSector;
+ mBuilder->AddLinearExtent(super, mBlockDeviceInfoName, numSectors, physicalSector);
+ }
}
}
diff --git a/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp b/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp
index e5fbe27..7f09ac8 100644
--- a/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp
+++ b/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp
@@ -48,8 +48,6 @@
static constexpr uint32_t kZeroAlignment = 0;
static constexpr uint32_t kZeroAlignmentOffset = 0;
static constexpr uint32_t kMaxBytes = 20;
-static constexpr uint32_t kMinSlot = 0;
-static constexpr uint32_t kMaxSlot = 10;
static constexpr uint32_t kMinBuilder = 0;
static constexpr uint32_t kMaxBuilder = 4;
@@ -108,9 +106,7 @@
switch (randomBuilder) {
case 0: {
uint32_t maxMetadataSize = getParamValue(kValidMaxMetadataSize);
- uint32_t numSlots = mFdp.ConsumeBool()
- ? kMaxSlot
- : mFdp.ConsumeIntegralInRange<uint32_t>(kMinSlot, kMaxSlot);
+ uint32_t numSlots = mFdp.ConsumeBool() ? 0 : 1;
mBuilder = MetadataBuilder::New(mBlockDevices, superBlockDeviceName, maxMetadataSize,
numSlots);
break;
@@ -120,14 +116,14 @@
mFdp.ConsumeIntegralInRange<uint64_t>(kMinBlockDevValue, kMaxBlockDevValue);
uint32_t metadataMaxSize =
mFdp.ConsumeIntegralInRange<uint32_t>(kMinMetadataValue, kMaxMetadataValue);
- uint32_t metadataSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(kMinSlot, kMaxSlot);
+ uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;
mBuilder = MetadataBuilder::New(blockDevSize, metadataMaxSize, metadataSlotCount);
break;
}
case 2: {
uint64_t blockDevSize = getParamValue(kValidBlockSize);
uint32_t metadataSize = getParamValue(kValidMetadataSize);
- uint32_t metadataSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(kMinSlot, kMaxSlot);
+ uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;
mBuilder = MetadataBuilder::New(blockDevSize, metadataSize, metadataSlotCount);
break;
}
@@ -213,13 +209,13 @@
void BuilderFuzzer::callVerifyExtentsAgainstSourceMetadata() {
uint64_t sourceBlockDevSize = getParamValue(kValidBlockSize);
uint32_t sourceMetadataMaxSize = getParamValue(kValidMetadataSize);
- uint32_t sourceSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(0, 2);
+ uint32_t sourceSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(0, 1);
auto sourceBuilder =
MetadataBuilder::New(sourceBlockDevSize, sourceMetadataMaxSize, sourceSlotCount);
uint64_t targetBlockDevSize = getParamValue(kValidBlockSize);
uint32_t targetMetadataMaxSize = getParamValue(kValidMetadataSize);
- uint32_t targetSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(0, 2);
+ uint32_t targetSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(0, 1);
auto targetBuilder =
MetadataBuilder::New(targetBlockDevSize, targetMetadataMaxSize, targetSlotCount);
@@ -292,17 +288,20 @@
int64_t numExtents =
mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);
- bool extentAdded = false;
- for (int64_t i = 0; i <= numExtents; ++i) {
- extentAdded = mBuilder->AddLinearExtent(mFuzzPartition, kDeviceInfoName,
- numSectors, physicalSector);
- }
+ if (mFuzzPartition) {
+ bool extentAdded = false;
+ for (int64_t i = 0; i <= numExtents; ++i) {
+ extentAdded =
+ mBuilder->AddLinearExtent(mFuzzPartition, kDeviceInfoName,
+ numSectors, physicalSector);
+ }
- if (extentAdded) {
- unique_ptr<LpMetadata> metadata = mBuilder->Export();
- uint64_t alignedSize =
- mFdp.ConsumeIntegralInRange<uint64_t>(kMinValue, kMaxValue);
- mFuzzPartition->GetBeginningExtents(LP_SECTOR_SIZE * numExtents);
+ if (extentAdded) {
+ unique_ptr<LpMetadata> metadata = mBuilder->Export();
+ uint64_t alignedSize =
+ mFdp.ConsumeIntegralInRange<uint64_t>(kMinValue, kMaxValue);
+ mFuzzPartition->GetBeginningExtents(LP_SECTOR_SIZE * numExtents);
+ }
}
},
[&]() { callVerifyExtentsAgainstSourceMetadata(); },
diff --git a/fs_mgr/liblp/fuzzer/liblp_super_layout_builder_fuzzer.cpp b/fs_mgr/liblp/fuzzer/liblp_super_layout_builder_fuzzer.cpp
index 887093c..a6642d7 100644
--- a/fs_mgr/liblp/fuzzer/liblp_super_layout_builder_fuzzer.cpp
+++ b/fs_mgr/liblp/fuzzer/liblp_super_layout_builder_fuzzer.cpp
@@ -38,8 +38,6 @@
static constexpr uint32_t kMinMetadataValue = 0;
static constexpr uint32_t kMaxMetadataValue = 10000;
static constexpr uint32_t kMaxBytes = 20;
-static constexpr uint32_t kMinSlot = 0;
-static constexpr uint32_t kMaxSlot = 10;
static constexpr uint32_t kMinOpen = 0;
static constexpr uint32_t kMaxOpen = 2;
@@ -58,14 +56,14 @@
void invokeSuperLayoutBuilderAPIs();
void callRandomOpen(int32_t open);
void addMultiplePartitions(int32_t numPartitions);
- void setupSuperLayoutBuilder(string fuzzPartitionName);
+ void setupSuperLayoutBuilder();
SuperLayoutBuilder mSuperLayoutBuilder;
unique_ptr<MetadataBuilder> mSuperBuilder;
unique_ptr<LpMetadata> mMetadata;
bool mOpenSuccess = false;
};
-void SuperLayoutBuilderFuzzer::setupSuperLayoutBuilder(string fuzzPartitionName) {
+void SuperLayoutBuilderFuzzer::setupSuperLayoutBuilder() {
uint64_t randomBlockDevSize =
mFdp.ConsumeIntegralInRange<uint64_t>(kMinBlockDevValue, kMaxBlockDevValue);
uint64_t blockDevSize = mFdp.ConsumeBool() ? kSuperLayoutValidBlockDevSize : randomBlockDevSize;
@@ -73,7 +71,7 @@
mFdp.ConsumeIntegralInRange<uint32_t>(kMinMetadataValue, kMaxMetadataValue);
uint32_t metadataMaxSize =
mFdp.ConsumeBool() ? kSuperLayoutValidMetadataSize : randomMetadataMaxSize;
- uint32_t metadataSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(kMinSlot, kMaxSlot);
+ uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;
mSuperBuilder = MetadataBuilder::New(blockDevSize, metadataMaxSize, metadataSlotCount);
if (mSuperBuilder.get()) {
@@ -85,10 +83,6 @@
uint32_t randomOpen = mFdp.ConsumeIntegralInRange<uint32_t>(kMinOpen, kMaxOpen);
callRandomOpen(randomOpen);
-
- if (!fuzzPartitionName.size()) {
- fuzzPartitionName = "builder_partition";
- }
}
}
@@ -125,7 +119,10 @@
string imageName = mFdp.ConsumeRandomLengthString(kMaxBytes);
string fuzzPartitionName =
mFdp.ConsumeBool() ? "builder_partition" : mFdp.ConsumeRandomLengthString(kMaxBytes);
- setupSuperLayoutBuilder(fuzzPartitionName);
+ if (!fuzzPartitionName.size()) {
+ fuzzPartitionName = "builder_partition";
+ }
+ setupSuperLayoutBuilder();
if (mOpenSuccess) {
while (mFdp.remaining_bytes()) {
auto invokeSuperAPIs = mFdp.PickValueInArray<const function<void()>>({
diff --git a/fs_mgr/liblp/super_layout_builder.cpp b/fs_mgr/liblp/super_layout_builder.cpp
index 5349e41..fd7416b 100644
--- a/fs_mgr/liblp/super_layout_builder.cpp
+++ b/fs_mgr/liblp/super_layout_builder.cpp
@@ -17,6 +17,8 @@
#include <liblp/liblp.h>
+#include <algorithm>
+
#include "images.h"
#include "utility.h"
#include "writer.h"
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index d8e171b..70c7b79 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -25,6 +25,7 @@
#include <sys/ioctl.h>
#endif
+#include <algorithm>
#include <map>
#include <string>
#include <vector>
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index ac58ba0..a8a7716 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -420,6 +420,7 @@
},
data: [
"tools/testdata/cow_v2",
+ "tools/testdata/incompressible_block",
],
auto_gen_config: true,
require_root: false,
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 91e0425..6b34152 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -16,8 +16,10 @@
#include <stdint.h>
+#include <limits>
#include <optional>
#include <string_view>
+#include <type_traits>
namespace android {
namespace snapshot {
@@ -32,7 +34,7 @@
static constexpr uint32_t kCowVersionManifest = 2;
static constexpr uint32_t kMinCowVersion = 1;
-static constexpr uint32_t kMaxCowVersion = 2;
+static constexpr uint32_t kMaxCowVersion = 3;
// Normally, this should be kMaxCowVersion. When a new version is under testing
// it may be the previous value of kMaxCowVersion.
@@ -94,11 +96,23 @@
} __attribute__((packed));
+// Resume point structure used for resume buffer
+struct ResumePoint {
+ // monotonically increasing value used by update_engine
+ uint64_t label;
+ // Index of last CowOperation guaranteed to be resumable
+ uint32_t op_index;
+} __attribute__((packed));
+
+static constexpr uint8_t kNumResumePoints = 4;
+
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;
+ // Number of sequence data stored (each of which is a 32 byte integer)
+ uint64_t sequence_data_count;
+ // Number of currently written resume points &&
+ uint32_t resume_point_count;
+ // Number of max resume points that can be written
+ uint32_t resume_point_max;
// Number of CowOperationV3 structs in the operation buffer, currently and total
// region size.
uint32_t op_count;
@@ -107,10 +121,30 @@
uint32_t compression_algorithm;
} __attribute__((packed));
+enum class CowOperationType : uint8_t {
+ kCowCopyOp = 1,
+ kCowReplaceOp = 2,
+ kCowZeroOp = 3,
+ kCowLabelOp = 4,
+ kCowClusterOp = 5,
+ kCowXorOp = 6,
+ kCowSequenceOp = 7,
+ kCowFooterOp = std::numeric_limits<uint8_t>::max(),
+};
+
+static constexpr CowOperationType kCowCopyOp = CowOperationType::kCowCopyOp;
+static constexpr CowOperationType kCowReplaceOp = CowOperationType::kCowReplaceOp;
+static constexpr CowOperationType kCowZeroOp = CowOperationType::kCowZeroOp;
+static constexpr CowOperationType kCowLabelOp = CowOperationType::kCowLabelOp;
+static constexpr CowOperationType kCowClusterOp = CowOperationType::kCowClusterOp;
+static constexpr CowOperationType kCowXorOp = CowOperationType::kCowXorOp;
+static constexpr CowOperationType kCowSequenceOp = CowOperationType::kCowSequenceOp;
+static constexpr CowOperationType kCowFooterOp = CowOperationType::kCowFooterOp;
+
// This structure is the same size of a normal Operation, but is repurposed for the footer.
struct CowFooterOperation {
// The operation code (always kCowFooterOp).
- uint8_t type;
+ CowOperationType type;
// If this operation reads from the data section of the COW, this contains
// the compression type of that data (see constants below).
@@ -129,7 +163,7 @@
// V2 version of COW. On disk format for older devices
struct CowOperationV2 {
// The operation code (see the constants and structures below).
- uint8_t type;
+ CowOperationType type;
// If this operation reads from the data section of the COW, this contains
// the compression type of that data (see constants below).
@@ -161,11 +195,12 @@
uint64_t source;
} __attribute__((packed));
+static constexpr uint64_t kCowOpSourceInfoDataMask = (1ULL << 48) - 1;
+static constexpr uint64_t kCowOpSourceInfoTypeBit = 60;
+static constexpr uint64_t kCowOpSourceInfoTypeNumBits = 4;
+static constexpr uint64_t kCowOpSourceInfoTypeMask = (1ULL << kCowOpSourceInfoTypeNumBits) - 1;
// 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;
@@ -173,6 +208,10 @@
// The block of data in the new image that this operation modifies.
uint32_t new_block;
+ // source_info with have the following layout
+ // |---4 bits ---| ---12 bits---| --- 48 bits ---|
+ // |--- type --- | -- unused -- | --- source --- |
+ //
// The value of |source| depends on the operation code.
//
// CopyOp: a 32-bit block location in the source image.
@@ -184,20 +223,34 @@
// 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;
+ uint64_t source_info_;
+ constexpr uint64_t source() const { return source_info_ & kCowOpSourceInfoDataMask; }
+ constexpr void set_source(uint64_t source) {
+ // Clear the first 48 bit first
+ source_info_ &= ~kCowOpSourceInfoDataMask;
+ // Set the actual source field
+ source_info_ |= source & kCowOpSourceInfoDataMask;
+ }
+ constexpr CowOperationType type() const {
+ // this is a mask to grab the first 4 bits of a 64 bit integer
+ const auto type = (source_info_ >> kCowOpSourceInfoTypeBit) & kCowOpSourceInfoTypeMask;
+ return static_cast<CowOperationType>(type);
+ }
+ constexpr void set_type(CowOperationType type) {
+ // Clear the top 4 bits first
+ source_info_ &= ((1ULL << kCowOpSourceInfoTypeBit) - 1);
+ // set the actual type bits
+ source_info_ |= (static_cast<uint64_t>(type) & kCowOpSourceInfoTypeMask)
+ << kCowOpSourceInfoTypeBit;
+ }
} __attribute__((packed));
+// Ensure that getters/setters added to CowOperationV3 does not increases size
+// of CowOperationV3 struct(no virtual method tables added).
+static_assert(std::is_trivially_copyable_v<CowOperationV3>);
+static_assert(std::is_standard_layout_v<CowOperationV3>);
static_assert(sizeof(CowOperationV2) == sizeof(CowFooterOperation));
-static constexpr uint8_t kCowCopyOp = 1;
-static constexpr uint8_t kCowReplaceOp = 2;
-static constexpr uint8_t kCowZeroOp = 3;
-static constexpr uint8_t kCowLabelOp = 4;
-static constexpr uint8_t kCowClusterOp = 5;
-static constexpr uint8_t kCowXorOp = 6;
-static constexpr uint8_t kCowSequenceOp = 7;
-static constexpr uint8_t kCowFooterOp = -1;
-
enum CowCompressionAlgorithm : uint8_t {
kCowCompressNone = 0,
kCowCompressGz = 1,
@@ -214,10 +267,21 @@
static constexpr uint8_t kCowReadAheadInProgress = 1;
static constexpr uint8_t kCowReadAheadDone = 2;
-static constexpr uint64_t kCowOpSourceInfoDataMask = (1ULL << 48) - 1;
+static constexpr off_t GetSequenceOffset(const CowHeaderV3& header) {
+ return header.prefix.header_size + header.buffer_size;
+}
-static inline uint64_t GetCowOpSourceInfoData(const CowOperation& op) {
- return op.source_info & kCowOpSourceInfoDataMask;
+static constexpr off_t GetResumeOffset(const CowHeaderV3& header) {
+ return GetSequenceOffset(header) + (header.sequence_data_count * sizeof(uint32_t));
+}
+
+static constexpr off_t GetOpOffset(uint32_t op_index, const CowHeaderV3& header) {
+ return GetResumeOffset(header) + (header.resume_point_max * sizeof(ResumePoint)) +
+ (op_index * sizeof(CowOperationV3));
+}
+
+static constexpr off_t GetDataOffset(const CowHeaderV3& header) {
+ return GetOpOffset(header.op_count_max, header);
}
struct CowFooter {
@@ -243,7 +307,9 @@
std::ostream& operator<<(std::ostream& os, CowOperationV2 const& arg);
-std::ostream& operator<<(std::ostream& os, CowOperation const& arg);
+std::ostream& operator<<(std::ostream& os, CowOperationV3 const& arg);
+
+std::ostream& operator<<(std::ostream& os, ResumePoint const& arg);
int64_t GetNextOpOffset(const CowOperationV2& op, uint32_t cluster_size);
int64_t GetNextDataOffset(const CowOperationV2& op, uint32_t cluster_size);
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index 755a3af..bf4c79f 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -16,7 +16,6 @@
#include <stdint.h>
-#include <functional>
#include <memory>
#include <optional>
#include <unordered_map>
@@ -168,6 +167,12 @@
private:
bool ParseV2(android::base::borrowed_fd fd, std::optional<uint64_t> label);
bool PrepMergeOps();
+ // sequence data is stored as an operation with actual data residing in the data offset.
+ bool GetSequenceDataV2(std::vector<uint32_t>* merge_op_blocks, std::vector<int>* other_ops,
+ std::unordered_map<uint32_t, int>* block_map);
+ // v3 of the cow writes sequence data within its own separate sequence buffer.
+ bool GetSequenceData(std::vector<uint32_t>* merge_op_blocks, std::vector<int>* other_ops,
+ std::unordered_map<uint32_t, int>* block_map);
uint64_t FindNumCopyops();
uint8_t GetCompressionType();
@@ -183,7 +188,7 @@
uint64_t num_total_data_ops_{};
uint64_t num_ordered_ops_to_merge_{};
bool has_seq_ops_{};
- std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
+ std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> xor_data_loc_;
ReaderFlags reader_flag_;
bool is_merge_{};
};
@@ -192,6 +197,5 @@
// The extra fields will just be filled as 0. V3 header is strictly a superset of v1/v2 header and
// contains all of the latter's field
bool ReadCowHeader(android::base::borrowed_fd fd, CowHeaderV3* header);
-
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 08a79ba..d102863 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -826,6 +826,9 @@
bool DeleteDeviceIfExists(const std::string& name,
const std::chrono::milliseconds& timeout_ms = {});
+ // Set read-ahead size during OTA
+ void SetReadAheadSize(const std::string& entry_block_device, off64_t size_kb);
+
android::dm::IDeviceMapper& dm_;
std::unique_ptr<IDeviceInfo> device_;
std::string metadata_dir_;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
index b905291..2c4d40f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
@@ -30,7 +30,7 @@
using android::base::unique_fd;
-std::ostream& EmitCowTypeString(std::ostream& os, uint8_t cow_type) {
+std::ostream& EmitCowTypeString(std::ostream& os, CowOperationType cow_type) {
switch (cow_type) {
case kCowCopyOp:
return os << "kCowCopyOp";
@@ -80,26 +80,30 @@
return os;
}
-std::ostream& operator<<(std::ostream& os, CowOperation const& op) {
+std::ostream& operator<<(std::ostream& os, CowOperationV3 const& op) {
os << "CowOperation(";
- EmitCowTypeString(os, op.type);
- if (op.type == kCowReplaceOp || op.type == kCowXorOp || op.type == kCowSequenceOp) {
+ 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) {
+ 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);
+ if (op.type() == kCowXorOp || op.type() == kCowReplaceOp || op.type() == kCowCopyOp) {
+ os << ", source:" << op.source();
+ } else if (op.type() == kCowClusterOp) {
+ os << ", cluster_data:" << op.source();
}
+ // V3 op stores resume points in header, so CowOp can never be Label.
os << ")";
return os;
}
+std::ostream& operator<<(std::ostream& os, ResumePoint const& resume_point) {
+ os << "ResumePoint(" << resume_point.label << " , " << resume_point.op_index << ")";
+ return os;
+}
+
int64_t GetNextOpOffset(const CowOperationV2& op, uint32_t cluster_ops) {
if (op.type == kCowClusterOp) {
return op.source;
@@ -121,7 +125,7 @@
}
bool IsMetadataOp(const CowOperation& op) {
- switch (op.type) {
+ switch (op.type()) {
case kCowLabelOp:
case kCowClusterOp:
case kCowFooterOp:
@@ -133,7 +137,7 @@
}
bool IsOrderedOp(const CowOperation& op) {
- switch (op.type) {
+ switch (op.type()) {
case kCowCopyOp:
case kCowXorOp:
return true;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index 93c31bb..1b4a971 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -24,6 +24,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <libsnapshot/cow_format.h>
#include <libsnapshot/cow_reader.h>
#include <zlib.h>
@@ -82,7 +83,7 @@
cow->merge_op_start_ = merge_op_start_;
cow->num_total_data_ops_ = num_total_data_ops_;
cow->num_ordered_ops_to_merge_ = num_ordered_ops_to_merge_;
- cow->data_loc_ = data_loc_;
+ cow->xor_data_loc_ = xor_data_loc_;
cow->block_pos_index_ = block_pos_index_;
cow->is_merge_ = is_merge_;
return cow;
@@ -139,7 +140,6 @@
LOG(ERROR) << "Unknown version: " << header_.prefix.major_version;
return false;
}
-
if (!parser->Parse(fd, header_, label)) {
return false;
}
@@ -154,7 +154,7 @@
footer_ = parser->footer();
fd_size_ = parser->fd_size();
last_label_ = parser->last_label();
- data_loc_ = parser->data_loc();
+ xor_data_loc_ = parser->xor_data_loc();
// If we're resuming a write, we're not ready to merge
if (label.has_value()) return true;
@@ -266,52 +266,31 @@
// Replace-op-4, Zero-op-9, Replace-op-5 }
//==============================================================
bool CowReader::PrepMergeOps() {
- auto merge_op_blocks = std::make_unique<std::vector<uint32_t>>();
std::vector<int> other_ops;
- auto seq_ops_set = std::unordered_set<uint32_t>();
- auto block_map = std::make_unique<std::unordered_map<uint32_t, int>>();
- size_t num_seqs = 0;
- size_t read;
+ std::vector<uint32_t> merge_op_blocks;
+ std::unordered_map<uint32_t, int> block_map;
- for (size_t i = 0; i < ops_->size(); i++) {
- auto& current_op = ops_->data()[i];
-
- if (current_op.type == kCowSequenceOp) {
- size_t seq_len = current_op.data_length / sizeof(uint32_t);
-
- merge_op_blocks->resize(merge_op_blocks->size() + seq_len);
- if (!GetRawBytes(¤t_op, &merge_op_blocks->data()[num_seqs],
- current_op.data_length, &read)) {
- PLOG(ERROR) << "Failed to read sequence op!";
- return false;
- }
- for (size_t j = num_seqs; j < num_seqs + seq_len; j++) {
- seq_ops_set.insert(merge_op_blocks->data()[j]);
- }
- num_seqs += seq_len;
- }
-
- if (IsMetadataOp(current_op)) {
- continue;
- }
-
- // Sequence ops must be the first ops in the stream.
- if (seq_ops_set.empty() && IsOrderedOp(current_op)) {
- merge_op_blocks->emplace_back(current_op.new_block);
- } else if (seq_ops_set.count(current_op.new_block) == 0) {
- other_ops.push_back(current_op.new_block);
- }
- block_map->insert({current_op.new_block, i});
+ switch (header_.prefix.major_version) {
+ case 1:
+ case 2:
+ GetSequenceDataV2(&merge_op_blocks, &other_ops, &block_map);
+ break;
+ case 3:
+ GetSequenceData(&merge_op_blocks, &other_ops, &block_map);
+ break;
+ default:
+ break;
}
- for (auto block : *merge_op_blocks) {
- if (block_map->count(block) == 0) {
+
+ for (auto block : merge_op_blocks) {
+ if (block_map.count(block) == 0) {
LOG(ERROR) << "Invalid Sequence Ops. Could not find Cow Op for new block " << block;
return false;
}
}
- if (merge_op_blocks->size() > header_.num_merge_ops) {
- num_ordered_ops_to_merge_ = merge_op_blocks->size() - header_.num_merge_ops;
+ if (merge_op_blocks.size() > header_.num_merge_ops) {
+ num_ordered_ops_to_merge_ = merge_op_blocks.size() - header_.num_merge_ops;
} else {
num_ordered_ops_to_merge_ = 0;
}
@@ -327,9 +306,9 @@
std::sort(other_ops.begin(), other_ops.end(), std::greater<int>());
}
- merge_op_blocks->insert(merge_op_blocks->end(), other_ops.begin(), other_ops.end());
+ merge_op_blocks.insert(merge_op_blocks.end(), other_ops.begin(), other_ops.end());
- num_total_data_ops_ = merge_op_blocks->size();
+ num_total_data_ops_ = merge_op_blocks.size();
if (header_.num_merge_ops > 0) {
merge_op_start_ = header_.num_merge_ops;
}
@@ -339,24 +318,94 @@
// the ops vector as required for merge operations.
auto merge_ops_buffer = std::make_shared<std::vector<CowOperation>>();
merge_ops_buffer->reserve(num_total_data_ops_);
- for (auto block : *merge_op_blocks) {
- merge_ops_buffer->emplace_back(ops_->data()[block_map->at(block)]);
+ for (auto block : merge_op_blocks) {
+ merge_ops_buffer->emplace_back(ops_->data()[block_map.at(block)]);
}
ops_->clear();
ops_ = merge_ops_buffer;
ops_->shrink_to_fit();
} else {
- for (auto block : *merge_op_blocks) {
- block_pos_index_->push_back(block_map->at(block));
+ for (auto block : merge_op_blocks) {
+ block_pos_index_->push_back(block_map.at(block));
}
}
- block_map->clear();
- merge_op_blocks->clear();
+ block_map.clear();
+ merge_op_blocks.clear();
return true;
}
+bool CowReader::GetSequenceDataV2(std::vector<uint32_t>* merge_op_blocks,
+ std::vector<int>* other_ops,
+ std::unordered_map<uint32_t, int>* block_map) {
+ auto seq_ops_set = std::unordered_set<uint32_t>();
+ size_t num_seqs = 0;
+ size_t read;
+ for (size_t i = 0; i < ops_->size(); i++) {
+ auto& current_op = ops_->data()[i];
+
+ if (current_op.type() == kCowSequenceOp) {
+ size_t seq_len = current_op.data_length / sizeof(uint32_t);
+
+ merge_op_blocks->resize(merge_op_blocks->size() + seq_len);
+ if (!GetRawBytes(¤t_op, &merge_op_blocks->data()[num_seqs],
+ current_op.data_length, &read)) {
+ PLOG(ERROR) << "Failed to read sequence op!";
+ return false;
+ }
+ for (size_t j = num_seqs; j < num_seqs + seq_len; j++) {
+ seq_ops_set.insert(merge_op_blocks->at(j));
+ }
+ num_seqs += seq_len;
+ }
+
+ if (IsMetadataOp(current_op)) {
+ continue;
+ }
+
+ // Sequence ops must be the first ops in the stream.
+ if (seq_ops_set.empty() && IsOrderedOp(current_op)) {
+ merge_op_blocks->emplace_back(current_op.new_block);
+ } else if (seq_ops_set.count(current_op.new_block) == 0) {
+ other_ops->push_back(current_op.new_block);
+ }
+ block_map->insert({current_op.new_block, i});
+ }
+ return false;
+}
+
+bool CowReader::GetSequenceData(std::vector<uint32_t>* merge_op_blocks, std::vector<int>* other_ops,
+ std::unordered_map<uint32_t, int>* block_map) {
+ std::unordered_set<uint32_t> seq_ops_set;
+ // read sequence ops data
+ merge_op_blocks->resize(header_.sequence_data_count);
+ if (!android::base::ReadFullyAtOffset(
+ fd_, merge_op_blocks->data(),
+ header_.sequence_data_count * sizeof(merge_op_blocks->at(0)),
+ GetSequenceOffset(header_))) {
+ PLOG(ERROR) << "failed to read sequence buffer. seq_data_count: "
+ << header_.sequence_data_count << " at offset: " << GetSequenceOffset(header_);
+ return false;
+ }
+ seq_ops_set.reserve(merge_op_blocks->size());
+ for (auto& i : *merge_op_blocks) {
+ seq_ops_set.insert(i);
+ }
+ // read ordered op data
+ for (size_t i = 0; i < ops_->size(); i++) {
+ auto& current_op = ops_->data()[i];
+ // Sequence ops must be the first ops in the stream.
+ if (seq_ops_set.empty()) {
+ merge_op_blocks->emplace_back(current_op.new_block);
+ } else if (seq_ops_set.count(current_op.new_block) == 0) {
+ other_ops->push_back(current_op.new_block);
+ }
+ block_map->insert({current_op.new_block, i});
+ }
+ return true;
+}
+
bool CowReader::VerifyMergeOps() {
auto itr = GetMergeOpIter(true);
std::unordered_map<uint64_t, const CowOperation*> overwritten_blocks;
@@ -588,11 +637,11 @@
}
bool CowReader::GetRawBytes(const CowOperation* op, void* buffer, size_t len, size_t* read) {
- switch (op->type) {
+ switch (op->type()) {
case kCowSequenceOp:
case kCowReplaceOp:
case kCowXorOp:
- return GetRawBytes(GetCowOpSourceInfoData(*op), buffer, len, read);
+ return GetRawBytes(op->source(), buffer, len, read);
default:
LOG(ERROR) << "Cannot get raw bytes of non-data op: " << *op;
return false;
@@ -681,13 +730,13 @@
}
uint64_t offset;
- if (op->type == kCowXorOp) {
- offset = data_loc_->at(op->new_block);
+ if (op->type() == kCowXorOp) {
+ offset = xor_data_loc_->at(op->new_block);
} else {
- offset = GetCowOpSourceInfoData(*op);
+ offset = op->source();
}
-
- if (!decompressor) {
+ if (!decompressor ||
+ ((op->data_length == header_.block_size) && (header_.prefix.major_version == 3))) {
CowDataStream stream(this, offset + ignore_bytes, op->data_length - ignore_bytes);
return stream.ReadFully(buffer, buffer_size);
}
@@ -698,12 +747,12 @@
}
bool CowReader::GetSourceOffset(const CowOperation* op, uint64_t* source_offset) {
- switch (op->type) {
+ switch (op->type()) {
case kCowCopyOp:
- *source_offset = GetCowOpSourceInfoData(*op) * header_.block_size;
+ *source_offset = op->source() * header_.block_size;
return true;
case kCowXorOp:
- *source_offset = GetCowOpSourceInfoData(*op);
+ *source_offset = op->source();
return true;
default:
return false;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
index 76509de..3718851 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
@@ -82,7 +82,8 @@
}
for (const auto& op : *parser.get_v2ops()) {
std::cout << op << "\n";
- if (auto iter = parser.data_loc()->find(op.new_block); iter != parser.data_loc()->end()) {
+ if (auto iter = parser.xor_data_loc()->find(op.new_block);
+ iter != parser.xor_data_loc()->end()) {
std::cout << " data loc: " << iter->second << "\n";
}
}
@@ -198,7 +199,7 @@
if (!FLAGS_silent && FLAGS_show_ops) std::cout << *op << "\n";
- if ((FLAGS_decompress || extract_to >= 0) && op->type == kCowReplaceOp) {
+ 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;
@@ -212,12 +213,12 @@
return false;
}
}
- } else if (extract_to >= 0 && !IsMetadataOp(*op) && op->type != kCowZeroOp) {
+ } 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) {
+ if (op->type() == kCowSequenceOp && FLAGS_show_merge_sequence) {
size_t read;
std::vector<uint32_t> merge_op_blocks;
size_t seq_len = op->data_length / sizeof(uint32_t);
@@ -235,13 +236,13 @@
}
}
- if (op->type == kCowCopyOp) {
+ if (op->type() == kCowCopyOp) {
copy_ops++;
- } else if (op->type == kCowReplaceOp) {
+ } else if (op->type() == kCowReplaceOp) {
replace_ops++;
- } else if (op->type == kCowZeroOp) {
+ } else if (op->type() == kCowZeroOp) {
zero_ops++;
- } else if (op->type == kCowXorOp) {
+ } else if (op->type() == kCowXorOp) {
xor_ops++;
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h
index 2783407..837b33e 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h
@@ -38,7 +38,9 @@
std::optional<uint64_t> label = {}) = 0;
virtual bool Translate(TranslatedCowOps* out) = 0;
virtual std::optional<CowFooter> footer() const { return std::nullopt; }
- virtual std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc() const = 0;
+ std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> xor_data_loc() {
+ return xor_data_loc_;
+ };
uint64_t fd_size() const { return fd_size_; }
const std::optional<uint64_t>& last_label() const { return last_label_; }
@@ -47,6 +49,7 @@
CowHeaderV3 header_ = {};
uint64_t fd_size_;
std::optional<uint64_t> last_label_;
+ std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> xor_data_loc_ = {};
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
index 51489c8..fe977b7 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
@@ -63,7 +63,7 @@
bool CowParserV2::ParseOps(borrowed_fd fd, std::optional<uint64_t> label) {
uint64_t pos;
- auto data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
+ auto xor_data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
// Skip the scratch space
if (header_.prefix.major_version >= 2 && (header_.buffer_size > 0)) {
@@ -111,7 +111,7 @@
auto& current_op = ops_buffer->data()[current_op_num];
current_op_num++;
if (current_op.type == kCowXorOp) {
- data_loc->insert({current_op.new_block, data_pos});
+ xor_data_loc->insert({current_op.new_block, data_pos});
}
pos += sizeof(CowOperationV2) + GetNextOpOffset(current_op, header_.cluster_ops);
data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);
@@ -193,7 +193,7 @@
v2_ops_ = ops_buffer;
v2_ops_->shrink_to_fit();
- data_loc_ = data_loc;
+ xor_data_loc_ = xor_data_loc;
return true;
}
@@ -205,7 +205,7 @@
const auto& v2_op = v2_ops_->at(i);
auto& new_op = out->ops->at(i);
- new_op.type = v2_op.type;
+ new_op.set_type(v2_op.type);
new_op.data_length = v2_op.data_length;
if (v2_op.new_block > std::numeric_limits<uint32_t>::max()) {
@@ -215,7 +215,7 @@
new_op.new_block = v2_op.new_block;
uint64_t source_info = v2_op.source;
- if (new_op.type != kCowLabelOp) {
+ 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;
@@ -232,16 +232,12 @@
return false;
}
}
- new_op.source_info = source_info;
+ new_op.set_source(source_info);
}
out->header = header_;
return true;
}
-std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> CowParserV2::data_loc() const {
- return data_loc_;
-}
-
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
index 318a3ac..f9ee2e5 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
@@ -35,10 +35,8 @@
const CowHeader& header() const { return header_; }
std::shared_ptr<std::vector<CowOperationV2>> get_v2ops() { return v2_ops_; }
- std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc() const override;
private:
- std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
bool ParseOps(android::base::borrowed_fd fd, std::optional<uint64_t> label);
std::shared_ptr<std::vector<CowOperationV2>> v2_ops_;
std::optional<CowFooter> footer_;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp
index 23ddaae..ce68b39 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp
@@ -15,8 +15,10 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/strings.h>
#include <libsnapshot/cow_format.h>
+#include <libsnapshot/cow_reader.h>
namespace android {
namespace snapshot {
@@ -36,6 +38,7 @@
LOG(ERROR) << "Footer size isn't 0, read " << header_.footer_size;
return false;
}
+
if (header_.op_size != sizeof(CowOperationV3)) {
LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
<< sizeof(CowOperationV3);
@@ -55,26 +58,71 @@
return false;
}
- return ParseOps(fd, label);
+ std::optional<uint32_t> op_index = header_.op_count;
+ if (label) {
+ if (!ReadResumeBuffer(fd)) {
+ PLOG(ERROR) << "Failed to read resume buffer";
+ return false;
+ }
+ op_index = FindResumeOp(label.value());
+ if (op_index == std::nullopt) {
+ LOG(ERROR) << "failed to get op index from given label: " << label.value();
+ return false;
+ }
+ }
+
+ return ParseOps(fd, op_index.value());
}
-bool CowParserV3::ParseOps(borrowed_fd fd, std::optional<uint64_t> label) {
- ops_ = std::make_shared<std::vector<CowOperationV3>>();
- ops_->resize(header_.op_count);
+bool CowParserV3::ReadResumeBuffer(borrowed_fd fd) {
+ resume_points_ = std::make_shared<std::vector<ResumePoint>>(header_.resume_point_count);
- const off_t offset = header_.prefix.header_size + header_.buffer_size;
+ return android::base::ReadFullyAtOffset(fd, resume_points_->data(),
+ header_.resume_point_count * sizeof(ResumePoint),
+ GetResumeOffset(header_));
+}
+
+std::optional<uint32_t> CowParserV3::FindResumeOp(const uint64_t label) {
+ for (auto& resume_point : *resume_points_) {
+ if (resume_point.label == label) {
+ return resume_point.op_index;
+ }
+ }
+ LOG(ERROR) << "failed to find label: " << label << "from following labels";
+ LOG(ERROR) << android::base::Join(*resume_points_, " ");
+
+ return std::nullopt;
+}
+
+bool CowParserV3::ParseOps(borrowed_fd fd, const uint32_t op_index) {
+ ops_ = std::make_shared<std::vector<CowOperationV3>>();
+ ops_->resize(op_index);
+
+ // read beginning of operation buffer -> so op_index = 0
+ const off_t offset = GetOpOffset(0, header_);
if (!android::base::ReadFullyAtOffset(fd, ops_->data(), ops_->size() * sizeof(CowOperationV3),
offset)) {
PLOG(ERROR) << "read ops failed";
return false;
}
+ // fill out mapping of XOR op data location
+ uint64_t data_pos = GetDataOffset(header_);
+
+ xor_data_loc_ = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
+
+ for (auto op : *ops_) {
+ if (op.type() == kCowXorOp) {
+ xor_data_loc_->insert({op.new_block, data_pos});
+ }
+ data_pos += op.data_length;
+ }
// :TODO: sequence buffer & resume buffer follow
// Once we implement labels, we'll have to discard unused ops and adjust
// the header as needed.
- CHECK(!label);
ops_->shrink_to_fit();
+
return true;
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.h
index 5760cf2..afc01af 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.h
@@ -45,15 +45,15 @@
bool Parse(android::base::borrowed_fd fd, const CowHeaderV3& header,
std::optional<uint64_t> label = {}) override;
bool Translate(TranslatedCowOps* out) override;
- std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc() const override {
- return nullptr;
- };
+ std::shared_ptr<std::vector<ResumePoint>> resume_points() const { return resume_points_; }
private:
- bool ParseOps(android::base::borrowed_fd fd, std::optional<uint64_t> label);
-
+ bool ParseOps(android::base::borrowed_fd fd, const uint32_t op_index);
+ std::optional<uint32_t> FindResumeOp(const uint64_t label);
CowHeaderV3 header_ = {};
std::shared_ptr<std::vector<CowOperationV3>> ops_;
+ bool ReadResumeBuffer(android::base::borrowed_fd fd);
+ std::shared_ptr<std::vector<ResumePoint>> resume_points_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
index a3e40d9..4e90a0f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
@@ -147,7 +147,7 @@
op = ops_[chunk];
}
- if (!op || op->type == kCowCopyOp) {
+ if (!op || op->type() == kCowCopyOp) {
borrowed_fd fd = GetSourceFd();
if (fd < 0) {
// GetSourceFd sets errno.
@@ -169,15 +169,15 @@
// ReadFullyAtOffset sets errno.
return -1;
}
- } else if (op->type == kCowZeroOp) {
+ } else if (op->type() == kCowZeroOp) {
memset(buffer, 0, bytes_to_read);
- } else if (op->type == kCowReplaceOp) {
+ } else if (op->type() == kCowReplaceOp) {
if (cow_->ReadData(op, buffer, bytes_to_read, start_offset) < bytes_to_read) {
LOG(ERROR) << "CompressedSnapshotReader failed to read replace op";
errno = EIO;
return -1;
}
- } else if (op->type == kCowXorOp) {
+ } else if (op->type() == kCowXorOp) {
borrowed_fd fd = GetSourceFd();
if (fd < 0) {
// GetSourceFd sets errno.
@@ -208,7 +208,7 @@
((char*)buffer)[i] ^= data[i];
}
} else {
- LOG(ERROR) << "CompressedSnapshotReader unknown op type: " << uint32_t(op->type);
+ LOG(ERROR) << "CompressedSnapshotReader unknown op type: " << uint32_t(op->type());
errno = EINVAL;
return -1;
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
index 35d74ba..49d86d8 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
@@ -85,10 +85,10 @@
size_t i = 0;
while (!iter->AtEnd()) {
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowCopyOp);
+ ASSERT_EQ(op->type(), kCowCopyOp);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 10 + i);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 1000 + i);
+ ASSERT_EQ(op->source(), 1000 + i);
iter->Next();
i += 1;
}
@@ -131,10 +131,10 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowCopyOp);
+ ASSERT_EQ(op->type(), kCowCopyOp);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 10);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 20);
+ ASSERT_EQ(op->source(), 20);
std::string sink(data.size(), '\0');
@@ -142,7 +142,7 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->type(), kCowReplaceOp);
ASSERT_EQ(op->data_length, 4096);
ASSERT_EQ(op->new_block, 50);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -153,19 +153,19 @@
op = iter->Get();
// Note: the zero operation gets split into two blocks.
- ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->type(), kCowZeroOp);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 51);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
+ ASSERT_EQ(op->source(), 0);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->type(), kCowZeroOp);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 52);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
+ ASSERT_EQ(op->source(), 0);
iter->Next();
ASSERT_TRUE(iter->AtEnd());
@@ -206,10 +206,10 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowCopyOp);
+ ASSERT_EQ(op->type(), kCowCopyOp);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 10);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 20);
+ ASSERT_EQ(op->source(), 20);
std::string sink(data.size(), '\0');
@@ -217,10 +217,10 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowXorOp);
+ ASSERT_EQ(op->type(), kCowXorOp);
ASSERT_EQ(op->data_length, 4096);
ASSERT_EQ(op->new_block, 50);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 98314); // 4096 * 24 + 10
+ ASSERT_EQ(op->source(), 98314); // 4096 * 24 + 10
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data);
@@ -229,19 +229,19 @@
op = iter->Get();
// Note: the zero operation gets split into two blocks.
- ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->type(), kCowZeroOp);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 51);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
+ ASSERT_EQ(op->source(), 0);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->type(), kCowZeroOp);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 52);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
+ ASSERT_EQ(op->source(), 0);
iter->Next();
ASSERT_TRUE(iter->AtEnd());
@@ -273,7 +273,7 @@
std::string sink(data.size(), '\0');
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->type(), kCowReplaceOp);
ASSERT_EQ(op->data_length, 56); // compressed!
ASSERT_EQ(op->new_block, 50);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -325,16 +325,16 @@
while (!iter->AtEnd()) {
auto op = iter->Get();
- if (op->type == kCowXorOp) {
+ if (op->type() == kCowXorOp) {
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(op->source(), 98314); // 4096 * 24 + 10
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, xor_data);
}
- if (op->type == kCowReplaceOp) {
+ if (op->type() == kCowReplaceOp) {
total_blocks += 1;
if (op->new_block == 100) {
data.resize(options.block_size);
@@ -399,7 +399,7 @@
while (!iter->AtEnd()) {
auto op = iter->Get();
- if (op->type == kCowReplaceOp) {
+ if (op->type() == kCowReplaceOp) {
total_blocks += 1;
if (op->new_block == 50) {
data.resize(options.block_size);
@@ -519,7 +519,7 @@
std::string sink(data.size(), '\0');
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->type(), kCowReplaceOp);
ASSERT_EQ(op->data_length, 56); // compressed!
ASSERT_EQ(op->new_block, 50);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -529,7 +529,7 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowClusterOp);
+ ASSERT_EQ(op->type(), kCowClusterOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
@@ -546,7 +546,7 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowClusterOp);
+ ASSERT_EQ(op->type(), kCowClusterOp);
iter->Next();
ASSERT_TRUE(iter->AtEnd());
@@ -580,7 +580,7 @@
std::string sink(options.block_size, '\0');
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->type(), kCowReplaceOp);
ASSERT_EQ(op->new_block, 51);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
}
@@ -653,7 +653,7 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->type(), kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data);
@@ -663,14 +663,14 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 3);
+ ASSERT_EQ(op->type(), kCowLabelOp);
+ ASSERT_EQ(op->source(), 3);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->type(), kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data2);
@@ -716,14 +716,14 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 0);
+ ASSERT_EQ(op->type(), kCowLabelOp);
+ ASSERT_EQ(op->source(), 0);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->type(), kCowZeroOp);
iter->Next();
@@ -774,8 +774,8 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 5);
+ ASSERT_EQ(op->type(), kCowLabelOp);
+ ASSERT_EQ(op->source(), 5);
iter->Next();
ASSERT_TRUE(iter->AtEnd());
@@ -825,7 +825,7 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->type(), kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data.substr(0, options.block_size));
@@ -835,7 +835,7 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->type(), kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data.substr(options.block_size, 2 * options.block_size));
@@ -843,26 +843,26 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 4);
+ ASSERT_EQ(op->type(), kCowLabelOp);
+ ASSERT_EQ(op->source(), 4);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->type(), kCowZeroOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->type(), kCowZeroOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 5);
+ ASSERT_EQ(op->type(), kCowLabelOp);
+ ASSERT_EQ(op->source(), 5);
iter->Next();
@@ -906,7 +906,7 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->type(), kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data.substr(0, options.block_size));
@@ -914,52 +914,52 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 4);
+ ASSERT_EQ(op->type(), kCowLabelOp);
+ ASSERT_EQ(op->source(), 4);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->type(), kCowZeroOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowClusterOp);
+ ASSERT_EQ(op->type(), kCowClusterOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->type(), kCowZeroOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 5);
+ ASSERT_EQ(op->type(), kCowLabelOp);
+ ASSERT_EQ(op->source(), 5);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowCopyOp);
+ ASSERT_EQ(op->type(), kCowCopyOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowClusterOp);
+ ASSERT_EQ(op->type(), kCowClusterOp);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 6);
+ ASSERT_EQ(op->type(), kCowLabelOp);
+ ASSERT_EQ(op->source(), 6);
iter->Next();
@@ -1005,14 +1005,14 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 50);
+ ASSERT_EQ(op->type(), kCowLabelOp);
+ ASSERT_EQ(op->source(), 50);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->type(), kCowReplaceOp);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
ASSERT_EQ(sink, data2);
@@ -1020,7 +1020,7 @@
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowClusterOp);
+ ASSERT_EQ(op->type(), kCowClusterOp);
iter->Next();
@@ -1117,12 +1117,12 @@
num_in_cluster++;
max_in_cluster = std::max(max_in_cluster, num_in_cluster);
- if (op->type == kCowReplaceOp) {
+ if (op->type() == kCowReplaceOp) {
num_replace++;
ASSERT_EQ(op->new_block, num_replace);
ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
- } else if (op->type == kCowClusterOp) {
+ } else if (op->type() == kCowClusterOp) {
num_in_cluster = 0;
num_clusters++;
}
@@ -1178,12 +1178,12 @@
num_in_cluster++;
max_in_cluster = std::max(max_in_cluster, num_in_cluster);
- if (op->type == kCowReplaceOp) {
+ if (op->type() == kCowReplaceOp) {
num_replace++;
ASSERT_EQ(op->new_block, num_replace);
ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
- } else if (op->type == kCowClusterOp) {
+ } else if (op->type() == kCowClusterOp) {
num_in_cluster = 0;
num_clusters++;
}
@@ -1229,12 +1229,12 @@
num_in_cluster++;
max_in_cluster = std::max(max_in_cluster, num_in_cluster);
- if (op->type == kCowReplaceOp) {
+ if (op->type() == kCowReplaceOp) {
num_replace++;
ASSERT_EQ(op->new_block, num_replace);
ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
- } else if (op->type == kCowClusterOp) {
+ } else if (op->type() == kCowClusterOp) {
num_in_cluster = 0;
num_clusters++;
}
@@ -1522,6 +1522,37 @@
ASSERT_TRUE(reader.GetFooter(&footer));
}
+TEST_F(CowTest, DecompressIncompressibleBlock) {
+ auto fd = OpenTestFile("incompressible_block", O_RDONLY);
+ ASSERT_GE(fd, 0);
+
+ std::string original;
+ ASSERT_TRUE(android::base::ReadFdToString(fd, &original)) << strerror(errno);
+ ASSERT_EQ(original.size(), 4096);
+
+ CowOptions options;
+ options.compression = "gz";
+ auto writer = CreateCowWriter(2, options, GetCowFd());
+ ASSERT_NE(writer, nullptr);
+ ASSERT_TRUE(writer->AddRawBlocks(0, original.data(), original.size()));
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+
+ std::string block(original.size(), '\0');
+ ASSERT_EQ(iter->Get()->data_length, 4096);
+ ASSERT_TRUE(ReadData(reader, iter->Get(), block.data(), block.size()));
+
+ for (size_t i = 0; i < block.size(); i++) {
+ ASSERT_EQ(block[i], original[i]) << "mismatch at byte " << i;
+ }
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
index c5d7a02..27accdc 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
@@ -15,17 +15,15 @@
#include <sys/stat.h>
#include <cstdio>
-#include <iostream>
#include <memory>
-#include <string_view>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
+#include <libsnapshot/cow_format.h>
#include <libsnapshot/cow_reader.h>
#include <libsnapshot/cow_writer.h>
-#include "cow_decompress.h"
-#include "libsnapshot/cow_format.h"
#include "writer_v2.h"
#include "writer_v3.h"
@@ -94,6 +92,24 @@
ASSERT_EQ(header.cluster_ops, 0);
}
+TEST_F(CowTestV3, MaxOp) {
+ CowOptions options;
+ options.op_count_max = 20;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+ ASSERT_FALSE(writer->AddZeroBlocks(1, 21));
+ ASSERT_FALSE(writer->AddZeroBlocks(1, 1));
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_FALSE(writer->AddRawBlocks(5, data.data(), data.size()));
+
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ ASSERT_EQ(reader.header_v3().op_count, 20);
+}
+
TEST_F(CowTestV3, ZeroOp) {
CowOptions options;
options.op_count_max = 20;
@@ -110,19 +126,19 @@
ASSERT_FALSE(iter->AtEnd());
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->type(), kCowZeroOp);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 1);
- ASSERT_EQ(op->source_info, 0);
+ ASSERT_EQ(op->source(), 0);
iter->Next();
ASSERT_FALSE(iter->AtEnd());
op = iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->type(), kCowZeroOp);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 2);
- ASSERT_EQ(op->source_info, 0);
+ ASSERT_EQ(op->source(), 0);
}
TEST_F(CowTestV3, ReplaceOp) {
@@ -153,7 +169,7 @@
auto op = iter->Get();
std::string sink(data.size(), '\0');
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->type(), kCowReplaceOp);
ASSERT_EQ(op->data_length, 4096);
ASSERT_EQ(op->new_block, 5);
ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
@@ -193,7 +209,7 @@
while (!iter->AtEnd()) {
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->type(), kCowReplaceOp);
ASSERT_EQ(op->data_length, options.block_size);
ASSERT_EQ(op->new_block, 5 + i);
ASSERT_TRUE(
@@ -218,7 +234,7 @@
CowReader reader;
ASSERT_TRUE(reader.Parse(cow_->fd));
- const auto& header = reader.GetHeader();
+ const auto& header = reader.header_v3();
ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
ASSERT_EQ(header.prefix.major_version, 3);
ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
@@ -231,10 +247,10 @@
size_t i = 0;
while (!iter->AtEnd()) {
auto op = iter->Get();
- ASSERT_EQ(op->type, kCowCopyOp);
+ ASSERT_EQ(op->type(), kCowCopyOp);
ASSERT_EQ(op->data_length, 0);
ASSERT_EQ(op->new_block, 10 + i);
- ASSERT_EQ(GetCowOpSourceInfoData(*op), 1000 + i);
+ ASSERT_EQ(op->source(), 1000 + i);
iter->Next();
i += 1;
}
@@ -242,5 +258,418 @@
ASSERT_EQ(i, 100);
}
+TEST_F(CowTestV3, XorOp) {
+ CowOptions options;
+ options.op_count_max = 100;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+
+ std::string data = "This is test data-1. Testing xor";
+ data.resize(options.block_size, '\0');
+ ASSERT_TRUE(writer->AddXorBlocks(50, data.data(), data.size(), 24, 10));
+ ASSERT_TRUE(writer->Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.header_v3();
+ ASSERT_EQ(header.op_count, 1);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+ auto op = iter->Get();
+ std::string sink(data.size(), '\0');
+
+ ASSERT_EQ(op->type(), kCowXorOp);
+ ASSERT_EQ(op->data_length, 4096);
+ ASSERT_EQ(op->new_block, 50);
+ ASSERT_EQ(op->source(), 98314); // 4096 * 24 + 10
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data);
+}
+
+TEST_F(CowTestV3, ConsecutiveXorOp) {
+ CowOptions options;
+ options.op_count_max = 100;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+
+ std::string data;
+ data.resize(options.block_size * 5);
+ for (int i = 0; i < data.size(); i++) {
+ data[i] = char(rand() % 256);
+ }
+
+ ASSERT_TRUE(writer->AddXorBlocks(50, data.data(), data.size(), 24, 10));
+ ASSERT_TRUE(writer->Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.header_v3();
+ ASSERT_EQ(header.op_count, 5);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+
+ std::string sink(data.size(), '\0');
+ size_t i = 0;
+
+ while (!iter->AtEnd()) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type(), kCowXorOp);
+ ASSERT_EQ(op->data_length, 4096);
+ ASSERT_EQ(op->new_block, 50 + i);
+ ASSERT_EQ(op->source(), 98314 + (i * options.block_size)); // 4096 * 24 + 10
+ ASSERT_TRUE(
+ ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
+ iter->Next();
+ i++;
+ }
+ ASSERT_EQ(sink, data);
+
+ ASSERT_EQ(i, 5);
+}
+
+TEST_F(CowTestV3, AllOpsWithCompression) {
+ CowOptions options;
+ options.compression = "gz";
+ options.op_count_max = 100;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+
+ std::string data;
+ data.resize(options.block_size * 5);
+ for (int i = 0; i < data.size(); i++) {
+ data[i] = char(rand() % 4);
+ }
+
+ ASSERT_TRUE(writer->AddZeroBlocks(10, 5));
+ ASSERT_TRUE(writer->AddCopy(15, 3, 5));
+ ASSERT_TRUE(writer->AddRawBlocks(18, data.data(), data.size()));
+ ASSERT_TRUE(writer->AddXorBlocks(50, data.data(), data.size(), 24, 10));
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.header_v3();
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, 3);
+ ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.block_size, options.block_size);
+ ASSERT_EQ(header.buffer_size, BUFFER_REGION_DEFAULT_SIZE);
+ ASSERT_EQ(header.op_count, 20);
+ ASSERT_EQ(header.op_count_max, 100);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+
+ size_t i = 0;
+
+ while (i < 5) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type(), kCowZeroOp);
+ ASSERT_EQ(op->new_block, 10 + i);
+ iter->Next();
+ i++;
+ }
+ i = 0;
+ while (i < 5) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type(), kCowCopyOp);
+ ASSERT_EQ(op->new_block, 15 + i);
+ ASSERT_EQ(op->source(), 3 + i);
+ iter->Next();
+ i++;
+ }
+ i = 0;
+ std::string sink(data.size(), '\0');
+
+ while (i < 5) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type(), kCowReplaceOp);
+ ASSERT_EQ(op->new_block, 18 + i);
+ ASSERT_TRUE(
+ ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
+ iter->Next();
+ i++;
+ }
+ ASSERT_EQ(sink, data);
+
+ i = 0;
+ std::fill(sink.begin(), sink.end(), '\0');
+ while (i < 5) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type(), kCowXorOp);
+ ASSERT_EQ(op->new_block, 50 + i);
+ ASSERT_EQ(op->source(), 98314 + (i * options.block_size)); // 4096 * 24 + 10
+ ASSERT_TRUE(
+ ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
+ iter->Next();
+ i++;
+ }
+ ASSERT_EQ(sink, data);
+}
+
+TEST_F(CowTestV3, GzCompression) {
+ CowOptions options;
+ options.op_count_max = 100;
+ options.compression = "gz";
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer->Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto header = reader.header_v3();
+ ASSERT_EQ(header.compression_algorithm, kCowCompressGz);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+ auto op = iter->Get();
+
+ std::string sink(data.size(), '\0');
+
+ ASSERT_EQ(op->type(), kCowReplaceOp);
+ ASSERT_EQ(op->data_length, 56); // compressed!
+ ASSERT_EQ(op->new_block, 50);
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data);
+
+ iter->Next();
+ ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTestV3, ResumePointTest) {
+ CowOptions options;
+ options.op_count_max = 100;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+
+ ASSERT_TRUE(writer->AddZeroBlocks(0, 15));
+ ASSERT_TRUE(writer->AddLabel(0));
+ ASSERT_TRUE(writer->AddZeroBlocks(15, 15));
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto header = reader.header_v3();
+ ASSERT_EQ(header.op_count, 30);
+
+ CowWriterV3 second_writer(options, GetCowFd());
+ ASSERT_TRUE(second_writer.Initialize(0));
+ ASSERT_TRUE(second_writer.Finalize());
+
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ header = reader.header_v3();
+ ASSERT_EQ(header.op_count, 15);
+}
+
+TEST_F(CowTestV3, BufferMetadataSyncTest) {
+ CowOptions options;
+ options.op_count_max = 100;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+ /*
+ Header metadafields
+ sequence_data_count = 0;
+ resume_point_count = 0;
+ resume_point_max = 4;
+ */
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto header = reader.header_v3();
+ ASSERT_EQ(header.sequence_data_count, 0);
+ ASSERT_EQ(header.resume_point_count, 0);
+ ASSERT_EQ(header.resume_point_max, 4);
+
+ writer->AddLabel(0);
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ header = reader.header_v3();
+ ASSERT_EQ(header.sequence_data_count, 0);
+ ASSERT_EQ(header.resume_point_count, 1);
+ ASSERT_EQ(header.resume_point_max, 4);
+
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ header = reader.header_v3();
+
+ /*
+ Header metadafields
+ sequence_data_count = 1;
+ resume_point_count = 0;
+ resume_point_max = 4;
+ */
+}
+
+TEST_F(CowTestV3, SequenceTest) {
+ CowOptions options;
+ options.op_count_max = std::numeric_limits<uint32_t>::max();
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+ // sequence data. This just an arbitrary set of integers that specify the merge order. The
+ // actual calculation is done by update_engine and passed to writer. All we care about here is
+ // writing that data correctly
+ const int seq_len = std::numeric_limits<uint16_t>::max() / sizeof(uint32_t) + 1;
+ uint32_t sequence[seq_len];
+ for (int i = 0; i < seq_len; i++) {
+ sequence[i] = i + 1;
+ }
+
+ ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));
+ ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len));
+ ASSERT_TRUE(writer->Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ auto iter = reader.GetRevMergeOpIter();
+
+ for (int i = 0; i < seq_len; i++) {
+ ASSERT_TRUE(!iter->AtEnd());
+ const auto& op = iter->Get();
+
+ ASSERT_EQ(op->new_block, seq_len - i);
+
+ iter->Next();
+ }
+ ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTestV3, MissingSeqOp) {
+ CowOptions options;
+ options.op_count_max = std::numeric_limits<uint32_t>::max();
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+ const int seq_len = 10;
+ uint32_t sequence[seq_len];
+ for (int i = 0; i < seq_len; i++) {
+ sequence[i] = i + 1;
+ }
+ ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));
+ ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len - 1));
+ ASSERT_TRUE(writer->Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_FALSE(reader.Parse(cow_->fd));
+}
+
+TEST_F(CowTestV3, ResumeSeqOp) {
+ CowOptions options;
+ options.op_count_max = std::numeric_limits<uint32_t>::max();
+ auto writer = std::make_unique<CowWriterV3>(options, GetCowFd());
+ const int seq_len = 10;
+ uint32_t sequence[seq_len];
+ for (int i = 0; i < seq_len; i++) {
+ sequence[i] = i + 1;
+ }
+ ASSERT_TRUE(writer->Initialize());
+
+ ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));
+ ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len / 2));
+ ASSERT_TRUE(writer->AddLabel(1));
+ ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, 1));
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+ auto reader = std::make_unique<CowReader>();
+ ASSERT_TRUE(reader->Parse(cow_->fd, 1));
+ auto itr = reader->GetRevMergeOpIter();
+ ASSERT_TRUE(itr->AtEnd());
+
+ writer = std::make_unique<CowWriterV3>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize({1}));
+ ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, seq_len / 2));
+ ASSERT_TRUE(writer->Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ reader = std::make_unique<CowReader>();
+ ASSERT_TRUE(reader->Parse(cow_->fd));
+
+ auto iter = reader->GetRevMergeOpIter();
+
+ uint64_t expected_block = 10;
+ while (!iter->AtEnd() && expected_block > 0) {
+ ASSERT_FALSE(iter->AtEnd());
+ const auto& op = iter->Get();
+
+ ASSERT_EQ(op->new_block, expected_block);
+
+ iter->Next();
+ expected_block--;
+ }
+ ASSERT_EQ(expected_block, 0);
+ ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTestV3, SetSourceManyTimes) {
+ CowOperationV3 op{};
+ op.set_source(1);
+ ASSERT_EQ(op.source(), 1);
+ op.set_source(2);
+ ASSERT_EQ(op.source(), 2);
+ op.set_source(4);
+ ASSERT_EQ(op.source(), 4);
+ op.set_source(8);
+ ASSERT_EQ(op.source(), 8);
+}
+
+TEST_F(CowTestV3, SetTypeManyTimes) {
+ CowOperationV3 op{};
+ op.set_type(kCowCopyOp);
+ ASSERT_EQ(op.type(), kCowCopyOp);
+ op.set_type(kCowReplaceOp);
+ ASSERT_EQ(op.type(), kCowReplaceOp);
+ op.set_type(kCowZeroOp);
+ ASSERT_EQ(op.type(), kCowZeroOp);
+ op.set_type(kCowXorOp);
+ ASSERT_EQ(op.type(), kCowXorOp);
+}
+
+TEST_F(CowTestV3, SetTypeSourceInverleave) {
+ CowOperationV3 op{};
+ op.set_type(kCowCopyOp);
+ ASSERT_EQ(op.type(), kCowCopyOp);
+ op.set_source(0x010203040506);
+ ASSERT_EQ(op.source(), 0x010203040506);
+ ASSERT_EQ(op.type(), kCowCopyOp);
+ op.set_type(kCowReplaceOp);
+ ASSERT_EQ(op.source(), 0x010203040506);
+ ASSERT_EQ(op.type(), kCowReplaceOp);
+}
+
+TEST_F(CowTestV3, CowSizeEstimate) {
+ CowOptions options{};
+ options.compression = "none";
+ auto estimator = android::snapshot::CreateCowEstimator(3, options);
+ ASSERT_TRUE(estimator->AddZeroBlocks(0, 1024 * 1024));
+ const auto cow_size = estimator->GetCowSize();
+ options.op_count_max = 1024 * 1024;
+ options.max_blocks = 1024 * 1024;
+ CowWriterV3 writer(options, GetCowFd());
+ ASSERT_TRUE(writer.Initialize());
+ ASSERT_TRUE(writer.AddZeroBlocks(0, 1024 * 1024));
+
+ ASSERT_LE(writer.GetCowSize(), cow_size);
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
index 37324c7..f9a4e47 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
@@ -369,7 +369,7 @@
}
bool CowWriterV2::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
- uint64_t old_block, uint16_t offset, uint8_t type) {
+ uint64_t old_block, uint16_t offset, CowOperationType type) {
CHECK(!merge_in_progress_);
const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
index 24170eb..50e635f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
@@ -42,7 +42,7 @@
bool EmitCluster();
bool EmitClusterIfNeeded();
bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
- uint16_t offset, uint8_t type);
+ uint16_t offset, CowOperationType type);
void SetupHeaders();
void SetupWriteOptions();
bool ParseOptions();
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
index aeb088d..07f6f00 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
@@ -34,6 +34,7 @@
#include <zlib.h>
#include <fcntl.h>
+#include <libsnapshot_cow/parser_v3.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <unistd.h>
@@ -76,8 +77,9 @@
// v3 specific fields
// WIP: not quite sure how some of these are calculated yet, assuming buffer_size is determined
// during COW size estimation
- header_.sequence_buffer_offset = 0;
- header_.resume_buffer_size = 0;
+ header_.sequence_data_count = 0;
+ header_.resume_point_count = 0;
+ header_.resume_point_max = kNumResumePoints;
header_.op_count = 0;
header_.op_count_max = 0;
header_.compression_algorithm = kCowCompressNone;
@@ -97,6 +99,9 @@
LOG(ERROR) << "unrecognized compression: " << options_.compression;
return false;
}
+ header_.compression_algorithm = *algorithm;
+ header_.op_count_max = options_.op_count_max;
+
if (parts.size() > 1) {
if (!android::base::ParseUint(parts[1], &compression_.compression_level)) {
LOG(ERROR) << "failed to parse compression level invalid type: " << parts[1];
@@ -108,6 +113,7 @@
}
compression_.algorithm = *algorithm;
+ compressor_ = ICompressor::Create(compression_, header_.block_size);
return true;
}
@@ -117,11 +123,14 @@
if (!InitFd() || !ParseOptions()) {
return false;
}
-
- CHECK(!label.has_value());
-
- if (!OpenForWrite()) {
- return false;
+ if (!label) {
+ if (!OpenForWrite()) {
+ return false;
+ }
+ } else {
+ if (!OpenForAppend(*label)) {
+ return false;
+ }
}
return true;
@@ -155,24 +164,54 @@
return false;
}
}
- header_.op_count_max = options_.op_count_max;
+
+ resume_points_ = std::make_shared<std::vector<ResumePoint>>();
if (!Sync()) {
LOG(ERROR) << "Header sync failed";
return false;
}
- next_data_pos_ =
- sizeof(CowHeaderV3) + header_.buffer_size + header_.op_count_max * sizeof(CowOperation);
+ next_data_pos_ = GetDataOffset(header_);
+ return true;
+}
+
+bool CowWriterV3::OpenForAppend(uint64_t label) {
+ CowHeaderV3 header_v3{};
+ if (!ReadCowHeader(fd_, &header_v3)) {
+ LOG(ERROR) << "Couldn't read Cow Header";
+ return false;
+ }
+
+ header_ = header_v3;
+
+ CHECK(label >= 0);
+ CowParserV3 parser;
+ if (!parser.Parse(fd_, header_, label)) {
+ PLOG(ERROR) << "unable to parse with given label: " << label;
+ return false;
+ }
+
+ resume_points_ = parser.resume_points();
+ options_.block_size = header_.block_size;
+ next_data_pos_ = GetDataOffset(header_);
+
+ TranslatedCowOps ops;
+ parser.Translate(&ops);
+ header_.op_count = ops.ops->size();
+
+ for (const auto& op : *ops.ops) {
+ next_data_pos_ += op.data_length;
+ }
return true;
}
bool CowWriterV3::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
for (size_t i = 0; i < num_blocks; i++) {
- CowOperationV3 op = {};
- op.type = kCowCopyOp;
+ CowOperationV3 op{};
+ op.set_type(kCowCopyOp);
op.new_block = new_block + i;
- op.source_info = old_block + i;
+ op.set_source(old_block + i);
if (!WriteOperation(op)) {
return false;
}
@@ -187,33 +226,46 @@
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;
+ return EmitBlocks(new_block_start, data, size, old_block, offset, kCowXorOp);
}
bool CowWriterV3::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
- uint64_t old_block, uint16_t offset, uint8_t type) {
- const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
-
- // Placing here until we support XOR ops
- CHECK_EQ(old_block, 0);
- CHECK_EQ(offset, 0);
-
+ uint64_t old_block, uint16_t offset, CowOperationType type) {
const size_t num_blocks = (size / header_.block_size);
-
for (size_t i = 0; i < num_blocks; i++) {
+ const uint8_t* const iter =
+ reinterpret_cast<const uint8_t*>(data) + (header_.block_size * i);
+
CowOperation op = {};
op.new_block = new_block_start + i;
- op.type = type;
- op.source_info = next_data_pos_;
- op.data_length = static_cast<uint16_t>(header_.block_size);
- if (!WriteOperation(op, iter, header_.block_size)) {
- LOG(ERROR) << "AddRawBlocks: write failed";
+ op.set_type(type);
+ if (type == kCowXorOp) {
+ op.set_source((old_block + i) * header_.block_size + offset);
+ } else {
+ op.set_source(next_data_pos_);
+ }
+ std::basic_string<uint8_t> compressed_data;
+ const void* out_data = iter;
+
+ op.data_length = header_.block_size;
+
+ if (compression_.algorithm) {
+ if (!compressor_) {
+ PLOG(ERROR) << "Compressor not initialized";
+ return false;
+ }
+ compressed_data = compressor_->Compress(out_data, header_.block_size);
+ if (compressed_data.size() < op.data_length) {
+ out_data = compressed_data.data();
+ op.data_length = compressed_data.size();
+ }
+ }
+ if (!WriteOperation(op, out_data, op.data_length)) {
+ PLOG(ERROR) << "AddRawBlocks with compression: write failed. new block: "
+ << new_block_start << " compression: " << compression_.algorithm;
return false;
}
- iter += header_.block_size;
}
return true;
@@ -221,11 +273,9 @@
bool CowWriterV3::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
for (uint64_t i = 0; i < num_blocks; i++) {
- CowOperationV3 op;
- op.type = kCowZeroOp;
- op.data_length = 0;
+ CowOperationV3 op{};
+ op.set_type(kCowZeroOp);
op.new_block = new_block_start + i;
- op.source_info = 0;
if (!WriteOperation(op)) {
return false;
}
@@ -234,21 +284,56 @@
}
bool CowWriterV3::EmitLabel(uint64_t label) {
- LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
- if (label) return false;
- return false;
+ // remove all labels greater than this current one. we want to avoid the situation of adding
+ // in
+ // duplicate labels with differing op values
+ auto remove_if_callback = [&](const auto& resume_point) -> bool {
+ if (resume_point.label >= label) return true;
+ return false;
+ };
+ resume_points_->erase(
+ std::remove_if(resume_points_->begin(), resume_points_->end(), remove_if_callback),
+ resume_points_->end());
+
+ resume_points_->push_back({label, header_.op_count});
+ header_.resume_point_count++;
+ // remove the oldest resume point if resume_buffer is full
+ while (resume_points_->size() > header_.resume_point_max) {
+ resume_points_->erase(resume_points_->begin());
+ }
+
+ CHECK_LE(resume_points_->size(), header_.resume_point_max);
+
+ if (!android::base::WriteFullyAtOffset(fd_, resume_points_->data(),
+ resume_points_->size() * sizeof(ResumePoint),
+ GetResumeOffset(header_))) {
+ PLOG(ERROR) << "writing resume buffer failed";
+ return false;
+ }
+ return Finalize();
}
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;
+ // TODO: size sequence buffer based on options
+ header_.sequence_data_count = num_ops;
+ if (!android::base::WriteFullyAtOffset(fd_, data, sizeof(data[0]) * num_ops,
+ GetSequenceOffset(header_))) {
+ PLOG(ERROR) << "writing sequence buffer failed";
+ return false;
+ }
+ return true;
}
bool CowWriterV3::WriteOperation(const CowOperationV3& op, const void* data, size_t size) {
if (IsEstimating()) {
header_.op_count++;
- header_.op_count_max++;
+ if (header_.op_count > header_.op_count_max) {
+ // If we increment op_count_max, the offset of data section would
+ // change. So need to update |next_data_pos_|
+ next_data_pos_ += (header_.op_count - header_.op_count_max) * sizeof(CowOperationV3);
+ header_.op_count_max = header_.op_count;
+ }
+ next_data_pos_ += op.data_length;
return true;
}
@@ -257,7 +342,7 @@
return false;
}
- const off_t offset = GetOpOffset(header_.op_count);
+ const off_t offset = GetOpOffset(header_.op_count, header_);
if (!android::base::WriteFullyAtOffset(fd_, &op, sizeof(op), offset)) {
PLOG(ERROR) << "write failed for " << op << " at " << offset;
return false;
@@ -286,9 +371,7 @@
}
uint64_t CowWriterV3::GetCowSize() {
- LOG(ERROR) << __LINE__ << " " << __FILE__
- << " <- Get Cow Size function here should never be called";
- return 0;
+ return next_data_pos_;
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
index af71a03..340218f 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
@@ -43,22 +43,25 @@
void SetupHeaders();
bool ParseOptions();
bool OpenForWrite();
+ bool OpenForAppend(uint64_t label);
bool WriteOperation(const CowOperationV3& op, const void* data = nullptr, size_t size = 0);
bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
- uint16_t offset, uint8_t type);
-
- off_t GetOpOffset(uint32_t op_index) const {
- CHECK_LT(op_index, header_.op_count_max);
- return header_.prefix.header_size + header_.buffer_size +
- (op_index * sizeof(CowOperationV3));
- }
+ uint16_t offset, CowOperationType type);
+ bool CompressBlocks(size_t num_blocks, const void* data);
private:
CowHeaderV3 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_;
+ std::vector<std::unique_ptr<CompressWorker>> compress_threads_;
+ // Resume points contain a laebl + cow_op_index.
+ std::shared_ptr<std::vector<ResumePoint>> resume_points_;
uint64_t next_op_pos_ = 0;
uint64_t next_data_pos_ = 0;
+ std::vector<std::basic_string<uint8_t>> compressed_buf_;
// in the case that we are using one thread for compression, we can store and re-use the same
// compressor
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index c639e43..f6a35a8 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -30,6 +30,7 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
@@ -82,12 +83,28 @@
using std::chrono::duration_cast;
using namespace std::chrono_literals;
using namespace std::string_literals;
+using android::base::Realpath;
+using android::base::StringPrintf;
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;
+/*
+ * The readahead size is set to 32kb so that
+ * there is no significant memory pressure (/proc/pressure/memory) during boot.
+ * After OTA, during boot, partitions are scanned before marking slot as successful.
+ * This scan will trigger readahead both on source and COW block device thereby
+ * leading to Inactive(file) pages to be very high.
+ *
+ * A lower value may help reduce memory pressure further, however, that will
+ * increase the boot time. Thus, for device which don't care about OTA boot
+ * time, they could use O_DIRECT functionality wherein the I/O to the source
+ * block device will be O_DIRECT.
+ */
+static constexpr auto kCowReadAheadSizeKb = 32;
+static constexpr auto kSourceReadAheadSizeKb = 32;
// Note: IImageManager is an incomplete type in the header, so the default
// destructor doesn't work.
@@ -1748,6 +1765,9 @@
snapuserd_argv->emplace_back(std::move(message));
}
+ SetReadAheadSize(cow_image_device, kCowReadAheadSizeKb);
+ SetReadAheadSize(source_device, kSourceReadAheadSizeKb);
+
// Do not attempt to connect to the new snapuserd yet, it hasn't
// been started. We do however want to wait for the misc device
// to have been created.
@@ -3642,6 +3662,9 @@
cow_options.max_blocks = {status.device_size() / cow_options.block_size};
cow_options.batch_write = status.batched_writes();
cow_options.num_compress_threads = status.enable_threading() ? 2 : 0;
+ // TODO(b/313962438) Improve op_count estimate. For now, use number of
+ // blocks as an upper bound.
+ cow_options.op_count_max = status.device_size() / cow_options.block_size;
// Disable scratch space for vts tests
if (device()->IsTestDevice()) {
cow_options.scratch_space = false;
@@ -4339,32 +4362,30 @@
}
bool SnapshotManager::IsUserspaceSnapshotUpdateInProgress() {
- auto slot = GetCurrentSlot();
- if (slot == Slot::Target) {
- // Merge in-progress
- if (IsSnapuserdRequired()) {
+ // We cannot grab /metadata/ota lock here as this
+ // is in reboot path. See b/308900853
+ //
+ // Check if any of the partitions are mounted
+ // off dm-user block device. If so, then we are certain
+ // that OTA update in progress.
+ auto current_suffix = device_->GetSlotSuffix();
+ auto& 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& partition : dm_block_devices) {
+ std::string partition_name = partition.first + current_suffix;
+ DeviceMapper::TargetInfo snap_target;
+ if (!GetSingleTarget(partition_name, TableQuery::Status, &snap_target)) {
+ return false;
+ }
+ auto type = DeviceMapper::GetTargetType(snap_target.spec);
+ if (type == "user") {
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;
}
@@ -4406,5 +4427,31 @@
return true;
}
+void SnapshotManager::SetReadAheadSize(const std::string& entry_block_device, off64_t size_kb) {
+ std::string block_device;
+ if (!Realpath(entry_block_device, &block_device)) {
+ PLOG(ERROR) << "Failed to realpath " << entry_block_device;
+ return;
+ }
+
+ static constexpr std::string_view kDevBlockPrefix("/dev/block/");
+ if (!android::base::StartsWith(block_device, kDevBlockPrefix)) {
+ LOG(ERROR) << block_device << " is not a block device";
+ return;
+ }
+
+ std::string block_name = block_device.substr(kDevBlockPrefix.length());
+ std::string sys_partition =
+ android::base::StringPrintf("/sys/class/block/%s/partition", block_name.c_str());
+ struct stat info;
+ if (lstat(sys_partition.c_str(), &info) == 0) {
+ block_name += "/..";
+ }
+ std::string sys_ra = android::base::StringPrintf("/sys/class/block/%s/queue/read_ahead_kb",
+ block_name.c_str());
+ std::string size = std::to_string(size_kb);
+ android::base::WriteStringToFile(size, sys_ra.c_str());
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index ebaca2d..0396a55 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -227,8 +227,12 @@
if (file_offset >= dev_sz) {
break;
}
+
+ if (fsync(cfd.get()) < 0) {
+ PLOG(ERROR) << "Fsync failed at offset: " << file_offset << " size: " << to_read;
+ return false;
+ }
}
- fsync(cfd.get());
return true;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
index 6dc082e..93bb0b2 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
@@ -406,9 +406,9 @@
break;
}
- if (cow_op->type == kCowReplaceOp) {
+ if (cow_op->type() == kCowReplaceOp) {
replace_ops++;
- } else if (cow_op->type == kCowZeroOp) {
+ } else if (cow_op->type() == kCowZeroOp) {
zero_ops++;
}
@@ -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 = cow_op->source();
if (prev_id.has_value()) {
if (dest_blocks.count(cow_op->new_block) || source_blocks.count(block_source)) {
break;
@@ -540,7 +540,7 @@
chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
offset += sizeof(struct disk_exception);
num_ops += 1;
- if (cow_op->type == kCowCopyOp) {
+ if (cow_op->type() == kCowCopyOp) {
copy_ops++;
}
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 ab0b309..f1b9245 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 = cow_op->source();
if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block)) {
overlap_ = true;
}
@@ -191,8 +191,8 @@
// Get the first block with offset
const CowOperation* cow_op = GetRAOpIter();
CHECK_NE(cow_op, nullptr);
- *source_offset = GetCowOpSourceInfoData(*cow_op);
- if (cow_op->type == kCowCopyOp) {
+ *source_offset = cow_op->source();
+ if (cow_op->type() == kCowCopyOp) {
*source_offset *= BLOCK_SZ;
}
RAIterNext();
@@ -210,8 +210,8 @@
while (!RAIterDone() && num_ops) {
const CowOperation* op = GetRAOpIter();
CHECK_NE(op, nullptr);
- uint64_t next_offset = GetCowOpSourceInfoData(*op);
- if (op->type == kCowCopyOp) {
+ uint64_t next_offset = op->source();
+ if (op->type() == kCowCopyOp) {
next_offset *= BLOCK_SZ;
}
if (next_offset + nr_consecutive * BLOCK_SZ != *source_offset) {
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 571b352..1f5d568 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
@@ -177,7 +177,7 @@
return false;
}
- switch (cow_op->type) {
+ switch (cow_op->type()) {
case kCowReplaceOp: {
return ProcessReplaceOp(cow_op);
}
@@ -191,7 +191,8 @@
}
default: {
- SNAP_LOG(ERROR) << "Unsupported operation-type found: " << cow_op->type;
+ SNAP_LOG(ERROR) << "Unsupported operation-type found: "
+ << static_cast<uint8_t>(cow_op->type());
}
}
return false;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
index c5718d5..c85331b 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
@@ -41,7 +41,7 @@
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);
+ opener, 1, false, false, false);
if (!handler_->InitCowDevice()) {
return false;
}
@@ -50,7 +50,7 @@
}
read_worker_ = std::make_unique<ReadWorker>(cow_path_, base_path_, control_name_, base_path_,
- handler_->GetSharedPtr(), opener);
+ handler_->GetSharedPtr(), opener, false);
if (!read_worker_->Init()) {
return false;
}
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 d979e20..711e704 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
@@ -52,10 +52,10 @@
const std::string& misc_name, 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) {
- auto snapuserd = std::make_shared<SnapshotHandler>(misc_name, cow_device_path, backing_device,
- base_path_merge, opener, num_worker_threads,
- use_iouring, perform_verification);
+ bool o_direct) {
+ auto snapuserd = std::make_shared<SnapshotHandler>(
+ misc_name, cow_device_path, backing_device, base_path_merge, opener, num_worker_threads,
+ use_iouring, perform_verification_, o_direct);
if (!snapuserd->InitCowDevice()) {
LOG(ERROR) << "Failed to initialize Snapuserd";
return nullptr;
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 b1605f0..f23f07e 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
@@ -58,7 +58,7 @@
const std::string& base_path_merge,
std::shared_ptr<IBlockServerOpener> opener,
int num_worker_threads, bool use_iouring,
- bool perform_verification) = 0;
+ bool o_direct) = 0;
// Start serving requests on a snapshot handler.
virtual bool StartHandler(const std::string& misc_name) = 0;
@@ -84,6 +84,9 @@
// Returns whether all snapshots have verified.
virtual bool GetVerificationStatus() = 0;
+
+ // Disable partition verification
+ virtual void DisableVerification() = 0;
};
class SnapshotHandlerManager final : public ISnapshotHandlerManager {
@@ -95,7 +98,7 @@
const std::string& base_path_merge,
std::shared_ptr<IBlockServerOpener> opener,
int num_worker_threads, bool use_iouring,
- bool perform_verification) override;
+ bool o_direct) override;
bool StartHandler(const std::string& misc_name) override;
bool DeleteHandler(const std::string& misc_name) override;
bool InitiateMerge(const std::string& misc_name) override;
@@ -104,6 +107,7 @@
void TerminateMergeThreads() override;
double GetMergePercentage() override;
bool GetVerificationStatus() override;
+ void DisableVerification() override { perform_verification_ = false; }
private:
bool StartHandler(const std::shared_ptr<HandlerThread>& handler);
@@ -128,6 +132,7 @@
int num_partitions_merge_complete_ = 0;
std::queue<std::shared_ptr<HandlerThread>> merge_handlers_;
android::base::unique_fd monitor_merge_event_fd_;
+ bool perform_verification_ = true;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
index 11b8d7c..bcf9aab 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
@@ -114,13 +114,13 @@
SNAP_LOG(ERROR) << "AcquireBuffer failed in MergeReplaceOps";
return false;
}
- if (cow_op->type == kCowReplaceOp) {
+ if (cow_op->type() == kCowReplaceOp) {
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);
+ CHECK(cow_op->type() == kCowZeroOp);
memset(buffer, 0, BLOCK_SZ);
}
}
@@ -557,7 +557,7 @@
return true;
}
- if (!SetThreadPriority(kNiceValueForMergeThreads)) {
+ if (!SetThreadPriority(ANDROID_PRIORITY_BACKGROUND)) {
SNAP_PLOG(ERROR) << "Failed to set thread priority";
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
index 5cb13e8..f1d4065 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
@@ -31,16 +31,19 @@
void ReadWorker::CloseFds() {
block_server_ = {};
backing_store_fd_ = {};
+ backing_store_direct_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)
+ std::shared_ptr<IBlockServerOpener> opener, bool direct_read)
: Worker(cow_device, misc_name, base_path_merge, snapuserd),
backing_store_device_(backing_device),
- block_server_opener_(opener) {}
+ direct_read_(direct_read),
+ block_server_opener_(opener),
+ aligned_buffer_(std::unique_ptr<void, decltype(&::free)>(nullptr, &::free)) {}
// Start the replace operation. This will read the
// internal COW format and if the block is compressed,
@@ -61,9 +64,20 @@
}
SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
<< " Op: " << *cow_op;
+
+ if (direct_read_ && IsBlockAligned(offset)) {
+ if (!android::base::ReadFullyAtOffset(backing_store_direct_fd_, aligned_buffer_.get(),
+ BLOCK_SZ, offset)) {
+ SNAP_PLOG(ERROR) << "O_DIRECT Read failed at offset: " << offset;
+ return false;
+ }
+ std::memcpy(buffer, aligned_buffer_.get(), BLOCK_SZ);
+ return true;
+ }
+
if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) {
std::string op;
- if (cow_op->type == kCowCopyOp)
+ if (cow_op->type() == kCowCopyOp)
op = "Copy-op";
else {
op = "Xor-op";
@@ -133,7 +147,7 @@
}
case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {
bool ret;
- if (cow_op->type == kCowCopyOp) {
+ if (cow_op->type() == kCowCopyOp) {
ret = ProcessCopyOp(cow_op, buffer);
} else {
ret = ProcessXorOp(cow_op, buffer);
@@ -167,7 +181,7 @@
return false;
}
- switch (cow_op->type) {
+ switch (cow_op->type()) {
case kCowReplaceOp: {
return ProcessReplaceOp(cow_op, buffer);
}
@@ -183,7 +197,8 @@
}
default: {
- SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
+ SNAP_LOG(ERROR) << "Unknown operation-type found: "
+ << static_cast<uint8_t>(cow_op->type());
}
}
return false;
@@ -200,6 +215,24 @@
return false;
}
+ if (direct_read_) {
+ backing_store_direct_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY | O_DIRECT));
+ if (backing_store_direct_fd_ < 0) {
+ SNAP_PLOG(ERROR) << "Open Failed with O_DIRECT: " << backing_store_direct_fd_;
+ direct_read_ = false;
+ } else {
+ void* aligned_addr;
+ ssize_t page_size = getpagesize();
+ if (posix_memalign(&aligned_addr, page_size, page_size) < 0) {
+ direct_read_ = false;
+ SNAP_PLOG(ERROR) << "posix_memalign failed "
+ << " page_size: " << page_size << " read_sz: " << page_size;
+ } else {
+ aligned_buffer_.reset(aligned_addr);
+ }
+ }
+ }
+
block_server_ = block_server_opener_->Open(this, PAYLOAD_BUFFER_SZ);
if (!block_server_) {
SNAP_PLOG(ERROR) << "Unable to open block server";
@@ -213,7 +246,7 @@
pthread_setname_np(pthread_self(), "ReadWorker");
- if (!SetThreadPriority(kNiceValueForMergeThreads)) {
+ if (!SetThreadPriority(ANDROID_PRIORITY_NORMAL)) {
SNAP_PLOG(ERROR) << "Failed to set thread priority";
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
index 6dbae81..1aff50c 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
@@ -28,7 +28,7 @@
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);
+ std::shared_ptr<IBlockServerOpener> opener, bool direct_read = false);
bool Run();
bool Init() override;
@@ -59,11 +59,14 @@
std::string backing_store_device_;
unique_fd backing_store_fd_;
+ unique_fd backing_store_direct_fd_;
+ bool direct_read_ = false;
std::shared_ptr<IBlockServerOpener> block_server_opener_;
std::unique_ptr<IBlockServer> block_server_;
std::basic_string<uint8_t> xor_buffer_;
+ std::unique_ptr<void, decltype(&::free)> aligned_buffer_;
};
} // namespace snapshot
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 950d771..05ba047 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -36,7 +36,7 @@
SnapshotHandler::SnapshotHandler(std::string misc_name, std::string cow_device,
std::string backing_device, std::string base_path_merge,
std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads,
- bool use_iouring, bool perform_verification) {
+ bool use_iouring, bool perform_verification, bool o_direct) {
misc_name_ = std::move(misc_name);
cow_device_ = std::move(cow_device);
backing_store_device_ = std::move(backing_device);
@@ -45,13 +45,14 @@
num_worker_threads_ = num_worker_threads;
is_io_uring_enabled_ = use_iouring;
perform_verification_ = perform_verification;
+ o_direct_ = o_direct;
}
bool SnapshotHandler::InitializeWorkers() {
for (int i = 0; i < num_worker_threads_; i++) {
auto wt = std::make_unique<ReadWorker>(cow_device_, backing_store_device_, misc_name_,
base_path_merge_, GetSharedPtr(),
- block_server_opener_);
+ block_server_opener_, o_direct_);
if (!wt->Init()) {
SNAP_LOG(ERROR) << "Thread initialization failed";
return false;
@@ -199,13 +200,13 @@
while (!cowop_iter->AtEnd()) {
const CowOperation* cow_op = cowop_iter->Get();
- if (cow_op->type == kCowCopyOp) {
+ if (cow_op->type() == kCowCopyOp) {
copy_ops += 1;
- } else if (cow_op->type == kCowReplaceOp) {
+ } else if (cow_op->type() == kCowReplaceOp) {
replace_ops += 1;
- } else if (cow_op->type == kCowZeroOp) {
+ } else if (cow_op->type() == kCowZeroOp) {
zero_ops += 1;
- } else if (cow_op->type == kCowXorOp) {
+ } else if (cow_op->type() == kCowXorOp) {
xor_ops += 1;
}
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 fa1e7a0..9b7238a 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -47,6 +47,7 @@
#include <snapuserd/snapuserd_buffer.h>
#include <snapuserd/snapuserd_kernel.h>
#include <storage_literals/storage_literals.h>
+#include <system/thread_defs.h>
#include "snapuserd_readahead.h"
#include "snapuserd_verify.h"
@@ -62,8 +63,6 @@
static constexpr int kNumWorkerThreads = 4;
-static constexpr int kNiceValueForMergeThreads = -5;
-
#define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
@@ -105,7 +104,7 @@
public:
SnapshotHandler(std::string misc_name, std::string cow_device, std::string backing_device,
std::string base_path_merge, std::shared_ptr<IBlockServerOpener> opener,
- int num_workers, bool use_iouring, bool perform_verification);
+ int num_workers, bool use_iouring, bool perform_verification, bool o_direct);
bool InitCowDevice();
bool Start();
@@ -247,6 +246,7 @@
bool perform_verification_ = true;
bool resume_merge_ = false;
bool merge_complete_ = false;
+ bool o_direct_ = false;
std::unique_ptr<UpdateVerify> update_verify_;
std::shared_ptr<IBlockServerOpener> block_server_opener_;
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 998d233..c08c1b1 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -77,7 +77,7 @@
SNAP_LOG(ERROR) << "PrepareNextReadAhead operation has no source offset: " << *cow_op;
return nr_consecutive;
}
- if (cow_op->type == kCowXorOp) {
+ if (cow_op->type() == kCowXorOp) {
xor_op_vec.push_back(cow_op);
}
@@ -106,7 +106,7 @@
break;
}
- if (op->type == kCowXorOp) {
+ if (op->type() == kCowXorOp) {
xor_op_vec.push_back(op);
}
@@ -778,7 +778,7 @@
InitializeIouring();
- if (!SetThreadPriority(kNiceValueForMergeThreads)) {
+ if (!SetThreadPriority(ANDROID_PRIORITY_BACKGROUND)) {
SNAP_PLOG(ERROR) << "Failed to set thread priority";
}
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 13b9a00..0b881b6 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -346,7 +346,8 @@
std::shared_ptr<HandlerThread> UserSnapshotServer::AddHandler(const std::string& misc_name,
const std::string& cow_device_path,
const std::string& backing_device,
- const std::string& base_path_merge) {
+ const std::string& base_path_merge,
+ const bool o_direct) {
// We will need multiple worker threads only during
// device boot after OTA. For all other purposes,
// one thread is sufficient. We don't want to consume
@@ -360,16 +361,15 @@
num_worker_threads = 1;
}
- bool perform_verification = true;
- if (android::base::EndsWith(misc_name, "-init") || is_socket_present_) {
- perform_verification = false;
+ if (android::base::EndsWith(misc_name, "-init") || is_socket_present_ ||
+ (access(kBootSnapshotsWithoutSlotSwitch, F_OK) == 0)) {
+ handlers_->DisableVerification();
}
auto opener = block_server_factory_->CreateOpener(misc_name);
return handlers_->AddHandler(misc_name, cow_device_path, backing_device, base_path_merge,
- opener, num_worker_threads, io_uring_enabled_,
- perform_verification);
+ opener, num_worker_threads, io_uring_enabled_, o_direct);
}
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 be28541..3013c47 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -40,6 +40,8 @@
static constexpr uint32_t kMaxPacketSize = 512;
static constexpr uint8_t kMaxMergeThreads = 2;
+static constexpr char kBootSnapshotsWithoutSlotSwitch[] =
+ "/metadata/ota/snapshot-boot-without-slot-switch";
class UserSnapshotServer {
private:
@@ -84,7 +86,8 @@
std::shared_ptr<HandlerThread> AddHandler(const std::string& misc_name,
const std::string& cow_device_path,
const std::string& backing_device,
- const std::string& base_path_merge);
+ const std::string& base_path_merge,
+ bool o_direct = false);
bool StartHandler(const std::string& misc_name);
void SetTerminating() { terminating_ = true; }
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 bed71cf..8ddb0f4 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -59,10 +59,16 @@
using testing::AssertionFailure;
using testing::AssertionResult;
using testing::AssertionSuccess;
+using ::testing::TestWithParam;
-class SnapuserdTestBase : public ::testing::TestWithParam<bool> {
+struct TestParam {
+ bool io_uring;
+ bool o_direct;
+};
+
+class SnapuserdTestBase : public ::testing::TestWithParam<TestParam> {
protected:
- void SetUp() override;
+ virtual void SetUp() override;
void TearDown() override;
void CreateBaseDevice();
void CreateCowDevice();
@@ -627,9 +633,11 @@
void SnapuserdTest::InitCowDevice() {
auto factory = harness_->GetBlockServerFactory();
auto opener = factory->CreateOpener(system_device_ctrl_name_);
- auto handler =
- handlers_->AddHandler(system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(),
- base_dev_->GetPath(), opener, 1, GetParam(), false);
+ handlers_->DisableVerification();
+ const TestParam params = GetParam();
+ auto handler = handlers_->AddHandler(system_device_ctrl_name_, cow_system_->path,
+ base_dev_->GetPath(), base_dev_->GetPath(), opener, 1,
+ params.io_uring, params.o_direct);
ASSERT_NE(handler, nullptr);
ASSERT_NE(handler->snapuserd(), nullptr);
#ifdef __ANDROID__
@@ -897,9 +905,10 @@
opener_ = factory_.CreateTestOpener(system_device_ctrl_name_);
ASSERT_NE(opener_, nullptr);
+ const TestParam params = GetParam();
handler_ = std::make_shared<SnapshotHandler>(system_device_ctrl_name_, cow_system_->path,
base_dev_->GetPath(), base_dev_->GetPath(),
- opener_, 1, false, false);
+ opener_, 1, false, false, params.o_direct);
ASSERT_TRUE(handler_->InitCowDevice());
ASSERT_TRUE(handler_->InitializeWorkers());
@@ -989,14 +998,28 @@
return {false, true};
}
-std::string IoUringConfigName(const testing::TestParamInfo<SnapuserdTest::ParamType>& info) {
- return info.param ? "io_uring" : "sync";
+std::vector<TestParam> GetTestConfigs() {
+ std::vector<TestParam> testParams;
+ std::vector<bool> uring_configs = GetIoUringConfigs();
+
+ for (bool config : uring_configs) {
+ TestParam param;
+ param.io_uring = config;
+ param.o_direct = false;
+ testParams.push_back(std::move(param));
+ }
+
+ for (bool config : uring_configs) {
+ TestParam param;
+ param.io_uring = config;
+ param.o_direct = true;
+ testParams.push_back(std::move(param));
+ }
+ return testParams;
}
-INSTANTIATE_TEST_SUITE_P(Io, SnapuserdTest, ::testing::ValuesIn(GetIoUringConfigs()),
- IoUringConfigName);
-INSTANTIATE_TEST_SUITE_P(Io, HandlerTest, ::testing::ValuesIn(GetIoUringConfigs()),
- IoUringConfigName);
+INSTANTIATE_TEST_SUITE_P(Io, SnapuserdTest, ::testing::ValuesIn(GetTestConfigs()));
+INSTANTIATE_TEST_SUITE_P(Io, HandlerTest, ::testing::ValuesIn(GetTestConfigs()));
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
index d07d2f8..7c99085 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
@@ -48,10 +48,27 @@
std::mutex m_lock_;
std::condition_variable m_cv_;
+ /*
+ * Scanning of partitions is an expensive operation both in terms of memory
+ * and CPU usage. The goal here is to scan the partitions fast enough without
+ * significant increase in the boot time.
+ *
+ * Partitions such as system, product which may be huge and may need multiple
+ * threads to speed up the verification process. Using multiple threads for
+ * all partitions may increase CPU usage significantly. Hence, limit that to
+ * 1 thread per partition.
+ *
+ * These numbers were derived by monitoring the memory and CPU pressure
+ * (/proc/pressure/{cpu,memory}; and monitoring the Inactive(file) and
+ * Active(file) pages from /proc/meminfo.
+ *
+ * Additionally, for low memory devices, it is advisible to use O_DIRECT
+ * fucntionality for source block device.
+ */
int kMinThreadsToVerify = 1;
- int kMaxThreadsToVerify = 4;
- uint64_t kThresholdSize = 512_MiB;
- uint64_t kBlockSizeVerify = 1_MiB;
+ int kMaxThreadsToVerify = 3;
+ uint64_t kThresholdSize = 750_MiB;
+ uint64_t kBlockSizeVerify = 2_MiB;
bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
void UpdatePartitionVerificationState(UpdateVerifyState state);
diff --git a/fs_mgr/libsnapshot/tools/testdata/incompressible_block b/fs_mgr/libsnapshot/tools/testdata/incompressible_block
new file mode 100644
index 0000000..cc45cd0
--- /dev/null
+++ b/fs_mgr/libsnapshot/tools/testdata/incompressible_block
Binary files differ
diff --git a/init/init.cpp b/init/init.cpp
index 19f34da..aeccd66 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -108,6 +108,7 @@
using android::base::StringPrintf;
using android::base::Timer;
using android::base::Trim;
+using android::base::unique_fd;
using android::fs_mgr::AvbHandle;
using android::snapshot::SnapshotManager;
@@ -116,7 +117,7 @@
static int property_triggers_enabled = 0;
-static int signal_fd = -1;
+static int sigterm_fd = -1;
static int property_fd = -1;
struct PendingControlMessage {
@@ -713,8 +714,9 @@
HandlePowerctlMessage("shutdown,container");
}
-static void HandleSignalFd() {
+static void HandleSignalFd(int signal) {
signalfd_siginfo siginfo;
+ const int signal_fd = signal == SIGCHLD ? Service::GetSigchldFd() : sigterm_fd;
ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
if (bytes_read != sizeof(siginfo)) {
PLOG(ERROR) << "Failed to read siginfo from signal_fd";
@@ -748,40 +750,51 @@
}
}
+static Result<void> RegisterSignalFd(Epoll* epoll, int signal, int fd) {
+ return epoll->RegisterHandler(
+ fd, [signal]() { HandleSignalFd(signal); }, EPOLLIN | EPOLLPRI);
+}
+
+static Result<int> CreateAndRegisterSignalFd(Epoll* epoll, int signal) {
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, signal);
+ if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
+ return ErrnoError() << "failed to block signal " << signal;
+ }
+
+ unique_fd signal_fd(signalfd(-1, &mask, SFD_CLOEXEC));
+ if (signal_fd.get() < 0) {
+ return ErrnoError() << "failed to create signalfd for signal " << signal;
+ }
+ OR_RETURN(RegisterSignalFd(epoll, signal, signal_fd.get()));
+
+ return signal_fd.release();
+}
+
static void InstallSignalFdHandler(Epoll* epoll) {
// Applying SA_NOCLDSTOP to a defaulted SIGCHLD handler prevents the signalfd from receiving
// SIGCHLD when a child process stops or continues (b/77867680#comment9).
const struct sigaction act { .sa_flags = SA_NOCLDSTOP, .sa_handler = SIG_DFL };
sigaction(SIGCHLD, &act, nullptr);
- sigset_t mask;
- sigemptyset(&mask);
- sigaddset(&mask, SIGCHLD);
-
- if (!IsRebootCapable()) {
- // If init does not have the CAP_SYS_BOOT capability, it is running in a container.
- // In that case, receiving SIGTERM will cause the system to shut down.
- sigaddset(&mask, SIGTERM);
- }
-
- if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
- PLOG(FATAL) << "failed to block signals";
- }
-
// Register a handler to unblock signals in the child processes.
const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals);
if (result != 0) {
LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
}
- signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
- if (signal_fd == -1) {
- PLOG(FATAL) << "failed to create signalfd";
+ Result<void> cs_result = RegisterSignalFd(epoll, SIGCHLD, Service::GetSigchldFd());
+ if (!cs_result.ok()) {
+ PLOG(FATAL) << cs_result.error();
}
- constexpr int flags = EPOLLIN | EPOLLPRI;
- if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd, flags); !result.ok()) {
- LOG(FATAL) << result.error();
+ if (!IsRebootCapable()) {
+ Result<int> cs_result = CreateAndRegisterSignalFd(epoll, SIGTERM);
+ if (!cs_result.ok()) {
+ PLOG(FATAL) << cs_result.error();
+ }
+ sigterm_fd = cs_result.value();
}
}
@@ -1059,8 +1072,8 @@
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
- am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
am.QueueEventTrigger("early-init");
+ am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
diff --git a/init/property_service.cpp b/init/property_service.cpp
index bd74358..e2cff95 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -84,6 +84,7 @@
using android::base::ErrnoError;
using android::base::Error;
+using android::base::GetIntProperty;
using android::base::GetProperty;
using android::base::ParseInt;
using android::base::ReadFileToString;
@@ -112,7 +113,7 @@
constexpr auto LEGACY_ID_PROP = "ro.build.legacy.id";
constexpr auto VBMETA_DIGEST_PROP = "ro.boot.vbmeta.digest";
constexpr auto DIGEST_SIZE_USED = 8;
-constexpr auto API_LEVEL_CURRENT = 10000;
+constexpr auto MAX_VENDOR_API_LEVEL = 1000000;
static bool persistent_properties_loaded = false;
@@ -1084,15 +1085,16 @@
}
}
-static int read_api_level_props(const std::vector<std::string>& api_level_props) {
- int api_level = API_LEVEL_CURRENT;
- for (const auto& api_level_prop : api_level_props) {
- api_level = android::base::GetIntProperty(api_level_prop, API_LEVEL_CURRENT);
- if (api_level != API_LEVEL_CURRENT) {
- break;
- }
+static int vendor_api_level_of(int sdk_api_level) {
+ if (sdk_api_level < __ANDROID_API_V__) {
+ return sdk_api_level;
}
- return api_level;
+ // In Android V, vendor API level started with version 202404.
+ // The calculation assumes that the SDK api level bumps once a year.
+ if (sdk_api_level < __ANDROID_API_FUTURE__) {
+ return 202404 + ((sdk_api_level - __ANDROID_API_V__) * 100);
+ }
+ return MAX_VENDOR_API_LEVEL;
}
static void property_initialize_ro_vendor_api_level() {
@@ -1100,20 +1102,27 @@
// required to support.
constexpr auto VENDOR_API_LEVEL_PROP = "ro.vendor.api_level";
- // Api level properties of the board. The order of the properties must be kept.
- std::vector<std::string> BOARD_API_LEVEL_PROPS = {"ro.board.api_level",
- "ro.board.first_api_level"};
- // Api level properties of the device. The order of the properties must be kept.
- std::vector<std::string> DEVICE_API_LEVEL_PROPS = {"ro.product.first_api_level",
- "ro.build.version.sdk"};
+ auto vendor_api_level = GetIntProperty("ro.board.first_api_level", MAX_VENDOR_API_LEVEL);
+ if (vendor_api_level != MAX_VENDOR_API_LEVEL) {
+ // Update the vendor_api_level with "ro.board.api_level" only if both "ro.board.api_level"
+ // and "ro.board.first_api_level" are defined.
+ vendor_api_level = GetIntProperty("ro.board.api_level", vendor_api_level);
+ }
- int api_level = std::min(read_api_level_props(BOARD_API_LEVEL_PROPS),
- read_api_level_props(DEVICE_API_LEVEL_PROPS));
+ auto product_first_api_level =
+ GetIntProperty("ro.product.first_api_level", __ANDROID_API_FUTURE__);
+ if (product_first_api_level == __ANDROID_API_FUTURE__) {
+ // Fallback to "ro.build.version.sdk" if the "ro.product.first_api_level" is not defined.
+ product_first_api_level = GetIntProperty("ro.build.version.sdk", __ANDROID_API_FUTURE__);
+ }
+
+ vendor_api_level = std::min(vendor_api_level_of(product_first_api_level), vendor_api_level);
+
std::string error;
- auto res = PropertySetNoSocket(VENDOR_API_LEVEL_PROP, std::to_string(api_level), &error);
+ auto res = PropertySetNoSocket(VENDOR_API_LEVEL_PROP, std::to_string(vendor_api_level), &error);
if (res != PROP_SUCCESS) {
- LOG(ERROR) << "Failed to set " << VENDOR_API_LEVEL_PROP << " with " << api_level << ": "
- << error << "(" << res << ")";
+ LOG(ERROR) << "Failed to set " << VENDOR_API_LEVEL_PROP << " with " << vendor_api_level
+ << ": " << error << "(" << res << ")";
}
}
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 3351c4c..1a26c4d 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -563,7 +563,7 @@
}
}
if (timeout > 0ms) {
- WaitToBeReaped(pids, timeout);
+ WaitToBeReaped(Service::GetSigchldFd(), pids, timeout);
} else {
// Even if we don't to wait for services to stop, we still optimistically reap zombies.
ReapAnyOutstandingChildren();
diff --git a/init/security.cpp b/init/security.cpp
index 0c73fae..3e15447 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -20,6 +20,7 @@
#include <errno.h>
#include <fcntl.h>
#include <linux/perf_event.h>
+#include <math.h>
#include <selinux/selinux.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
@@ -121,8 +122,12 @@
}
#elif defined(__x86_64__)
// x86_64 supports 28 - 32 rnd bits, but Android wants to ensure that the
- // theoretical maximum of 32 bits is always supported and used.
- if (SetMmapRndBitsMin(32, 32, false) && (!Has32BitAbi() || SetMmapRndBitsMin(16, 16, true))) {
+ // theoretical maximum of 32 bits is always supported and used; except in
+ // the case of the x86 page size emulator which supports a maximum
+ // of 30 bits for 16k page size, or 28 bits for 64k page size.
+ int max_bits = 32 - (static_cast<int>(log2(getpagesize())) - 12);
+ if (SetMmapRndBitsMin(max_bits, max_bits, false) &&
+ (!Has32BitAbi() || SetMmapRndBitsMin(16, 16, true))) {
return {};
}
#elif defined(__arm__) || defined(__i386__)
diff --git a/init/service.cpp b/init/service.cpp
index 5e900ee..eb24dd5 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -37,6 +37,7 @@
#include <cutils/sockets.h>
#include <processgroup/processgroup.h>
#include <selinux/selinux.h>
+#include <sys/signalfd.h>
#include <string>
@@ -68,6 +69,7 @@
using android::base::SetProperty;
using android::base::StartsWith;
using android::base::StringPrintf;
+using android::base::unique_fd;
using android::base::WriteStringToFile;
namespace android {
@@ -194,28 +196,20 @@
}
}
-void Service::KillProcessGroup(int signal, bool report_oneshot) {
- // If we've already seen a successful result from killProcessGroup*(), then we have removed
- // the cgroup already and calling these functions a second time will simply result in an error.
- // This is true regardless of which signal was sent.
- // These functions handle their own logging, so no additional logging is needed.
- if (!process_cgroup_empty_) {
+void Service::KillProcessGroup(int signal) {
+ // Always attempt the process kill if process is still running.
+ // Cgroup clean up routines are idempotent. It's safe to call
+ // killProcessGroup repeatedly. During shutdown, `init` will
+ // call this function to send SIGTERM/SIGKILL to all processes.
+ // These signals must be sent for a successful shutdown.
+ if (!process_cgroup_empty_ || IsRunning()) {
LOG(INFO) << "Sending signal " << signal << " to service '" << name_ << "' (pid " << pid_
<< ") process group...";
- int max_processes = 0;
int r;
if (signal == SIGTERM) {
- r = killProcessGroupOnce(uid(), pid_, signal, &max_processes);
+ r = killProcessGroupOnce(uid(), pid_, signal);
} else {
- r = killProcessGroup(uid(), pid_, signal, &max_processes);
- }
-
- if (report_oneshot && max_processes > 0) {
- LOG(WARNING)
- << "Killed " << max_processes
- << " additional processes from a oneshot process group for service '" << name_
- << "'. This is new behavior, previously child processes would not be killed in "
- "this case.";
+ r = killProcessGroup(uid(), pid_, signal);
}
if (r == 0) process_cgroup_empty_ = true;
@@ -265,7 +259,7 @@
void Service::Reap(const siginfo_t& siginfo) {
if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
- KillProcessGroup(SIGKILL, false);
+ KillProcessGroup(SIGKILL);
} else {
// Legacy behavior from ~2007 until Android R: this else branch did not exist and we did not
// kill the process group in this case.
@@ -273,7 +267,7 @@
// The new behavior in Android R is to kill these process groups in all cases. The
// 'true' parameter instructions KillProcessGroup() to report a warning message where it
// detects a difference in behavior has occurred.
- KillProcessGroup(SIGKILL, true);
+ KillProcessGroup(SIGKILL);
}
}
@@ -799,6 +793,35 @@
mount_namespace_ = IsDefaultMountNamespaceReady() ? NS_DEFAULT : NS_BOOTSTRAP;
}
+static int ThreadCount() {
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir("/proc/self/task"), closedir);
+ if (!dir) {
+ return -1;
+ }
+
+ int count = 0;
+ dirent* entry;
+ while ((entry = readdir(dir.get())) != nullptr) {
+ if (entry->d_name[0] != '.') {
+ count++;
+ }
+ }
+ return count;
+}
+
+// Must be called BEFORE any threads are created. See also the sigprocmask() man page.
+unique_fd Service::CreateSigchldFd() {
+ CHECK_EQ(ThreadCount(), 1);
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+ if (sigprocmask(SIG_BLOCK, &mask, nullptr) < 0) {
+ PLOG(FATAL) << "Failed to block SIGCHLD";
+ }
+
+ return unique_fd(signalfd(-1, &mask, SFD_CLOEXEC));
+}
+
void Service::SetStartedInFirstStage(pid_t pid) {
LOG(INFO) << "adding first-stage service '" << name_ << "'...";
diff --git a/init/service.h b/init/service.h
index 9f09cef..5e9af25 100644
--- a/init/service.h
+++ b/init/service.h
@@ -156,11 +156,15 @@
const Subcontext* subcontext() const { return subcontext_; }
const std::string& filename() const { return filename_; }
void set_filename(const std::string& name) { filename_ = name; }
+ static int GetSigchldFd() {
+ static int sigchld_fd = CreateSigchldFd().release();
+ return sigchld_fd;
+ }
private:
void NotifyStateChange(const std::string& new_state) const;
void StopOrReset(int how);
- void KillProcessGroup(int signal, bool report_oneshot = false);
+ void KillProcessGroup(int signal);
void SetProcessAttributesAndCaps(InterprocessFifo setsid_finished);
void ResetFlagsForStart();
Result<void> CheckConsole();
@@ -168,6 +172,8 @@
void RunService(const std::vector<Descriptor>& descriptors, InterprocessFifo cgroups_activated,
InterprocessFifo setsid_finished);
void SetMountNamespace();
+ static ::android::base::unique_fd CreateSigchldFd();
+
static unsigned long next_start_order_;
static bool is_exec_service_running_;
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index a1b2cc5..92e350b 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -179,8 +179,9 @@
if (!ParseInt(args[1], &service_->proc_attr_.priority,
static_cast<int>(ANDROID_PRIORITY_HIGHEST), // highest is negative
static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
- return Errorf("process priority value must be range {} - {}", ANDROID_PRIORITY_HIGHEST,
- ANDROID_PRIORITY_LOWEST);
+ return Errorf("process priority value must be range {} - {}",
+ static_cast<int>(ANDROID_PRIORITY_HIGHEST),
+ static_cast<int>(ANDROID_PRIORITY_LOWEST));
}
return {};
}
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 87a2ce5..a3590b5 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -17,18 +17,45 @@
#include "service.h"
#include <algorithm>
+#include <fstream>
#include <memory>
#include <type_traits>
#include <vector>
#include <gtest/gtest.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <selinux/selinux.h>
+#include <sys/signalfd.h>
#include "lmkd_service.h"
+#include "reboot.h"
+#include "service.h"
+#include "service_list.h"
+#include "service_parser.h"
#include "util.h"
+using ::android::base::ReadFileToString;
+using ::android::base::StringPrintf;
+using ::android::base::StringReplace;
+using ::android::base::unique_fd;
+using ::android::base::WriteStringToFd;
+using ::android::base::WriteStringToFile;
+
namespace android {
namespace init {
+static std::string GetSecurityContext() {
+ char* ctx;
+ if (getcon(&ctx) == -1) {
+ ADD_FAILURE() << "Failed to call getcon : " << strerror(errno);
+ }
+ std::string result{ctx};
+ freecon(ctx);
+ return result;
+}
+
TEST(service, pod_initialized) {
constexpr auto memory_size = sizeof(Service);
alignas(alignof(Service)) unsigned char old_memory[memory_size];
@@ -190,5 +217,69 @@
Test_make_temporary_oneshot_service(false, false, false, false, false);
}
+// Returns the path in the v2 cgroup hierarchy for a given process in the format /uid_%d/pid_%d.
+static std::string CgroupPath(pid_t pid) {
+ std::string cgroup_path = StringPrintf("/proc/%d/cgroup", pid);
+ std::ifstream is(cgroup_path, std::ios::in);
+ std::string line;
+ while (std::getline(is, line)) {
+ if (line.substr(0, 3) == "0::") {
+ return line.substr(3);
+ }
+ }
+ return {};
+}
+
+class ServiceStopTest : public testing::TestWithParam<bool> {};
+
+// Before November 2023, processes that were migrated to another v2 cgroup were ignored by
+// Service::Stop() if their uid_%d/pid_%d cgroup directory got removed. This test, if run with the
+// parameter set to 'true', verifies that such services are stopped.
+TEST_P(ServiceStopTest, stop) {
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Must be run as root.";
+ return;
+ }
+
+ static constexpr std::string_view kServiceName = "ServiceA";
+ static constexpr std::string_view kScriptTemplate = R"init(
+service $name /system/bin/yes
+ user shell
+ group shell
+ seclabel $selabel
+)init";
+
+ std::string script = StringReplace(StringReplace(kScriptTemplate, "$name", kServiceName, false),
+ "$selabel", GetSecurityContext(), false);
+ ServiceList& service_list = ServiceList::GetInstance();
+ Parser parser;
+ parser.AddSectionParser("service",
+ std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+
+ TemporaryFile tf;
+ ASSERT_GE(tf.fd, 0);
+ ASSERT_TRUE(WriteStringToFd(script, tf.fd));
+ ASSERT_TRUE(parser.ParseConfig(tf.path));
+
+ Service* const service = ServiceList::GetInstance().FindService(kServiceName);
+ ASSERT_NE(service, nullptr);
+ ASSERT_RESULT_OK(service->Start());
+ ASSERT_TRUE(service->IsRunning());
+ if (GetParam()) {
+ const pid_t pid = service->pid();
+ const std::string cgroup_path = CgroupPath(pid);
+ EXPECT_NE(cgroup_path, "");
+ EXPECT_NE(cgroup_path, "/");
+ const std::string pid_str = std::to_string(pid);
+ EXPECT_TRUE(WriteStringToFile(pid_str, "/sys/fs/cgroup/cgroup.procs"));
+ EXPECT_EQ(CgroupPath(pid), "/");
+ EXPECT_EQ(rmdir(("/sys/fs/cgroup" + cgroup_path).c_str()), 0);
+ }
+ EXPECT_EQ(0, StopServicesAndLogViolations({service->name()}, 10s, /*terminate=*/true));
+ ServiceList::GetInstance().RemoveService(*service);
+}
+
+INSTANTIATE_TEST_SUITE_P(service, ServiceStopTest, testing::Values(false, true));
+
} // namespace init
} // namespace android
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 0901a96..8e9e713 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -18,6 +18,7 @@
#include <signal.h>
#include <string.h>
+#include <sys/signalfd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -31,6 +32,7 @@
#include <thread>
+#include "epoll.h"
#include "init.h"
#include "service.h"
#include "service_list.h"
@@ -116,26 +118,69 @@
return pid;
}
-void ReapAnyOutstandingChildren() {
- while (ReapOneProcess() != 0) {
+std::set<pid_t> ReapAnyOutstandingChildren() {
+ std::set<pid_t> reaped_pids;
+ for (;;) {
+ const pid_t pid = ReapOneProcess();
+ if (pid <= 0) {
+ return reaped_pids;
+ }
+ reaped_pids.emplace(pid);
}
}
-void WaitToBeReaped(const std::vector<pid_t>& pids, std::chrono::milliseconds timeout) {
+static void ReapAndRemove(std::vector<pid_t>& alive_pids) {
+ for (auto pid : ReapAnyOutstandingChildren()) {
+ const auto it = std::find(alive_pids.begin(), alive_pids.end(), pid);
+ if (it != alive_pids.end()) {
+ alive_pids.erase(it);
+ }
+ }
+}
+
+static void HandleSignal(int signal_fd) {
+ signalfd_siginfo siginfo;
+ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
+ if (bytes_read != sizeof(siginfo)) {
+ LOG(WARNING) << "Unexpected: " << __func__ << " read " << bytes_read << " bytes instead of "
+ << sizeof(siginfo);
+ }
+}
+
+void WaitToBeReaped(int sigchld_fd, const std::vector<pid_t>& pids,
+ std::chrono::milliseconds timeout) {
Timer t;
- std::vector<pid_t> alive_pids(pids.begin(), pids.end());
+ Epoll epoll;
+ if (sigchld_fd >= 0) {
+ if (auto result = epoll.Open(); result.ok()) {
+ result =
+ epoll.RegisterHandler(sigchld_fd, [sigchld_fd]() { HandleSignal(sigchld_fd); });
+ if (!result.ok()) {
+ LOG(WARNING) << __func__
+ << " RegisterHandler() failed. Falling back to sleep_for(): "
+ << result.error();
+ sigchld_fd = -1;
+ }
+ } else {
+ LOG(WARNING) << __func__ << " Epoll::Open() failed. Falling back to sleep_for(): "
+ << result.error();
+ sigchld_fd = -1;
+ }
+ }
+ std::vector<pid_t> alive_pids(pids);
+ ReapAndRemove(alive_pids);
while (!alive_pids.empty() && t.duration() < timeout) {
- pid_t pid;
- while ((pid = ReapOneProcess()) != 0) {
- auto it = std::find(alive_pids.begin(), alive_pids.end(), pid);
- if (it != alive_pids.end()) {
- alive_pids.erase(it);
+ if (sigchld_fd >= 0) {
+ auto result = epoll.Wait(std::max(timeout - t.duration(), 0ms));
+ if (result.ok()) {
+ ReapAndRemove(alive_pids);
+ continue;
+ } else {
+ LOG(WARNING) << "Epoll::Wait() failed " << result.error();
}
}
- if (alive_pids.empty()) {
- break;
- }
std::this_thread::sleep_for(50ms);
+ ReapAndRemove(alive_pids);
}
LOG(INFO) << "Waiting for " << pids.size() << " pids to be reaped took " << t << " with "
<< alive_pids.size() << " of them still running";
diff --git a/init/sigchld_handler.h b/init/sigchld_handler.h
index fac1020..5351302 100644
--- a/init/sigchld_handler.h
+++ b/init/sigchld_handler.h
@@ -18,14 +18,16 @@
#define _INIT_SIGCHLD_HANDLER_H_
#include <chrono>
+#include <set>
#include <vector>
namespace android {
namespace init {
-void ReapAnyOutstandingChildren();
+std::set<pid_t> ReapAnyOutstandingChildren();
-void WaitToBeReaped(const std::vector<pid_t>& pids, std::chrono::milliseconds timeout);
+void WaitToBeReaped(int sigchld_fd, const std::vector<pid_t>& pids,
+ std::chrono::milliseconds timeout);
} // namespace init
} // namespace android
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 586e2cf..3f0d0e9 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -297,6 +297,10 @@
}
static UeventdConfiguration GetConfiguration() {
+ if (IsMicrodroid()) {
+ return ParseConfig({"/system/etc/ueventd.rc", "/vendor/etc/ueventd.rc"});
+ }
+
auto hardware = android::base::GetProperty("ro.hardware", "");
struct LegacyPathInfo {
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index dbaeb93..9107838 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -26,7 +26,8 @@
__BEGIN_DECLS
-static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
+static constexpr const char* CGROUPV2_HIERARCHY_NAME = "cgroup2";
+[[deprecated]] static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
bool CgroupsAvailable();
bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path);
@@ -67,14 +68,11 @@
// Return 0 and removes the cgroup if there are no longer any processes in it.
// Returns -1 in the case of an error occurring or if there are processes still running
// even after retrying for up to 200ms.
-// If max_processes is not nullptr, it returns the maximum number of processes seen in the cgroup
-// during the killing process. Note that this can be 0 if all processes from the process group have
-// already been terminated.
-int killProcessGroup(uid_t uid, int initialPid, int signal, int* max_processes = nullptr);
+int killProcessGroup(uid_t uid, int initialPid, int signal);
// Returns the same as killProcessGroup(), however it does not retry, which means
// that it only returns 0 in the case that the cgroup exists and it contains no processes.
-int killProcessGroupOnce(uid_t uid, int initialPid, int signal, int* max_processes = nullptr);
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
// Sends the provided signal to all members of a process group, but does not wait for processes to
// exit, or for the cgroup to be removed. Callers should also ensure that killProcessGroup is called
@@ -89,7 +87,6 @@
bool setProcessGroupSoftLimit(uid_t uid, int initialPid, int64_t softLimitInBytes);
bool setProcessGroupLimit(uid_t uid, int initialPid, int64_t limitInBytes);
-void removeAllProcessGroups(void);
void removeAllEmptyProcessGroups(void);
// Provides the path for an attribute in a specific process group
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index cc2565f..f594f7f 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -129,7 +129,7 @@
}
if (!attr->GetPathForTask(tid, path)) {
- PLOG(ERROR) << "Failed to find cgroup for tid " << tid;
+ LOG(ERROR) << "Failed to find cgroup for tid " << tid;
return false;
}
@@ -213,12 +213,18 @@
return StringPrintf("%s/uid_%u/pid_%d", cgroup, uid, pid);
}
-static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid, unsigned int retries) {
+static int RemoveCgroup(const char* cgroup, uid_t uid, int pid, unsigned int retries) {
int ret = 0;
auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
while (retries--) {
ret = rmdir(uid_pid_path.c_str());
+ // If we get an error 2 'No such file or directory' , that means the
+ // cgroup is already removed, treat it as success and return 0 for
+ // idempotency.
+ if (ret < 0 && errno == ENOENT) {
+ ret = 0;
+ }
if (!ret || errno != EBUSY || !retries) break;
std::this_thread::sleep_for(5ms);
}
@@ -228,12 +234,15 @@
// so free up the kernel resources for the UID level cgroup.
const auto uid_path = ConvertUidToPath(cgroup, uid);
ret = rmdir(uid_path.c_str());
+ if (ret < 0 && errno == ENOENT) {
+ ret = 0;
+ }
}
return ret;
}
-static bool RemoveUidProcessGroups(const std::string& uid_path, bool empty_only) {
+static bool RemoveEmptyUidCgroups(const std::string& uid_path) {
std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir);
bool empty = true;
if (uid != NULL) {
@@ -248,21 +257,6 @@
}
auto path = StringPrintf("%s/%s", uid_path.c_str(), dir->d_name);
- if (empty_only) {
- struct stat st;
- auto procs_file = StringPrintf("%s/%s", path.c_str(),
- PROCESSGROUP_CGROUP_PROCS_FILE);
- if (stat(procs_file.c_str(), &st) == -1) {
- PLOG(ERROR) << "Failed to get stats for " << procs_file;
- continue;
- }
- if (st.st_size > 0) {
- // skip non-empty groups
- LOG(VERBOSE) << "Skipping non-empty group " << path;
- empty = false;
- continue;
- }
- }
LOG(VERBOSE) << "Removing " << path;
if (rmdir(path.c_str()) == -1) {
if (errno != EBUSY) {
@@ -275,11 +269,13 @@
return empty;
}
-void removeAllProcessGroupsInternal(bool empty_only) {
+void removeAllEmptyProcessGroups() {
+ LOG(VERBOSE) << "removeAllEmptyProcessGroups()";
+
std::vector<std::string> cgroups;
std::string path, memcg_apps_path;
- if (CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &path)) {
+ if (CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &path)) {
cgroups.push_back(path);
}
if (CgroupGetMemcgAppsPath(&memcg_apps_path) && memcg_apps_path != path) {
@@ -302,7 +298,7 @@
}
auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
- if (!RemoveUidProcessGroups(path, empty_only)) {
+ if (!RemoveEmptyUidCgroups(path)) {
LOG(VERBOSE) << "Skip removing " << path;
continue;
}
@@ -315,16 +311,6 @@
}
}
-void removeAllProcessGroups() {
- LOG(VERBOSE) << "removeAllProcessGroups()";
- removeAllProcessGroupsInternal(false);
-}
-
-void removeAllEmptyProcessGroups() {
- LOG(VERBOSE) << "removeAllEmptyProcessGroups()";
- removeAllProcessGroupsInternal(true);
-}
-
/**
* Process groups are primarily created by the Zygote, meaning that uid/pid groups are created by
* the user root. Ownership for the newly created cgroup and all of its files must thus be
@@ -392,8 +378,11 @@
fd.reset(fopen(path.c_str(), "re"));
if (!fd) {
if (errno == ENOENT) {
- // This happens when process is already dead
- return 0;
+ // This happens when the process is already dead or if, as the result of a bug, it
+ // has been migrated to another cgroup. An example of a bug that can cause migration
+ // to another cgroup is using the JoinCgroup action with a cgroup controller that
+ // has been activated in the v2 cgroup hierarchy.
+ goto kill;
}
PLOG(WARNING) << __func__ << " failed to open process cgroup uid " << uid << " pid "
<< initialPid;
@@ -432,6 +421,7 @@
}
}
+kill:
// Kill all process groups.
for (const auto pgid : pgids) {
LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid
@@ -455,29 +445,21 @@
return (!fd || feof(fd.get())) ? processes : -1;
}
-static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries,
- int* max_processes) {
+static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
CHECK_GE(uid, 0);
CHECK_GT(initialPid, 0);
std::string hierarchy_root_path;
if (CgroupsAvailable()) {
- CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &hierarchy_root_path);
+ CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &hierarchy_root_path);
}
const char* cgroup = hierarchy_root_path.c_str();
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
- if (max_processes != nullptr) {
- *max_processes = 0;
- }
-
int retry = retries;
int processes;
while ((processes = DoKillProcessGroupOnce(cgroup, uid, initialPid, signal)) > 0) {
- if (max_processes != nullptr && processes > *max_processes) {
- *max_processes = processes;
- }
LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
if (!CgroupsAvailable()) {
// makes no sense to retry, because there are no cgroup_procs file
@@ -519,12 +501,12 @@
}
// 400 retries correspond to 2 secs max timeout
- int err = RemoveProcessGroup(cgroup, uid, initialPid, 400);
+ int err = RemoveCgroup(cgroup, uid, initialPid, 400);
if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
std::string memcg_apps_path;
if (CgroupGetMemcgAppsPath(&memcg_apps_path) &&
- RemoveProcessGroup(memcg_apps_path.c_str(), uid, initialPid, 400) < 0) {
+ RemoveCgroup(memcg_apps_path.c_str(), uid, initialPid, 400) < 0) {
return -1;
}
}
@@ -540,18 +522,18 @@
}
}
-int killProcessGroup(uid_t uid, int initialPid, int signal, int* max_processes) {
- return KillProcessGroup(uid, initialPid, signal, 40 /*retries*/, max_processes);
+int killProcessGroup(uid_t uid, int initialPid, int signal) {
+ return KillProcessGroup(uid, initialPid, signal, 40 /*retries*/);
}
-int killProcessGroupOnce(uid_t uid, int initialPid, int signal, int* max_processes) {
- return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/, max_processes);
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
+ return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/);
}
int sendSignalToProcessGroup(uid_t uid, int initialPid, int signal) {
std::string hierarchy_root_path;
if (CgroupsAvailable()) {
- CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &hierarchy_root_path);
+ CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &hierarchy_root_path);
}
const char* cgroup = hierarchy_root_path.c_str();
return DoKillProcessGroupOnce(cgroup, uid, initialPid, signal);
@@ -609,7 +591,7 @@
CHECK_GT(initialPid, 0);
if (memControl && !UsePerAppMemcg()) {
- PLOG(ERROR) << "service memory controls are used without per-process memory cgroup support";
+ LOG(ERROR) << "service memory controls are used without per-process memory cgroup support";
return -EINVAL;
}
@@ -625,19 +607,19 @@
}
std::string cgroup;
- CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cgroup);
+ CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &cgroup);
return createProcessGroupInternal(uid, initialPid, cgroup, true);
}
static bool SetProcessGroupValue(int tid, const std::string& attr_name, int64_t value) {
if (!isMemoryCgroupSupported()) {
- PLOG(ERROR) << "Memcg is not mounted.";
+ LOG(ERROR) << "Memcg is not mounted.";
return false;
}
std::string path;
if (!CgroupGetAttributePathForTask(attr_name, tid, &path)) {
- PLOG(ERROR) << "Failed to find attribute '" << attr_name << "'";
+ LOG(ERROR) << "Failed to find attribute '" << attr_name << "'";
return false;
}
@@ -672,4 +654,4 @@
}
return tp->IsValidForProcess(uid, pid);
-}
\ No newline at end of file
+}
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 3e4393d..d013ec8 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -1,13 +1,6 @@
{
"Cgroups": [
{
- "Controller": "blkio",
- "Path": "/dev/blkio",
- "Mode": "0775",
- "UID": "system",
- "GID": "system"
- },
- {
"Controller": "cpu",
"Path": "/dev/cpuctl",
"Mode": "0755",
@@ -39,6 +32,12 @@
{
"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 1fc66ba..f2ef316 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -76,6 +76,26 @@
"Name": "FreezerState",
"Controller": "freezer",
"File": "cgroup.freeze"
+ },
+ {
+ "Name": "BfqWeight",
+ "Controller": "io",
+ "File": "io.bfq.weight"
+ },
+ {
+ "Name": "CfqGroupIdle",
+ "Controller": "io",
+ "File": "io.group_idle"
+ },
+ {
+ "Name": "CfqWeight",
+ "Controller": "io",
+ "File": "io.weight"
+ },
+ {
+ "Name": "IoPrioClass",
+ "Controller": "io",
+ "File": "io.prio.class"
}
],
@@ -439,11 +459,39 @@
"Name": "LowIoPriority",
"Actions": [
{
- "Name": "JoinCgroup",
+ "Name": "SetAttribute",
"Params":
{
- "Controller": "blkio",
- "Path": "background"
+ "Name": "BfqWeight",
+ "Value": "10",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqGroupIdle",
+ "Value": "0",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqWeight",
+ "Value": "200",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "IoPrioClass",
+ "Value": "restrict-to-be",
+ "Optional": "true"
}
}
]
@@ -452,11 +500,39 @@
"Name": "NormalIoPriority",
"Actions": [
{
- "Name": "JoinCgroup",
+ "Name": "SetAttribute",
"Params":
{
- "Controller": "blkio",
- "Path": ""
+ "Name": "BfqWeight",
+ "Value": "100",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqGroupIdle",
+ "Value": "0",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqWeight",
+ "Value": "1000",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "IoPrioClass",
+ "Value": "restrict-to-be",
+ "Optional": "true"
}
}
]
@@ -465,11 +541,39 @@
"Name": "HighIoPriority",
"Actions": [
{
- "Name": "JoinCgroup",
+ "Name": "SetAttribute",
"Params":
{
- "Controller": "blkio",
- "Path": ""
+ "Name": "BfqWeight",
+ "Value": "100",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqGroupIdle",
+ "Value": "0",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqWeight",
+ "Value": "1000",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "IoPrioClass",
+ "Value": "promote-to-rt",
+ "Optional": "true"
}
}
]
@@ -478,11 +582,39 @@
"Name": "MaxIoPriority",
"Actions": [
{
- "Name": "JoinCgroup",
+ "Name": "SetAttribute",
"Params":
{
- "Controller": "blkio",
- "Path": ""
+ "Name": "BfqWeight",
+ "Value": "100",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqGroupIdle",
+ "Value": "0",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqWeight",
+ "Value": "1000",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "IoPrioClass",
+ "Value": "promote-to-rt",
+ "Optional": "true"
}
}
]
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index fbeedf9..4e44c91 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -212,7 +212,7 @@
if (root.isMember("Cgroups2")) {
const Json::Value& cgroups2 = root["Cgroups2"];
std::string root_path = cgroups2["Path"].asString();
- MergeCgroupToDescriptors(descriptors, cgroups2, CGROUPV2_CONTROLLER_NAME, "", 2);
+ MergeCgroupToDescriptors(descriptors, cgroups2, CGROUPV2_HIERARCHY_NAME, "", 2);
const Json::Value& childGroups = cgroups2["Controllers"];
for (Json::Value::ArrayIndex i = 0; i < childGroups.size(); ++i) {
@@ -358,7 +358,7 @@
const format::CgroupController* controller = descriptor.controller();
if (controller->version() == 2) {
- if (!strcmp(controller->name(), CGROUPV2_CONTROLLER_NAME)) {
+ if (!strcmp(controller->name(), CGROUPV2_HIERARCHY_NAME)) {
return MountV2CgroupController(descriptor);
} else {
return ActivateV2CgroupController(descriptor);
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index f51b076..d5bd47c 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -316,7 +316,7 @@
FdCacheHelper::Init(controller_.GetProcsFilePath(path_, 0, 0), fd_[ProfileAction::RCT_PROCESS]);
}
-bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_name) {
+bool SetCgroupAction::AddTidToCgroup(int tid, int fd, ResourceCacheType cache_type) const {
if (tid <= 0) {
return true;
}
@@ -332,6 +332,7 @@
return true;
}
+ const char* controller_name = controller()->name();
// ENOSPC is returned when cpuset cgroup that we are joining has no online cpus
if (errno == ENOSPC && !strcmp(controller_name, "cpuset")) {
// This is an abnormal case happening only in testing, so report it only once
@@ -345,7 +346,8 @@
<< "' into cpuset because all cpus in that cpuset are offline";
empty_cpuset_reported = true;
} else {
- PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
+ PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; path=" << path_ << "; "
+ << (cache_type == RCT_TASK ? "task" : "process");
}
return false;
@@ -356,7 +358,7 @@
std::lock_guard<std::mutex> lock(fd_mutex_);
if (FdCacheHelper::IsCached(fd_[cache_type])) {
// fd is cached, reuse it
- if (!AddTidToCgroup(id, fd_[cache_type], controller()->name())) {
+ if (!AddTidToCgroup(id, fd_[cache_type], cache_type)) {
LOG(ERROR) << "Failed to add task into cgroup";
return ProfileAction::FAIL;
}
@@ -391,7 +393,7 @@
PLOG(WARNING) << Name() << "::" << __func__ << ": failed to open " << procs_path;
return false;
}
- if (!AddTidToCgroup(pid, tmp_fd, controller()->name())) {
+ if (!AddTidToCgroup(pid, tmp_fd, RCT_PROCESS)) {
LOG(ERROR) << "Failed to add task into cgroup";
return false;
}
@@ -412,7 +414,7 @@
PLOG(WARNING) << Name() << "::" << __func__ << ": failed to open " << tasks_path;
return false;
}
- if (!AddTidToCgroup(tid, tmp_fd, controller()->name())) {
+ if (!AddTidToCgroup(tid, tmp_fd, RCT_TASK)) {
LOG(ERROR) << "Failed to add task into cgroup";
return false;
}
@@ -866,7 +868,13 @@
auto controller = cg_map.FindController(controller_name);
if (controller.HasValue()) {
- profile->Add(std::make_unique<SetCgroupAction>(controller, path));
+ if (controller.version() == 1) {
+ profile->Add(std::make_unique<SetCgroupAction>(controller, path));
+ } else {
+ LOG(WARNING) << "A JoinCgroup action in the " << profile_name
+ << " profile is used for controller " << controller_name
+ << " in the cgroup v2 hierarchy and will be ignored";
+ }
} else {
LOG(WARNING) << "JoinCgroup: controller " << controller_name << " is not found";
}
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 4663f64..16ffe63 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -160,7 +160,7 @@
android::base::unique_fd fd_[ProfileAction::RCT_COUNT];
mutable std::mutex fd_mutex_;
- static bool AddTidToCgroup(int tid, int fd, const char* controller_name);
+ bool AddTidToCgroup(int tid, int fd, ResourceCacheType cache_type) const;
CacheUseResult UseCachedFd(ResourceCacheType cache_type, int id) const;
};
diff --git a/libprocessgroup/task_profiles_test.cpp b/libprocessgroup/task_profiles_test.cpp
index 99d819a..b17e695 100644
--- a/libprocessgroup/task_profiles_test.cpp
+++ b/libprocessgroup/task_profiles_test.cpp
@@ -116,7 +116,7 @@
}
bool GetPathForTask(int tid, std::string* path) const override {
#ifdef __ANDROID__
- CHECK(CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, path));
+ CHECK(CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, path));
CHECK_GT(path->length(), 0);
if (path->rbegin()[0] != '/') {
*path += "/";
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 4d4294b..1c622ff 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -56,7 +56,7 @@
}
cc_defaults {
- name: "libutils_defaults",
+ name: "libutils_defaults_nodeps",
vendor_available: true,
product_available: true,
recovery_available: true,
@@ -69,10 +69,6 @@
"-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
- shared_libs: [
- "libcutils",
- "liblog",
- ],
sanitize: {
misc_undefined: ["integer"],
},
@@ -118,6 +114,18 @@
}
cc_defaults {
+ name: "libutils_defaults",
+ defaults: [
+ "libutils_defaults_nodeps",
+ ],
+
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ ],
+}
+
+cc_defaults {
name: "libutils_impl_defaults",
defaults: [
"libutils_defaults",
@@ -203,6 +211,7 @@
defaults: ["libutils_impl_defaults"],
cflags: [
+ "-DDEBUG_CALLBACKS=1",
"-DDEBUG_POLL_AND_WAKE=1",
"-DDEBUG_REFS=1",
"-DDEBUG_TOKENIZER=1",
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 402e43c..576c61d 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -534,7 +534,7 @@
int Looper::removeSequenceNumberLocked(SequenceNumber seq) {
#if DEBUG_CALLBACKS
- ALOGD("%p ~ removeFd - fd=%d, seq=%u", this, fd, seq);
+ ALOGD("%p ~ removeFd - seq=%" PRIu64, this, seq);
#endif
const auto& request_it = mRequests.find(seq);
diff --git a/libutils/binder/Android.bp b/libutils/binder/Android.bp
index a049f3d..60b0cb6 100644
--- a/libutils/binder/Android.bp
+++ b/libutils/binder/Android.bp
@@ -3,9 +3,9 @@
}
cc_defaults {
- name: "libutils_binder_impl_defaults",
+ name: "libutils_binder_impl_defaults_nodeps",
defaults: [
- "libutils_defaults",
+ "libutils_defaults_nodeps",
"apex-lowest-min-sdk-version",
],
native_bridge_supported: true,
@@ -30,11 +30,33 @@
afdo: true,
}
+cc_defaults {
+ name: "libutils_binder_impl_defaults",
+ defaults: [
+ "libutils_defaults",
+ "libutils_binder_impl_defaults_nodeps",
+ ],
+}
+
cc_library {
name: "libutils_binder",
defaults: ["libutils_binder_impl_defaults"],
}
+cc_library_shared {
+ name: "libutils_binder_sdk",
+ defaults: ["libutils_binder_impl_defaults_nodeps"],
+
+ header_libs: [
+ "liblog_stub",
+ ],
+
+ cflags: [
+ "-DANDROID_LOG_STUB_WEAK_PRINT",
+ "-DANDROID_UTILS_CALLSTACK_ENABLED=0",
+ ],
+}
+
cc_library {
name: "libutils_binder_test_compile",
defaults: ["libutils_binder_impl_defaults"],
diff --git a/libutils/binder/StrongPointer_test.cpp b/libutils/binder/StrongPointer_test.cpp
index f27c1f1..aa993c3 100644
--- a/libutils/binder/StrongPointer_test.cpp
+++ b/libutils/binder/StrongPointer_test.cpp
@@ -106,3 +106,17 @@
EXPECT_DEATH(sp<TypeParam>::fromExisting(foo), "");
delete foo;
}
+
+TYPED_TEST(StrongPointer, release) {
+ bool isDeleted = false;
+ TypeParam* foo = nullptr;
+ {
+ sp<TypeParam> sp1 = sp<TypeParam>::make(&isDeleted);
+ ASSERT_EQ(1, sp1->getStrongCount());
+ foo = sp1.release();
+ }
+ ASSERT_FALSE(isDeleted) << "release failed, deleted anyway when sp left scope";
+ ASSERT_EQ(1, foo->getStrongCount()) << "release mismanaged refcount";
+ foo->decStrong(nullptr);
+ ASSERT_TRUE(isDeleted) << "foo was leaked!";
+}
diff --git a/libutils/binder/include/utils/StrongPointer.h b/libutils/binder/include/utils/StrongPointer.h
index 54aa691..43c00c9 100644
--- a/libutils/binder/include/utils/StrongPointer.h
+++ b/libutils/binder/include/utils/StrongPointer.h
@@ -98,6 +98,15 @@
void clear();
+ // Releases the ownership of the object managed by this instance of sp, if any.
+ // The caller is now responsible for managing it. That is, the caller must ensure
+ // decStrong() is called when the pointer is no longer used.
+ [[nodiscard]] inline T* release() noexcept {
+ auto ret = m_ptr;
+ m_ptr = nullptr;
+ return ret;
+ }
+
// Accessors
inline T& operator* () const { return *m_ptr; }
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index cc6b64a..7deb173 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -72,6 +72,11 @@
endif
endif
+EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE :=
+ifneq ($(PRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE),)
+ EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE := export SCUDO_ALLOCATION_RING_BUFFER_SIZE $(PRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE)
+endif
+
EXPORT_GLOBAL_GCOV_OPTIONS :=
ifeq ($(NATIVE_COVERAGE),true)
EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/trace
@@ -216,6 +221,7 @@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%?$(EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_HWASAN_OPTIONS%?$(EXPORT_GLOBAL_HWASAN_OPTIONS)?g' $@
+ $(hide) sed -i -e 's?%EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE%?$(EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE)?g' $@
# Append PLATFORM_VNDK_VERSION to base name.
define append_vndk_version
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index bf6e986..7ba1f46 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -14,3 +14,4 @@
%EXPORT_GLOBAL_GCOV_OPTIONS%
%EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%
%EXPORT_GLOBAL_HWASAN_OPTIONS%
+ %EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 317f809..fb64736 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -219,26 +219,6 @@
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
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 60dcc2a..3927501 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -71,6 +71,7 @@
/dev/mtp_usb 0660 root mtp
/dev/usb_accessory 0660 root usb
/dev/tun 0660 system vpn
+/dev/hidraw* 0660 system system
# CDMA radio interface MUX
/dev/ppp 0660 radio vpn
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index cc92c68..32057b4 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -76,9 +76,6 @@
error(1, errno, "couldn't stat %s", path);
}
- // /data/user/0 is a known safe symlink.
- if (strcmp("/data/user/0", path) == 0) return;
-
// Must be a real directory, not a symlink.
if (!S_ISDIR(st.st_mode)) {
error(1, 0, "%s not a directory: %o", path, st.st_mode);
@@ -191,27 +188,17 @@
}
// Retrieve package information from system, switching egid so we can read the file.
+ pkg_info info = {.name = pkgname};
gid_t old_egid = getegid();
if (setegid(AID_PACKAGE_INFO) == -1) error(1, errno, "setegid(AID_PACKAGE_INFO) failed");
- pkg_info info;
- memset(&info, 0, sizeof(info));
- info.name = pkgname;
if (!packagelist_parse(packagelist_parse_callback, &info)) {
error(1, errno, "packagelist_parse failed");
}
-
- // Handle a multi-user data path
- if (userId > 0) {
- free(info.data_dir);
- if (asprintf(&info.data_dir, "/data/user/%d/%s", userId, pkgname) == -1) {
- error(1, errno, "asprintf failed");
- }
- }
+ if (setegid(old_egid) == -1) error(1, errno, "couldn't restore egid");
if (info.uid == 0) {
error(1, 0, "unknown package: %s", pkgname);
}
- if (setegid(old_egid) == -1) error(1, errno, "couldn't restore egid");
// Verify that user id is not too big.
if ((UID_MAX - info.uid) / AID_USER_OFFSET < (uid_t)userId) {
@@ -231,6 +218,12 @@
error(1, 0, "package not debuggable: %s", pkgname);
}
+ // Ensure we have the right data path for the specific user.
+ free(info.data_dir);
+ if (asprintf(&info.data_dir, "/data/user/%d/%s", userId, pkgname) == -1) {
+ error(1, errno, "asprintf failed");
+ }
+
// Check that the data directory path is valid.
check_data_path(pkgname, info.data_dir, userAppId);
diff --git a/trusty/OWNERS b/trusty/OWNERS
index bf16912..4016792 100644
--- a/trusty/OWNERS
+++ b/trusty/OWNERS
@@ -2,7 +2,6 @@
arve@android.com
danielangell@google.com
gmar@google.com
-marcone@google.com
mikemcternan@google.com
mmaurer@google.com
ncbray@google.com
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index f782d2a..0915eab 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -107,7 +107,11 @@
return {};
}
- assert(st.st_size >= 0);
+ if (st.st_size == 0) {
+ LOG(ERROR) << "Zero length file '" << file_name << "'";
+ return {};
+ }
+
file_size = st.st_size;
/* The dmabuf size needs to be a multiple of the page size */
@@ -123,7 +127,8 @@
BufferAllocator alloc;
unique_fd dmabuf_fd(alloc.Alloc(kDmabufSystemHeapName, file_page_size));
if (!dmabuf_fd.ok()) {
- LOG(ERROR) << "Error creating dmabuf: " << dmabuf_fd.get();
+ LOG(ERROR) << "Error creating dmabuf for " << file_page_size
+ << " bytes: " << dmabuf_fd.get();
return dmabuf_fd;
}