Merge "storageproxy: Report fsync failures with a distinct error code"
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 7cff7dc..299b5d4 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -518,20 +518,20 @@
reboot,bootloader | reboot,bootloader,* ) var=${var#reboot,} ; var=${var%,} ;;
reboot | reboot,?* ) ;;
# Aliases and Heuristics
- *wdog* | *watchdog* ) var="watchdog" ;;
- *powerkey* | *power_key* | *PowerKey* ) var="cold,powerkey" ;;
- *panic* | *kernel_panic* ) var="kernel_panic" ;;
- *thermal* ) var="shutdown,thermal" ;;
- *s3_wakeup* ) var="warm,s3_wakeup" ;;
- *hw_reset* ) var="hard,hw_reset" ;;
- *usb* ) var="cold,charger" ;;
- *rtc* ) var="cold,rtc" ;;
- *2sec_reboot* ) var="cold,rtc,2sec" ;;
- *wdt_by_pass_pwk* ) var="warm" ;;
- wdt ) var="reboot" ;;
- *tool_by_pass_pwk* ) var="reboot,tool" ;;
- *bootloader* ) var="bootloader" ;;
- * ) var="reboot" ;;
+ *wdog* | *watchdog* ) var="watchdog" ;;
+ *powerkey* | *power_on_key* | *power_key* | *PowerKey* ) var="cold,powerkey" ;;
+ *panic* | *kernel_panic* ) var="kernel_panic" ;;
+ *thermal* ) var="shutdown,thermal" ;;
+ *s3_wakeup* ) var="warm,s3_wakeup" ;;
+ *hw_reset* ) var="hard,hw_reset" ;;
+ *usb* | *power_on_cable* ) var="cold,charger" ;;
+ *rtc* ) var="cold,rtc" ;;
+ *2sec_reboot* ) var="cold,rtc,2sec" ;;
+ *wdt_by_pass_pwk* ) var="warm" ;;
+ wdt ) var="reboot" ;;
+ *tool_by_pass_pwk* ) var="reboot,tool" ;;
+ *bootloader* ) var="bootloader" ;;
+ * ) var="reboot" ;;
esac
echo ${var}
}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 2c878f0..589b99a 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -58,7 +58,7 @@
};
// Maps BootEvent used inside bootstat into statsd atom defined in
-// frameworks/base/cmds/statsd/src/atoms.proto.
+// frameworks/proto_logging/stats/atoms.proto.
const std::unordered_map<std::string_view, AtomInfo> kBootEventToAtomInfo = {
// ELAPSED_TIME
{"ro.boottime.init",
@@ -463,6 +463,18 @@
{"watchdog,gsa,hard", 215},
{"watchdog,gsa,soft", 216},
{"watchdog,pmucal", 217},
+ {"reboot,early,bl", 218},
+ {"watchdog,apc,gsa,crashed", 219},
+ {"watchdog,apc,bl31,crashed", 220},
+ {"watchdog,apc,pbl,crashed", 221},
+ {"reboot,memory_protect,hyp", 222},
+ {"reboot,tsd,pmic,main", 223},
+ {"reboot,tsd,pmic,sub", 224},
+ {"reboot,ocp,pmic,main", 225},
+ {"reboot,ocp,pmic,sub", 226},
+ {"reboot,sys_ldo_ok,pmic,main", 227},
+ {"reboot,sys_ldo_ok,pmic,sub", 228},
+ {"reboot,smpl_timeout,pmic,main", 229},
};
// Converts a string value representing the reason the system booted to an
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 5c7d847..ad0231d 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -293,13 +293,6 @@
"libdebuggerd/test/utility_test.cpp",
],
- product_variables: {
- malloc_not_svelte: {
- srcs: ["libdebuggerd/test/scudo_test.cpp"],
- header_libs: ["scudo_headers"],
- },
- },
-
target: {
android: {
srcs: [
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
index 68bfd5b..a506859 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
@@ -34,10 +34,9 @@
class ScudoCrashData {
public:
- ScudoCrashData() = default;
+ ScudoCrashData() = delete;
~ScudoCrashData() = default;
-
- bool SetErrorInfo(unwindstack::Memory* process_memory, const ProcessInfo& process_info);
+ ScudoCrashData(unwindstack::Memory* process_memory, const ProcessInfo& process_info);
bool CrashIsMine() const;
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index 9483e59..5d861f8 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -14,11 +14,6 @@
* limitations under the License.
*/
-#include <stdint.h>
-#include <unistd.h>
-
-#include <vector>
-
#include "libdebuggerd/scudo.h"
#include "libdebuggerd/tombstone.h"
@@ -30,92 +25,57 @@
#include "tombstone.pb.h"
-bool ScudoCrashData::SetErrorInfo(unwindstack::Memory* process_memory,
- const ProcessInfo& process_info) {
+std::unique_ptr<char[]> AllocAndReadFully(unwindstack::Memory* process_memory, uint64_t addr,
+ size_t size) {
+ auto buf = std::make_unique<char[]>(size);
+ if (!process_memory->ReadFully(addr, buf.get(), size)) {
+ return std::unique_ptr<char[]>();
+ }
+ return buf;
+}
+
+ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory,
+ const ProcessInfo& process_info) {
if (!process_info.has_fault_address) {
- return false;
+ return;
}
- std::vector<char> stack_depot(__scudo_get_stack_depot_size());
- if (!process_memory->ReadFully(process_info.scudo_stack_depot, stack_depot.data(),
- stack_depot.size())) {
- return false;
+ auto stack_depot = AllocAndReadFully(process_memory, process_info.scudo_stack_depot,
+ __scudo_get_stack_depot_size());
+ auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info,
+ __scudo_get_region_info_size());
+ auto ring_buffer = AllocAndReadFully(process_memory, process_info.scudo_ring_buffer,
+ __scudo_get_ring_buffer_size());
+ if (!stack_depot || !region_info || !ring_buffer) {
+ return;
}
- std::vector<char> region_info(__scudo_get_region_info_size());
- if (!process_memory->ReadFully(process_info.scudo_region_info, region_info.data(),
- region_info.size())) {
- return false;
- }
- std::vector<char> ring_buffer(__scudo_get_ring_buffer_size());
- if (!process_memory->ReadFully(process_info.scudo_ring_buffer, ring_buffer.data(),
- ring_buffer.size())) {
- return false;
- }
-
- uintptr_t page_size = getpagesize();
untagged_fault_addr_ = process_info.untagged_fault_address;
- uintptr_t fault_page = untagged_fault_addr_ & ~(page_size - 1);
+ uintptr_t fault_page = untagged_fault_addr_ & ~(PAGE_SIZE - 1);
- // Attempt to get 16 pages before the fault page and 16 pages after.
- constexpr size_t kExtraPages = 16;
- std::vector<char> memory(page_size * (kExtraPages * 2 + 1));
-
- // Read faulting page first.
- size_t memory_index = kExtraPages;
- if (!process_memory->ReadFully(fault_page, &memory[memory_index * page_size], page_size)) {
- return false;
+ uintptr_t memory_begin = fault_page - PAGE_SIZE * 16;
+ if (memory_begin > fault_page) {
+ return;
}
- // Attempt to read the pages after the fault page, stop as soon as we
- // fail to read.
- uintptr_t read_addr = fault_page;
- if (!__builtin_add_overflow(fault_page, page_size, &read_addr)) {
- memory_index++;
- for (size_t i = 0; i < kExtraPages; i++, memory_index++) {
- if (!process_memory->ReadFully(read_addr, &memory[memory_index * page_size], page_size)) {
- break;
- }
- if (__builtin_add_overflow(read_addr, page_size, &read_addr)) {
- break;
- }
- }
- }
- uintptr_t memory_end = read_addr;
-
- // Attempt to read the pages before the fault page, stop as soon as we
- // fail to read.
- memory_index = kExtraPages;
- if (fault_page > 0) {
- read_addr = fault_page - page_size;
- for (size_t i = 0; i < kExtraPages; i++, memory_index--) {
- if (!process_memory->ReadFully(read_addr, &memory[(memory_index - 1) * page_size],
- page_size)) {
- break;
- }
- if (read_addr == 0) {
- memory_index--;
- break;
- }
- read_addr -= page_size;
- }
- }
- size_t start_memory_index = memory_index;
- uintptr_t memory_begin = fault_page - (kExtraPages - memory_index) * page_size;
-
- std::vector<long> memory_tags((memory_end - memory_begin) / kTagGranuleSize);
- read_addr = memory_begin;
- for (size_t i = 0; i < memory_tags.size(); i++) {
- memory_tags[i] = process_memory->ReadTag(read_addr);
- read_addr += kTagGranuleSize;
+ uintptr_t memory_end = fault_page + PAGE_SIZE * 16;
+ if (memory_end < fault_page) {
+ return;
}
- __scudo_get_error_info(
- &error_info_, process_info.maybe_tagged_fault_address, stack_depot.data(), region_info.data(),
- ring_buffer.data(), &memory[start_memory_index * page_size],
- reinterpret_cast<const char*>(memory_tags.data()), memory_begin, memory_end - memory_begin);
+ auto memory = std::make_unique<char[]>(memory_end - memory_begin);
+ for (auto i = memory_begin; i != memory_end; i += PAGE_SIZE) {
+ process_memory->ReadFully(i, memory.get() + i - memory_begin, PAGE_SIZE);
+ }
- return true;
+ auto memory_tags = std::make_unique<char[]>((memory_end - memory_begin) / kTagGranuleSize);
+ for (auto i = memory_begin; i != memory_end; i += kTagGranuleSize) {
+ memory_tags[(i - memory_begin) / kTagGranuleSize] = process_memory->ReadTag(i);
+ }
+
+ __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(),
+ memory_begin, memory_end - memory_begin);
}
bool ScudoCrashData::CrashIsMine() const {
diff --git a/debuggerd/libdebuggerd/test/scudo_test.cpp b/debuggerd/libdebuggerd/test/scudo_test.cpp
deleted file mode 100644
index d8fc6a7..0000000
--- a/debuggerd/libdebuggerd/test/scudo_test.cpp
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include "libdebuggerd/scudo.h"
-#include "libdebuggerd/types.h"
-#include "unwindstack/Memory.h"
-
-#include "log_fake.h"
-
-#include <inttypes.h>
-
-// This needs to match the kExtraPages from ScudoCrashData::SetErrorInfo.
-constexpr uint64_t kMaxPages = 16;
-
-class MemoryAlwaysZero : public unwindstack::Memory {
- public:
- MemoryAlwaysZero() = default;
- virtual ~MemoryAlwaysZero() = default;
-
- size_t Read(uint64_t addr, void* buffer, size_t size) override {
- if (test_unreadable_addrs_.count(addr) != 0) {
- return 0;
- }
- test_read_addrs_.insert(addr);
- memset(buffer, 0, size);
- return size;
- }
-
- void TestAddUnreadableAddress(uint64_t addr) { test_unreadable_addrs_.insert(addr); }
-
- void TestClearAddresses() {
- test_read_addrs_.clear();
- test_unreadable_addrs_.clear();
- }
-
- std::set<uint64_t>& test_read_addrs() { return test_read_addrs_; }
-
- private:
- std::set<uint64_t> test_unreadable_addrs_;
-
- std::set<uint64_t> test_read_addrs_;
-};
-
-TEST(ScudoTest, no_fault_address) {
- MemoryAlwaysZero process_memory;
- ProcessInfo info;
- info.has_fault_address = false;
- info.untagged_fault_address = 0x5000;
- info.scudo_stack_depot = 0x1000;
- info.scudo_region_info = 0x2000;
- info.scudo_ring_buffer = 0x3000;
-
- ScudoCrashData crash;
- ASSERT_FALSE(crash.SetErrorInfo(&process_memory, info));
-
- info.has_fault_address = true;
- ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
-}
-
-TEST(ScudoTest, scudo_data_read_check) {
- MemoryAlwaysZero process_memory;
- ProcessInfo info;
- info.has_fault_address = true;
- info.untagged_fault_address = 0x5000;
- info.scudo_stack_depot = 0x1000;
- info.scudo_region_info = 0x2000;
- info.scudo_ring_buffer = 0x3000;
-
- ScudoCrashData crash;
- ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
-
- // Stack Depot unreadable
- process_memory.TestClearAddresses();
- process_memory.TestAddUnreadableAddress(0x1000);
- ASSERT_FALSE(crash.SetErrorInfo(&process_memory, info));
-
- // The Region Info doesn't exist for 32 bit.
-#if defined(__LP64__)
- // Region Info unreadable
- process_memory.TestClearAddresses();
- process_memory.TestAddUnreadableAddress(0x2000);
- ASSERT_FALSE(crash.SetErrorInfo(&process_memory, info));
-#endif
-
- // Ring Buffer unreadable
- process_memory.TestClearAddresses();
- process_memory.TestAddUnreadableAddress(0x3000);
- ASSERT_FALSE(crash.SetErrorInfo(&process_memory, info));
-
- // Verify that with all scudo data readable, the error info works.
- process_memory.TestClearAddresses();
- ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
-}
-
-TEST(ScudoTest, fault_page_unreadable) {
- MemoryAlwaysZero process_memory;
- ProcessInfo info;
- info.has_fault_address = true;
- info.untagged_fault_address = 0x5124;
- info.scudo_stack_depot = 0x1000;
- info.scudo_region_info = 0x2000;
- info.scudo_ring_buffer = 0x3000;
-
- ScudoCrashData crash;
- ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
-
- uint64_t fault_page = info.untagged_fault_address & ~(getpagesize() - 1);
- process_memory.TestAddUnreadableAddress(fault_page);
- ASSERT_FALSE(crash.SetErrorInfo(&process_memory, info));
-}
-
-TEST(ScudoTest, pages_before_fault_unreadable) {
- MemoryAlwaysZero process_memory;
- ProcessInfo info;
- info.has_fault_address = true;
- info.untagged_fault_address = 0x15124;
- info.scudo_stack_depot = 0x1000;
- info.scudo_region_info = 0x2000;
- info.scudo_ring_buffer = 0x3000;
-
- ScudoCrashData crash;
- ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
-
- uint64_t page_size = getpagesize();
- uint64_t fault_page = info.untagged_fault_address & ~(page_size - 1);
-
- std::vector<uint64_t> expected_reads = {0x1000, 0x2000, 0x3000};
- for (size_t i = 0; i <= kMaxPages; i++) {
- expected_reads.emplace_back(fault_page + i * page_size);
- }
-
- // Loop through and make pages before the fault page unreadable.
- for (size_t i = 1; i <= kMaxPages + 1; i++) {
- process_memory.TestClearAddresses();
- uint64_t unreadable_addr = fault_page - i * page_size;
- SCOPED_TRACE(testing::Message()
- << "Failed at unreadable address 0x" << std::hex << unreadable_addr);
- process_memory.TestAddUnreadableAddress(unreadable_addr);
- ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
- ASSERT_THAT(process_memory.test_read_addrs(),
- testing::UnorderedElementsAreArray(expected_reads));
- // Need to add the previous unreadable_addr to the list of expected addresses.
- expected_reads.emplace_back(unreadable_addr);
- }
-}
-
-TEST(ScudoTest, pages_after_fault_unreadable) {
- MemoryAlwaysZero process_memory;
- ProcessInfo info;
- info.has_fault_address = true;
- info.untagged_fault_address = 0x15124;
- info.scudo_stack_depot = 0x1000;
- info.scudo_region_info = 0x2000;
- info.scudo_ring_buffer = 0x3000;
-
- ScudoCrashData crash;
- ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
-
- uint64_t page_size = getpagesize();
- uint64_t fault_page = info.untagged_fault_address & ~(page_size - 1);
-
- std::vector<uint64_t> expected_reads = {0x1000, 0x2000, 0x3000};
- for (size_t i = 0; i <= kMaxPages; i++) {
- expected_reads.emplace_back(fault_page - i * page_size);
- }
-
- // Loop through and make pages after the fault page unreadable.
- for (size_t i = 1; i <= kMaxPages + 1; i++) {
- process_memory.TestClearAddresses();
- uint64_t unreadable_addr = fault_page + i * page_size;
- SCOPED_TRACE(testing::Message()
- << "Failed at unreadable address 0x" << std::hex << unreadable_addr);
- process_memory.TestAddUnreadableAddress(unreadable_addr);
- ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
- ASSERT_THAT(process_memory.test_read_addrs(),
- testing::UnorderedElementsAreArray(expected_reads));
- // Need to add the previous unreadable_addr to the list of expected addresses.
- expected_reads.emplace_back(unreadable_addr);
- }
-}
-
-// Make sure that if the fault address is low, you won't underflow.
-TEST(ScudoTest, fault_address_low) {
- MemoryAlwaysZero process_memory;
- ProcessInfo info;
- info.has_fault_address = true;
- info.scudo_stack_depot = 0x21000;
- info.scudo_region_info = 0x22000;
- info.scudo_ring_buffer = 0x23000;
-
- ScudoCrashData crash;
- ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
-
- uint64_t page_size = getpagesize();
- for (size_t i = 0; i < kMaxPages + 1; i++) {
- process_memory.TestClearAddresses();
- info.untagged_fault_address = 0x124 + i * getpagesize();
- SCOPED_TRACE(testing::Message()
- << "Failed with fault address 0x" << std::hex << info.untagged_fault_address);
- ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
- std::vector<uint64_t> expected_reads = {0x21000, 0x22000, 0x23000};
- uint64_t fault_page = info.untagged_fault_address & ~(page_size - 1);
- expected_reads.emplace_back(fault_page);
- for (size_t j = 1; j <= kMaxPages; j++) {
- expected_reads.emplace_back(fault_page + j * page_size);
- }
- while (fault_page != 0) {
- fault_page -= page_size;
- expected_reads.emplace_back(fault_page);
- }
- ASSERT_THAT(process_memory.test_read_addrs(),
- testing::UnorderedElementsAreArray(expected_reads));
- }
-}
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 6e1ce8f..159ebc8 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -193,9 +193,8 @@
static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
const ProcessInfo& process_info, const ThreadInfo& main_thread) {
#if defined(USE_SCUDO)
- ScudoCrashData scudo_crash_data;
- if (scudo_crash_data.SetErrorInfo(unwinder->GetProcessMemory().get(), process_info) &&
- scudo_crash_data.CrashIsMine()) {
+ ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
+ if (scudo_crash_data.CrashIsMine()) {
scudo_crash_data.AddCauseProtos(tombstone, unwinder);
return;
}
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 5c07eb0..26e6e00 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -164,6 +164,8 @@
shared_libs: [
"android.hardware.boot@1.0",
"android.hardware.boot@1.1",
+ "android.hardware.boot-V1-ndk",
+ "libboot_control_client",
"android.hardware.fastboot@1.1",
"android.hardware.health@2.0",
"android.hardware.health-V1-ndk",
@@ -190,6 +192,7 @@
"libhealthhalutils",
"libhealthshim",
"libsnapshot_cow",
+ "liblz4",
"libsnapshot_nobinder",
"update_metadata-protos",
],
@@ -254,6 +257,7 @@
"libsparse",
"libutils",
"liblog",
+ "liblz4",
"libz",
"libdiagnose_usb",
"libbase",
diff --git a/fastboot/OWNERS b/fastboot/OWNERS
index 17b3466..3dec07e 100644
--- a/fastboot/OWNERS
+++ b/fastboot/OWNERS
@@ -1,3 +1,5 @@
dvander@google.com
elsk@google.com
enh@google.com
+zhangkelvin@google.com
+
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 4ea68da..b732c76 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -79,3 +79,4 @@
#define FB_VAR_SECURITY_PATCH_LEVEL "security-patch-level"
#define FB_VAR_TREBLE_ENABLED "treble-enabled"
#define FB_VAR_MAX_FETCH_SIZE "max-fetch-size"
+#define FB_VAR_DMESG "dmesg"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 0f3a208..823783e 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -40,6 +40,7 @@
#include <storage_literals/storage_literals.h>
#include <uuid/uuid.h>
+#include "BootControlClient.h"
#include "constants.h"
#include "fastboot_device.h"
#include "flashing.h"
@@ -52,15 +53,12 @@
#endif
using android::fs_mgr::MetadataBuilder;
+using android::hal::CommandResult;
using ::android::hardware::hidl_string;
-using ::android::hardware::boot::V1_0::BoolResult;
-using ::android::hardware::boot::V1_0::CommandResult;
-using ::android::hardware::boot::V1_0::Slot;
-using ::android::hardware::boot::V1_1::MergeStatus;
using ::android::hardware::fastboot::V1_0::Result;
using ::android::hardware::fastboot::V1_0::Status;
using android::snapshot::SnapshotManager;
-using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
+using MergeStatus = android::hal::BootControlClient::MergeStatus;
using namespace android::storage_literals;
@@ -112,52 +110,65 @@
}
}
-bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args) {
- const std::unordered_map<std::string, VariableHandlers> kVariableMap = {
- {FB_VAR_VERSION, {GetVersion, nullptr}},
- {FB_VAR_VERSION_BOOTLOADER, {GetBootloaderVersion, nullptr}},
- {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
- {FB_VAR_VERSION_OS, {GetOsVersion, nullptr}},
- {FB_VAR_VERSION_VNDK, {GetVndkVersion, nullptr}},
- {FB_VAR_PRODUCT, {GetProduct, nullptr}},
- {FB_VAR_SERIALNO, {GetSerial, nullptr}},
- {FB_VAR_VARIANT, {GetVariant, nullptr}},
- {FB_VAR_SECURE, {GetSecure, nullptr}},
- {FB_VAR_UNLOCKED, {GetUnlocked, nullptr}},
- {FB_VAR_MAX_DOWNLOAD_SIZE, {GetMaxDownloadSize, nullptr}},
- {FB_VAR_CURRENT_SLOT, {::GetCurrentSlot, nullptr}},
- {FB_VAR_SLOT_COUNT, {GetSlotCount, nullptr}},
- {FB_VAR_HAS_SLOT, {GetHasSlot, GetAllPartitionArgsNoSlot}},
- {FB_VAR_SLOT_SUCCESSFUL, {GetSlotSuccessful, nullptr}},
- {FB_VAR_SLOT_UNBOOTABLE, {GetSlotUnbootable, nullptr}},
- {FB_VAR_PARTITION_SIZE, {GetPartitionSize, GetAllPartitionArgsWithSlot}},
- {FB_VAR_PARTITION_TYPE, {GetPartitionType, GetAllPartitionArgsWithSlot}},
- {FB_VAR_IS_LOGICAL, {GetPartitionIsLogical, GetAllPartitionArgsWithSlot}},
- {FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}},
- {FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
- {FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
- {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
- {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
- {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
- {FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}},
- {FB_VAR_CPU_ABI, {GetCpuAbi, nullptr}},
- {FB_VAR_SYSTEM_FINGERPRINT, {GetSystemFingerprint, nullptr}},
- {FB_VAR_VENDOR_FINGERPRINT, {GetVendorFingerprint, nullptr}},
- {FB_VAR_DYNAMIC_PARTITION, {GetDynamicPartition, nullptr}},
- {FB_VAR_FIRST_API_LEVEL, {GetFirstApiLevel, nullptr}},
- {FB_VAR_SECURITY_PATCH_LEVEL, {GetSecurityPatchLevel, nullptr}},
- {FB_VAR_TREBLE_ENABLED, {GetTrebleEnabled, nullptr}},
- {FB_VAR_MAX_FETCH_SIZE, {GetMaxFetchSize, nullptr}},
- };
+const std::unordered_map<std::string, VariableHandlers> kVariableMap = {
+ {FB_VAR_VERSION, {GetVersion, nullptr}},
+ {FB_VAR_VERSION_BOOTLOADER, {GetBootloaderVersion, nullptr}},
+ {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
+ {FB_VAR_VERSION_OS, {GetOsVersion, nullptr}},
+ {FB_VAR_VERSION_VNDK, {GetVndkVersion, nullptr}},
+ {FB_VAR_PRODUCT, {GetProduct, nullptr}},
+ {FB_VAR_SERIALNO, {GetSerial, nullptr}},
+ {FB_VAR_VARIANT, {GetVariant, nullptr}},
+ {FB_VAR_SECURE, {GetSecure, nullptr}},
+ {FB_VAR_UNLOCKED, {GetUnlocked, nullptr}},
+ {FB_VAR_MAX_DOWNLOAD_SIZE, {GetMaxDownloadSize, nullptr}},
+ {FB_VAR_CURRENT_SLOT, {::GetCurrentSlot, nullptr}},
+ {FB_VAR_SLOT_COUNT, {GetSlotCount, nullptr}},
+ {FB_VAR_HAS_SLOT, {GetHasSlot, GetAllPartitionArgsNoSlot}},
+ {FB_VAR_SLOT_SUCCESSFUL, {GetSlotSuccessful, nullptr}},
+ {FB_VAR_SLOT_UNBOOTABLE, {GetSlotUnbootable, nullptr}},
+ {FB_VAR_PARTITION_SIZE, {GetPartitionSize, GetAllPartitionArgsWithSlot}},
+ {FB_VAR_PARTITION_TYPE, {GetPartitionType, GetAllPartitionArgsWithSlot}},
+ {FB_VAR_IS_LOGICAL, {GetPartitionIsLogical, GetAllPartitionArgsWithSlot}},
+ {FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}},
+ {FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
+ {FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
+ {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
+ {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
+ {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
+ {FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}},
+ {FB_VAR_CPU_ABI, {GetCpuAbi, nullptr}},
+ {FB_VAR_SYSTEM_FINGERPRINT, {GetSystemFingerprint, nullptr}},
+ {FB_VAR_VENDOR_FINGERPRINT, {GetVendorFingerprint, nullptr}},
+ {FB_VAR_DYNAMIC_PARTITION, {GetDynamicPartition, nullptr}},
+ {FB_VAR_FIRST_API_LEVEL, {GetFirstApiLevel, nullptr}},
+ {FB_VAR_SECURITY_PATCH_LEVEL, {GetSecurityPatchLevel, nullptr}},
+ {FB_VAR_TREBLE_ENABLED, {GetTrebleEnabled, nullptr}},
+ {FB_VAR_MAX_FETCH_SIZE, {GetMaxFetchSize, nullptr}},
+};
+static bool GetVarAll(FastbootDevice* device) {
+ for (const auto& [name, handlers] : kVariableMap) {
+ GetAllVars(device, name, handlers);
+ }
+ return true;
+}
+
+const std::unordered_map<std::string, std::function<bool(FastbootDevice*)>> kSpecialVars = {
+ {"all", GetVarAll},
+ {"dmesg", GetDmesg},
+};
+
+bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args) {
if (args.size() < 2) {
return device->WriteFail("Missing argument");
}
- // Special case: return all variables that we can.
- if (args[1] == "all") {
- for (const auto& [name, handlers] : kVariableMap) {
- GetAllVars(device, name, handlers);
+ // "all" and "dmesg" are multiline and handled specially.
+ auto found_special = kSpecialVars.find(args[1]);
+ if (found_special != kSpecialVars.end()) {
+ if (!found_special->second(device)) {
+ return false;
}
return device->WriteOkay("");
}
@@ -304,7 +315,7 @@
"set_active command is not allowed on locked devices");
}
- Slot slot;
+ int32_t slot = 0;
if (!GetSlotNumber(args[1], &slot)) {
// Slot suffix needs to be between 'a' and 'z'.
return device->WriteStatus(FastbootResult::FAIL, "Bad slot suffix");
@@ -316,7 +327,7 @@
return device->WriteStatus(FastbootResult::FAIL,
"Cannot set slot: boot control HAL absent");
}
- if (slot >= boot_control_hal->getNumberSlots()) {
+ if (slot >= boot_control_hal->GetNumSlots()) {
return device->WriteStatus(FastbootResult::FAIL, "Slot out of range");
}
@@ -345,10 +356,8 @@
}
}
- CommandResult ret;
- auto cb = [&ret](CommandResult result) { ret = result; };
- auto result = boot_control_hal->setActiveBootSlot(slot, cb);
- if (result.isOk() && ret.success) {
+ CommandResult ret = boot_control_hal->SetActiveBootSlot(slot);
+ if (ret.success) {
// Save as slot suffix to match the suffix format as returned from
// the boot control HAL.
auto current_slot = "_" + args[1];
@@ -669,9 +678,14 @@
if (args[1] == "cancel") {
switch (status) {
case MergeStatus::SNAPSHOTTED:
- case MergeStatus::MERGING:
- hal->setSnapshotMergeStatus(MergeStatus::CANCELLED);
+ case MergeStatus::MERGING: {
+ const auto ret = hal->SetSnapshotMergeStatus(MergeStatus::CANCELLED);
+ if (!ret.success) {
+ device->WriteFail("Failed to SetSnapshotMergeStatus(MergeStatus::CANCELLED) " +
+ ret.errMsg);
+ }
break;
+ }
default:
break;
}
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index ae225de..4932e5c 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -18,6 +18,7 @@
#include <algorithm>
+#include <BootControlClient.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
@@ -38,9 +39,8 @@
using android::fs_mgr::EnsurePathUnmounted;
using android::fs_mgr::Fstab;
using ::android::hardware::hidl_string;
-using ::android::hardware::boot::V1_0::IBootControl;
-using ::android::hardware::boot::V1_0::Slot;
using ::android::hardware::fastboot::V1_1::IFastboot;
+using BootControlClient = FastbootDevice::BootControlClient;
namespace sph = std::placeholders;
@@ -85,7 +85,7 @@
{FB_CMD_SNAPSHOT_UPDATE, SnapshotUpdateHandler},
{FB_CMD_FETCH, FetchHandler},
}),
- boot_control_hal_(IBootControl::getService()),
+ boot_control_hal_(BootControlClient::WaitForService()),
health_hal_(get_health_service()),
fastboot_hal_(IFastboot::getService()),
active_slot_("") {
@@ -95,10 +95,6 @@
transport_ = std::make_unique<ClientUsbTransport>();
}
- if (boot_control_hal_) {
- boot1_1_ = android::hardware::boot::V1_1::IBootControl::castFrom(boot_control_hal_);
- }
-
// Make sure cache is unmounted, since recovery will have mounted it for
// logging.
Fstab fstab;
@@ -125,12 +121,17 @@
if (!boot_control_hal_) {
return "";
}
- std::string suffix;
- auto cb = [&suffix](hidl_string s) { suffix = s; };
- boot_control_hal_->getSuffix(boot_control_hal_->getCurrentSlot(), cb);
+ std::string suffix = boot_control_hal_->GetSuffix(boot_control_hal_->GetCurrentSlot());
return suffix;
}
+BootControlClient* FastbootDevice::boot1_1() const {
+ if (boot_control_hal_->GetVersion() >= android::hal::BootControlVersion::BOOTCTL_V1_1) {
+ return boot_control_hal_.get();
+ }
+ return nullptr;
+}
+
bool FastbootDevice::WriteStatus(FastbootResult result, const std::string& message) {
constexpr size_t kResponseReasonSize = 4;
constexpr size_t kNumResponseTypes = 4; // "FAIL", "OKAY", "INFO", "DATA"
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
index 91ffce3..9df8fa5 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -22,9 +22,8 @@
#include <utility>
#include <vector>
+#include <BootControlClient.h>
#include <aidl/android/hardware/health/IHealth.h>
-#include <android/hardware/boot/1.0/IBootControl.h>
-#include <android/hardware/boot/1.1/IBootControl.h>
#include <android/hardware/fastboot/1.1/IFastboot.h>
#include "commands.h"
@@ -33,6 +32,7 @@
class FastbootDevice {
public:
+ using BootControlClient = android::hal::BootControlClient;
FastbootDevice();
~FastbootDevice();
@@ -50,10 +50,8 @@
std::vector<char>& download_data() { return download_data_; }
Transport* get_transport() { return transport_.get(); }
- android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal() {
- return boot_control_hal_;
- }
- android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1() { return boot1_1_; }
+ BootControlClient* boot_control_hal() const { return boot_control_hal_.get(); }
+ BootControlClient* boot1_1() const;
android::sp<android::hardware::fastboot::V1_1::IFastboot> fastboot_hal() {
return fastboot_hal_;
}
@@ -65,8 +63,7 @@
const std::unordered_map<std::string, CommandHandler> kCommandMap;
std::unique_ptr<Transport> transport_;
- android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal_;
- android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1_;
+ std::unique_ptr<BootControlClient> boot_control_hal_;
std::shared_ptr<aidl::android::hardware::health::IHealth> health_hal_;
android::sp<android::hardware::fastboot::V1_1::IFastboot> fastboot_hal_;
std::vector<char> download_data_;
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 3302c43..e12ee64 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -36,7 +36,6 @@
using namespace android::fs_mgr;
using namespace std::chrono_literals;
using android::base::unique_fd;
-using android::hardware::boot::V1_0::Slot;
namespace {
@@ -137,7 +136,7 @@
return true;
}
-bool GetSlotNumber(const std::string& slot, Slot* number) {
+bool GetSlotNumber(const std::string& slot, int32_t* number) {
if (slot.size() != 1) {
return false;
}
@@ -204,7 +203,7 @@
size_t num_slots = 1;
auto boot_control_hal = device->boot_control_hal();
if (boot_control_hal) {
- num_slots = boot_control_hal->getNumberSlots();
+ num_slots = boot_control_hal->GetNumSlots();
}
bool ok = true;
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index 6e1453f..a62125e 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -21,7 +21,6 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
-#include <android/hardware/boot/1.0/IBootControl.h>
#include <fstab/fstab.h>
#include <liblp/liblp.h>
@@ -124,7 +123,7 @@
bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle,
int flags = O_WRONLY);
-bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number);
+bool GetSlotNumber(const std::string& slot, int32_t* number);
std::vector<std::string> ListPartitions(FastbootDevice* device);
bool GetDeviceLockStatus();
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 0cf4699..b6eb2cd 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -17,6 +17,7 @@
#include "variables.h"
#include <inttypes.h>
+#include <stdio.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -28,6 +29,7 @@
#include <fs_mgr.h>
#include <liblp/liblp.h>
+#include "BootControlClient.h"
#include "fastboot_device.h"
#include "flashing.h"
#include "utility.h"
@@ -38,14 +40,12 @@
static constexpr bool kEnableFetch = false;
#endif
-using ::android::hardware::boot::V1_0::BoolResult;
-using ::android::hardware::boot::V1_0::Slot;
-using ::android::hardware::boot::V1_1::MergeStatus;
+using MergeStatus = android::hal::BootControlClient::MergeStatus;
using ::android::hardware::fastboot::V1_0::FileSystemType;
using ::android::hardware::fastboot::V1_0::Result;
using ::android::hardware::fastboot::V1_0::Status;
-using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
using namespace android::fs_mgr;
+using namespace std::string_literals;
constexpr char kFastbootProtocolVersion[] = "0.4";
@@ -208,7 +208,7 @@
if (!boot_control_hal) {
*message = "0";
} else {
- *message = std::to_string(boot_control_hal->getNumberSlots());
+ *message = std::to_string(boot_control_hal->GetNumSlots());
}
return true;
}
@@ -219,7 +219,7 @@
*message = "Missing argument";
return false;
}
- Slot slot;
+ int32_t slot = -1;
if (!GetSlotNumber(args[0], &slot)) {
*message = "Invalid slot";
return false;
@@ -229,7 +229,7 @@
*message = "Device has no slots";
return false;
}
- if (boot_control_hal->isSlotMarkedSuccessful(slot) != BoolResult::TRUE) {
+ if (boot_control_hal->IsSlotMarkedSuccessful(slot).value_or(false)) {
*message = "no";
} else {
*message = "yes";
@@ -243,7 +243,7 @@
*message = "Missing argument";
return false;
}
- Slot slot;
+ int32_t slot = -1;
if (!GetSlotNumber(args[0], &slot)) {
*message = "Invalid slot";
return false;
@@ -253,7 +253,7 @@
*message = "Device has no slots";
return false;
}
- if (boot_control_hal->isSlotBootable(slot) != BoolResult::TRUE) {
+ if (!boot_control_hal->IsSlotBootable(slot).value_or(false)) {
*message = "yes";
} else {
*message = "no";
@@ -518,3 +518,36 @@
*message = android::base::StringPrintf("0x%X", kMaxFetchSizeDefault);
return true;
}
+
+bool GetDmesg(FastbootDevice* device) {
+ if (GetDeviceLockStatus()) {
+ return device->WriteFail("Cannot use when device flashing is locked");
+ }
+
+ std::unique_ptr<FILE, decltype(&::fclose)> fp(popen("/system/bin/dmesg", "re"), ::fclose);
+ if (!fp) {
+ PLOG(ERROR) << "popen /system/bin/dmesg";
+ return device->WriteFail("Unable to run dmesg: "s + strerror(errno));
+ }
+
+ ssize_t rv;
+ size_t n = 0;
+ char* str = nullptr;
+ while ((rv = ::getline(&str, &n, fp.get())) > 0) {
+ if (str[rv - 1] == '\n') {
+ rv--;
+ }
+ device->WriteInfo(std::string(str, rv));
+ }
+
+ int saved_errno = errno;
+ ::free(str);
+
+ if (rv < 0 && saved_errno) {
+ LOG(ERROR) << "dmesg getline: " << strerror(saved_errno);
+ device->WriteFail("Unable to read dmesg: "s + strerror(saved_errno));
+ return false;
+ }
+
+ return true;
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index f40a025..aa4d9fc 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -83,6 +83,9 @@
bool GetMaxFetchSize(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
std::string* message);
+// Complex cases.
+bool GetDmesg(FastbootDevice* device);
+
// Helpers for getvar all.
std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
std::vector<std::vector<std::string>> GetAllPartitionArgsNoSlot(FastbootDevice* device);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 79c3ac7..46190f2 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1683,10 +1683,9 @@
return size;
}
-static void fb_perform_format(
- const std::string& partition, int skip_if_not_supported,
+static void fb_perform_format(const std::string& partition, int skip_if_not_supported,
const std::string& type_override, const std::string& size_override,
- const std::string& initial_dir, const unsigned fs_options) {
+ const unsigned fs_options) {
std::string partition_type, partition_size;
struct fastboot_buffer buf;
@@ -1748,8 +1747,7 @@
eraseBlkSize = fb_get_flash_block_size("erase-block-size");
logicalBlkSize = fb_get_flash_block_size("logical-block-size");
- if (fs_generator_generate(gen, output.path, size, initial_dir,
- eraseBlkSize, logicalBlkSize, fs_options)) {
+ if (fs_generator_generate(gen, output.path, size, eraseBlkSize, logicalBlkSize, fs_options)) {
die("Cannot generate image for %s", partition.c_str());
}
@@ -2091,7 +2089,7 @@
std::string partition = next_arg(&args);
auto format = [&](const std::string& partition) {
- fb_perform_format(partition, 0, type_override, size_override, "", fs_options);
+ fb_perform_format(partition, 0, type_override, size_override, fs_options);
};
do_for_partitions(partition, slot_override, format, true);
} else if (command == "signature") {
@@ -2282,7 +2280,7 @@
}
if (partition_type.empty()) continue;
fb->Erase(partition);
- fb_perform_format(partition, 1, partition_type, "", "", fs_options);
+ fb_perform_format(partition, 1, partition_type, "", fs_options);
}
}
if (wants_set_active) {
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index d268a50..c8d1b59 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -111,8 +111,7 @@
}
#endif
-static int generate_ext4_image(const char* fileName, long long partSize,
- const std::string& initial_dir, unsigned eraseBlkSize,
+static int generate_ext4_image(const char* fileName, long long partSize, unsigned eraseBlkSize,
unsigned logicalBlkSize, const unsigned fsOptions) {
static constexpr int block_size = 4096;
const std::string exec_dir = android::base::GetExecutableDirectory();
@@ -163,16 +162,7 @@
if (ret != 0) {
return -1;
}
-
- if (initial_dir.empty()) {
- return 0;
- }
-
- const std::string e2fsdroid_path = exec_dir + "/e2fsdroid";
- std::vector<const char*> e2fsdroid_args = {e2fsdroid_path.c_str(), "-f", initial_dir.c_str(),
- fileName, nullptr};
-
- return exec_cmd(e2fsdroid_args[0], e2fsdroid_args.data(), nullptr);
+ return 0;
}
enum {
@@ -188,8 +178,7 @@
// clang-format on
};
-static int generate_f2fs_image(const char* fileName, long long partSize,
- const std::string& initial_dir, unsigned /* unused */,
+static int generate_f2fs_image(const char* fileName, long long partSize, unsigned /* unused */,
unsigned /* unused */, const unsigned fsOptions) {
const std::string exec_dir = android::base::GetExecutableDirectory();
const std::string mkf2fs_path = exec_dir + "/make_f2fs";
@@ -227,19 +216,6 @@
if (ret != 0) {
return -1;
}
-
- if (initial_dir.empty()) {
- return 0;
- }
-
- const std::string sload_path = exec_dir + "/sload_f2fs";
- std::vector<const char*> sload_args = {sload_path.c_str(), "-S",
- "-f", initial_dir.c_str(), fileName, nullptr};
-
- ret = exec_cmd(sload_args[0], sload_args.data(), nullptr);
- if (ret != 0 && ret != FSCK_ERROR_CORRECTED) {
- return -1;
- }
return 0;
}
@@ -247,8 +223,8 @@
const char* fs_type; //must match what fastboot reports for partition type
//returns 0 or error value
- int (*generate)(const char* fileName, long long partSize, const std::string& initial_dir,
- unsigned eraseBlkSize, unsigned logicalBlkSize, const unsigned fsOptions);
+ int (*generate)(const char* fileName, long long partSize, unsigned eraseBlkSize,
+ unsigned logicalBlkSize, const unsigned fsOptions);
} generators[] = {
{ "ext4", generate_ext4_image},
@@ -265,7 +241,7 @@
}
int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
- const std::string& initial_dir, unsigned eraseBlkSize,
- unsigned logicalBlkSize, const unsigned fsOptions) {
- return gen->generate(fileName, partSize, initial_dir, eraseBlkSize, logicalBlkSize, fsOptions);
+ unsigned eraseBlkSize, unsigned logicalBlkSize,
+ const unsigned fsOptions) {
+ return gen->generate(fileName, partSize, eraseBlkSize, logicalBlkSize, fsOptions);
}
diff --git a/fastboot/fs.h b/fastboot/fs.h
index f832938..5ae473b 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -13,5 +13,5 @@
const struct fs_generator* fs_get_generator(const std::string& fs_type);
int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
- const std::string& initial_dir, unsigned eraseBlkSize = 0,
- unsigned logicalBlkSize = 0, unsigned fsOptions = 0);
+ unsigned eraseBlkSize = 0, unsigned logicalBlkSize = 0,
+ unsigned fsOptions = 0);
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index c12a672..c0e3161 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -90,9 +90,6 @@
#define SYSFS_EXT4_VERITY "/sys/fs/ext4/features/verity"
#define SYSFS_EXT4_CASEFOLD "/sys/fs/ext4/features/casefold"
-// FIXME: this should be in system/extras
-#define EXT4_FEATURE_COMPAT_STABLE_INODES 0x0800
-
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
using android::base::Basename;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 06368b8..43961da 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -439,34 +439,6 @@
return fstab_result;
}
-// Return the path to the fstab file. There may be multiple fstab files; the
-// one that is returned will be the first that exists of fstab.<fstab_suffix>,
-// fstab.<hardware>, and fstab.<hardware.platform>. The fstab is searched for
-// in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in
-// the first stage ramdisk during early boot. Previously, the first stage
-// ramdisk's copy of the fstab had to be located in the root directory, but now
-// the system/etc directory is supported too and is the preferred location.
-std::string GetFstabPath() {
- for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
- std::string suffix;
-
- if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
-
- for (const char* prefix : {// late-boot/post-boot locations
- "/odm/etc/fstab.", "/vendor/etc/fstab.",
- // early boot locations
- "/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.",
- "/fstab.", "/first_stage_ramdisk/fstab."}) {
- std::string fstab_path = prefix + suffix;
- if (access(fstab_path.c_str(), F_OK) == 0) {
- return fstab_path;
- }
- }
- }
-
- return "";
-}
-
/* Extracts <device>s from the by-name symlinks specified in a fstab:
* /dev/block/<type>/<device>/by-name/<partition>
*
@@ -510,37 +482,50 @@
return boot_devices;
}
-FstabEntry BuildDsuUserdataFstabEntry() {
- constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
-
- FstabEntry userdata = {
- .blk_device = "userdata_gsi",
- .mount_point = "/data",
- .fs_type = "ext4",
- .flags = kFlags,
- .reserved_size = 128 * 1024 * 1024,
- };
- userdata.fs_mgr_flags.wait = true;
- userdata.fs_mgr_flags.check = true;
- userdata.fs_mgr_flags.logical = true;
- userdata.fs_mgr_flags.quota = true;
- userdata.fs_mgr_flags.late_mount = true;
- userdata.fs_mgr_flags.formattable = true;
- return userdata;
-}
-
-bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
- auto iter = std::remove_if(fstab->begin(), fstab->end(),
- [&](const auto& entry) { return entry.mount_point == mount_point; });
- if (iter != fstab->end()) {
- fstab->erase(iter, fstab->end());
- return true;
+template <typename Pred>
+std::vector<FstabEntry*> GetEntriesByPred(Fstab* fstab, const Pred& pred) {
+ if (fstab == nullptr) {
+ return {};
}
- return false;
+ std::vector<FstabEntry*> entries;
+ for (auto&& entry : *fstab) {
+ if (pred(entry)) {
+ entries.push_back(&entry);
+ }
+ }
+ return entries;
}
} // namespace
+// Return the path to the fstab file. There may be multiple fstab files; the
+// one that is returned will be the first that exists of fstab.<fstab_suffix>,
+// fstab.<hardware>, and fstab.<hardware.platform>. The fstab is searched for
+// in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in
+// the first stage ramdisk during early boot. Previously, the first stage
+// ramdisk's copy of the fstab had to be located in the root directory, but now
+// the system/etc directory is supported too and is the preferred location.
+std::string GetFstabPath() {
+ for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
+ std::string suffix;
+
+ if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
+
+ for (const char* prefix : {// late-boot/post-boot locations
+ "/odm/etc/fstab.", "/vendor/etc/fstab.",
+ // early boot locations
+ "/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.",
+ "/fstab.", "/first_stage_ramdisk/fstab."}) {
+ std::string fstab_path = prefix + suffix;
+ if (access(fstab_path.c_str(), F_OK) == 0) {
+ return fstab_path;
+ }
+ }
+ }
+
+ return "";
+}
+
bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out) {
const int expected_fields = proc_mounts ? 4 : 5;
@@ -599,38 +584,28 @@
void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,
const std::vector<std::string>& dsu_partitions) {
static constexpr char kDsuKeysDir[] = "/avb";
- // Convert userdata
- // Inherit fstab properties for userdata.
- FstabEntry userdata;
- if (FstabEntry* entry = GetEntryForMountPoint(fstab, "/data")) {
- userdata = *entry;
- userdata.blk_device = android::gsi::kDsuUserdata;
- userdata.fs_mgr_flags.logical = true;
- userdata.fs_mgr_flags.formattable = true;
- if (!userdata.metadata_key_dir.empty()) {
- userdata.metadata_key_dir = android::gsi::GetDsuMetadataKeyDir(dsu_slot);
- }
- } else {
- userdata = BuildDsuUserdataFstabEntry();
- }
-
- if (EraseFstabEntry(fstab, "/data")) {
- fstab->emplace_back(userdata);
- }
-
- // Convert others
for (auto&& partition : dsu_partitions) {
if (!EndsWith(partition, gsi::kDsuPostfix)) {
continue;
}
- // userdata has been handled
- if (partition == android::gsi::kDsuUserdata) {
- continue;
- }
// scratch is handled by fs_mgr_overlayfs
if (partition == android::gsi::kDsuScratch) {
continue;
}
+ // Convert userdata partition.
+ if (partition == android::gsi::kDsuUserdata) {
+ for (auto&& entry : GetEntriesForMountPoint(fstab, "/data")) {
+ entry->blk_device = android::gsi::kDsuUserdata;
+ entry->fs_mgr_flags.logical = true;
+ entry->fs_mgr_flags.formattable = true;
+ if (!entry->metadata_key_dir.empty()) {
+ entry->metadata_key_dir = android::gsi::GetDsuMetadataKeyDir(dsu_slot);
+ }
+ }
+ continue;
+ }
+ // Convert RO partitions.
+ //
// dsu_partition_name = corresponding_partition_name + kDsuPostfix
// e.g.
// system_gsi for system
@@ -638,45 +613,51 @@
// vendor_gsi for vendor
std::string lp_name = partition.substr(0, partition.length() - strlen(gsi::kDsuPostfix));
std::string mount_point = "/" + lp_name;
- std::vector<FstabEntry*> entries = GetEntriesForMountPoint(fstab, mount_point);
- if (entries.empty()) {
- FstabEntry entry = {
- .blk_device = partition,
- // .logical_partition_name is required to look up AVB Hashtree descriptors.
- .logical_partition_name = "system",
- .mount_point = mount_point,
- .fs_type = "ext4",
- .flags = MS_RDONLY,
- .fs_options = "barrier=1",
- .avb_keys = kDsuKeysDir,
- };
- entry.fs_mgr_flags.wait = true;
- entry.fs_mgr_flags.logical = true;
- entry.fs_mgr_flags.first_stage_mount = true;
- fstab->emplace_back(entry);
- } else {
- // If the corresponding partition exists, transform all its Fstab
- // by pointing .blk_device to the DSU partition.
- for (auto&& entry : entries) {
- entry->blk_device = partition;
- // AVB keys for DSU should always be under kDsuKeysDir.
- entry->avb_keys = kDsuKeysDir;
- entry->fs_mgr_flags.logical = true;
+
+ // List of fs_type entries we're lacking, need to synthesis these later.
+ std::vector<std::string> lack_fs_list = {"ext4", "erofs"};
+
+ // Only support early mount (first_stage_mount) partitions.
+ auto pred = [&mount_point](const FstabEntry& entry) {
+ return entry.fs_mgr_flags.first_stage_mount && entry.mount_point == mount_point;
+ };
+
+ // Transform all matching entries and assume they are all adjacent for simplicity.
+ for (auto&& entry : GetEntriesByPred(fstab, pred)) {
+ // .blk_device is replaced with the DSU partition.
+ entry->blk_device = partition;
+ // .avb_keys hints first_stage_mount to load the chained-vbmeta image from partition
+ // footer. See aosp/932779 for more details.
+ entry->avb_keys = kDsuKeysDir;
+ // .logical_partition_name is required to look up AVB Hashtree descriptors.
+ entry->logical_partition_name = lp_name;
+ entry->fs_mgr_flags.logical = true;
+ entry->fs_mgr_flags.slot_select = false;
+ entry->fs_mgr_flags.slot_select_other = false;
+
+ if (auto it = std::find(lack_fs_list.begin(), lack_fs_list.end(), entry->fs_type);
+ it != lack_fs_list.end()) {
+ lack_fs_list.erase(it);
}
- // Make sure the ext4 is included to support GSI.
- auto partition_ext4 =
- std::find_if(fstab->begin(), fstab->end(), [&](const auto& entry) {
- return entry.mount_point == mount_point && entry.fs_type == "ext4";
- });
- if (partition_ext4 == fstab->end()) {
- auto new_entry = *GetEntryForMountPoint(fstab, mount_point);
- new_entry.fs_type = "ext4";
- auto it = std::find_if(fstab->rbegin(), fstab->rend(),
- [&mount_point](const auto& entry) {
- return entry.mount_point == mount_point;
- });
- auto end_of_mount_point_group = fstab->begin() + std::distance(it, fstab->rend());
- fstab->insert(end_of_mount_point_group, new_entry);
+ }
+
+ if (!lack_fs_list.empty()) {
+ // Insert at the end of the existing mountpoint group, or at the end of fstab.
+ // We assume there is at most one matching mountpoint group, which is the common case.
+ auto it = std::find_if_not(std::find_if(fstab->begin(), fstab->end(), pred),
+ fstab->end(), pred);
+ for (const auto& fs_type : lack_fs_list) {
+ it = std::next(fstab->insert(it, {.blk_device = partition,
+ .logical_partition_name = lp_name,
+ .mount_point = mount_point,
+ .fs_type = fs_type,
+ .flags = MS_RDONLY,
+ .avb_keys = kDsuKeysDir,
+ .fs_mgr_flags{
+ .wait = true,
+ .logical = true,
+ .first_stage_mount = true,
+ }}));
}
}
}
@@ -721,22 +702,13 @@
}
if (!is_proc_mounts) {
if (!access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
- // This is expected to fail if host is android Q, since Q doesn't
- // support DSU slotting. The DSU "active" indicator file would be
- // non-existent or empty if DSU is enabled within the guest system.
- // In that case, just use the default slot name "dsu".
std::string dsu_slot;
- if (!android::gsi::GetActiveDsu(&dsu_slot) && errno != ENOENT) {
+ if (!android::gsi::GetActiveDsu(&dsu_slot)) {
PERROR << __FUNCTION__ << "(): failed to get active DSU slot";
return false;
}
- if (dsu_slot.empty()) {
- dsu_slot = "dsu";
- LWARNING << __FUNCTION__ << "(): assuming default DSU slot: " << dsu_slot;
- }
- // This file is non-existent on Q vendor.
std::string lp_names;
- if (!ReadFileToString(gsi::kGsiLpNamesFile, &lp_names) && errno != ENOENT) {
+ if (!ReadFileToString(gsi::kGsiLpNamesFile, &lp_names)) {
PERROR << __FUNCTION__ << "(): failed to read DSU LP names";
return false;
}
@@ -832,7 +804,7 @@
std::string default_fstab_path;
// Use different fstab paths for normal boot and recovery boot, respectively
- if (access("/system/bin/recovery", F_OK) == 0) {
+ if ((access("/sbin/recovery", F_OK) == 0) || (access("/system/bin/recovery", F_OK) == 0)) {
default_fstab_path = "/etc/recovery.fstab";
} else { // normal boot
default_fstab_path = GetFstabPath();
@@ -865,18 +837,8 @@
}
std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path) {
- std::vector<FstabEntry*> entries;
- if (fstab == nullptr) {
- return entries;
- }
-
- for (auto& entry : *fstab) {
- if (entry.mount_point == path) {
- entries.emplace_back(&entry);
- }
- }
-
- return entries;
+ return GetEntriesByPred(fstab,
+ [&path](const FstabEntry& entry) { return entry.mount_point == path; });
}
std::set<std::string> GetBootDevices() {
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 82b5275..07eaf58 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -642,6 +642,10 @@
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;
@@ -1140,7 +1144,13 @@
return 0;
}
- return std::min(super_info.size, (uint64_t(s.f_frsize) * s.f_bfree) / 2);
+ auto ideal_size = std::min(super_info.size, (uint64_t(s.f_frsize) * s.f_bfree) / 2);
+
+ // Align up to the filesystem block size.
+ if (auto remainder = ideal_size % s.f_bsize; remainder > 0) {
+ ideal_size += s.f_bsize - remainder;
+ }
+ return ideal_size;
}
static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists, bool* change) {
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 8f200a8..689d18b 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -95,6 +95,8 @@
// Exported for testability. Regular users should use ReadFstabFromFile().
bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out);
+// Exported for testability. Regular users should use ReadDefaultFstab().
+std::string GetFstabPath();
bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
bool ReadFstabFromDt(Fstab* fstab, bool verbose = true);
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 7472949..fb104b7 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -14,11 +14,13 @@
// limitations under the License.
//
+#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/vfs.h>
#include <chrono>
#include <iostream>
@@ -26,12 +28,14 @@
#include <android-base/file.h>
#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <ext4_utils/ext4_utils.h>
#include <fs_mgr/file_wait.h>
#include <gtest/gtest.h>
#include <libdm/dm.h>
+#include <libdm/loop_control.h>
#include <libfiemap/image_manager.h>
#include "utility.h"
@@ -46,7 +50,7 @@
using android::fs_mgr::WaitForFile;
static std::string gDataPath;
-static std::string gDataMountPath;
+static std::string gTestDir;
static constexpr char kMetadataPath[] = "/metadata/gsi/test";
static constexpr uint64_t kTestImageSize = 1024 * 1024;
@@ -178,6 +182,119 @@
INSTANTIATE_TEST_SUITE_P(IsSubdirTest, IsSubdirTest, ::testing::ValuesIn(IsSubdirTestValues()));
+// This allows test cases for filesystems with larger than 4KiB alignment.
+// It creates a loop device, formats it with a FAT filesystem, and then
+// creates an ImageManager so backing images can be created on that filesystem.
+class VfatTest : public ::testing::Test {
+ protected:
+ // 64MB Filesystem and 32k block size by default
+ static constexpr uint64_t kBlockSize = 32768;
+ static constexpr uint64_t kFilesystemSize = 64 * 1024 * 1024;
+
+ void SetUp() override {
+ const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
+ base_name_ = tinfo->name();
+
+ fs_path_ = gTestDir + "/vfat.img";
+ uint64_t count = kFilesystemSize / kBlockSize;
+ std::string dd_cmd =
+ ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
+ " count=%" PRIu64 " > /dev/null 2>&1",
+ fs_path_.c_str(), kBlockSize, count);
+ // create mount point
+ mntpoint_ = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
+ if (mkdir(mntpoint_.c_str(), S_IRWXU) < 0) {
+ ASSERT_EQ(errno, EEXIST) << strerror(errno);
+ }
+
+ // create file for the file system
+ int ret = system(dd_cmd.c_str());
+ ASSERT_EQ(ret, 0);
+
+ // Get and attach a loop device to the filesystem we created
+ loop_device_.emplace(fs_path_, 10s);
+ ASSERT_TRUE(loop_device_->valid());
+
+ // create file system
+ uint64_t sectors = kFilesystemSize / 512;
+ std::string mkfs_cmd =
+ ::android::base::StringPrintf("/system/bin/newfs_msdos -A -O Android -s %" PRIu64
+ " -b %" PRIu64 " %s > /dev/null 2>&1",
+ sectors, kBlockSize, loop_device_->device().c_str());
+ ret = system(mkfs_cmd.c_str());
+ ASSERT_EQ(ret, 0);
+
+ // Create a wrapping DM device to prevent gsid taking the loopback path.
+ auto& dm = DeviceMapper::Instance();
+ DmTable table;
+ table.Emplace<DmTargetLinear>(0, kFilesystemSize / 512, loop_device_->device(), 0);
+
+ dm_name_ = android::base::Basename(loop_device_->device()) + "-wrapper";
+ ASSERT_TRUE(dm.CreateDevice(dm_name_, table, &dm_path_, 10s));
+
+ // mount the file system
+ ASSERT_EQ(mount(dm_path_.c_str(), mntpoint_.c_str(), "vfat", 0, nullptr), 0)
+ << strerror(errno);
+ }
+
+ void TearDown() override {
+ // Clear up anything backed on the temporary FS.
+ if (manager_) {
+ manager_->UnmapImageIfExists(base_name_);
+ manager_->DeleteBackingImage(base_name_);
+ }
+
+ // Unmount temporary FS.
+ if (umount(mntpoint_.c_str()) < 0) {
+ ASSERT_EQ(errno, EINVAL) << strerror(errno);
+ }
+
+ // Destroy the dm wrapper.
+ auto& dm = DeviceMapper::Instance();
+ ASSERT_TRUE(dm.DeleteDeviceIfExists(dm_name_));
+
+ // Destroy the loop device.
+ loop_device_ = {};
+
+ // Destroy the temporary FS.
+ if (rmdir(mntpoint_.c_str()) < 0) {
+ ASSERT_EQ(errno, ENOENT) << strerror(errno);
+ }
+ if (unlink(fs_path_.c_str()) < 0) {
+ ASSERT_EQ(errno, ENOENT) << strerror(errno);
+ }
+ }
+
+ std::string base_name_;
+ std::string mntpoint_;
+ std::string fs_path_;
+ std::optional<LoopDevice> loop_device_;
+ std::string dm_name_;
+ std::string dm_path_;
+ std::unique_ptr<ImageManager> manager_;
+};
+
+// The actual size of the block device should be the requested size. For
+// example, a 16KB image should be mapped as a 16KB device, even if the
+// underlying filesystem requires 32KB to be fallocated.
+TEST_F(VfatTest, DeviceIsRequestedSize) {
+ manager_ = ImageManager::Open(kMetadataPath, mntpoint_);
+ ASSERT_NE(manager_, nullptr);
+
+ manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
+
+ // Create something not aligned to the backing fs block size.
+ constexpr uint64_t kTestSize = (kBlockSize * 64) - (kBlockSize / 2);
+ ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestSize, false, nullptr));
+
+ std::string path;
+ ASSERT_TRUE(manager_->MapImageDevice(base_name_, 10s, &path));
+
+ unique_fd fd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_GE(fd, 0);
+ ASSERT_EQ(get_block_device_size(fd.get()), kTestSize);
+}
+
} // namespace
bool Mkdir(const std::string& path) {
@@ -194,13 +311,27 @@
if (argc >= 2) {
gDataPath = argv[1];
} else {
- gDataPath = "/data/gsi/test";
+ gDataPath = "/data/local/tmp";
}
- gDataMountPath = gDataPath + "/mnt"s;
- if (!Mkdir(gDataPath) || !Mkdir(kMetadataPath) || !Mkdir(gDataMountPath) ||
- !Mkdir(kMetadataPath + "/mnt"s)) {
+ if (!Mkdir(gDataPath) || !Mkdir(kMetadataPath) || !Mkdir(kMetadataPath + "/mnt"s)) {
return 1;
}
- return RUN_ALL_TESTS();
+
+ std::string tempdir = gDataPath + "/XXXXXX";
+ if (!mkdtemp(tempdir.data())) {
+ std::cerr << "unable to create tempdir on " << tempdir << "\n";
+ exit(EXIT_FAILURE);
+ }
+ if (!android::base::Realpath(tempdir, &gTestDir)) {
+ std::cerr << "unable to find realpath for " << tempdir;
+ exit(EXIT_FAILURE);
+ }
+
+ auto rv = RUN_ALL_TESTS();
+
+ std::string cmd = "rm -rf " + gTestDir;
+ system(cmd.c_str());
+
+ return rv;
}
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 6db8f13..d5e85e6 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -44,7 +44,6 @@
"libext2_uuid",
"libext4_utils",
"libfstab",
- "libsnapshot_cow",
"libsnapshot_snapuserd",
"libz",
],
@@ -75,6 +74,8 @@
shared_libs: [
"android.hardware.boot@1.0",
"android.hardware.boot@1.1",
+ "android.hardware.boot-V1-ndk",
+ "libboot_control_client",
],
}
@@ -154,22 +155,6 @@
"-Wall",
"-Werror",
],
- export_include_dirs: ["include"],
- srcs: [
- "cow_decompress.cpp",
- "cow_reader.cpp",
- "cow_writer.cpp",
- "cow_format.cpp",
- ],
-}
-
-cc_library_static {
- name: "libsnapshot_cow",
- defaults: [
- "libsnapshot_cow_defaults",
- ],
- host_supported: true,
- recovery_available: true,
shared_libs: [
"libbase",
"liblog",
@@ -177,7 +162,24 @@
static_libs: [
"libbrotli",
"libz",
+ "liblz4",
],
+ export_include_dirs: ["include"],
+}
+
+cc_library_static {
+ name: "libsnapshot_cow",
+ defaults: [
+ "libsnapshot_cow_defaults",
+ ],
+ srcs: [
+ "cow_decompress.cpp",
+ "cow_reader.cpp",
+ "cow_writer.cpp",
+ "cow_format.cpp",
+ ],
+ host_supported: true,
+ recovery_available: true,
ramdisk_available: true,
vendor_ramdisk_available: true,
}
@@ -214,7 +216,7 @@
cc_defaults {
name: "libsnapshot_test_defaults",
- defaults: ["libsnapshot_defaults"],
+ defaults: ["libsnapshot_defaults", "libsnapshot_cow_defaults"],
srcs: [
"partition_cow_creator_test.cpp",
"snapshot_metadata_updater_test.cpp",
@@ -233,6 +235,7 @@
static_libs: [
"android.hardware.boot@1.0",
"android.hardware.boot@1.1",
+ "android.hardware.boot-V1-ndk",
"libbrotli",
"libc++fs",
"libfs_mgr_binder",
@@ -261,7 +264,7 @@
cc_test {
name: "vts_libsnapshot_test",
- defaults: ["libsnapshot_test_defaults"],
+ defaults: ["libsnapshot_test_defaults", "libsnapshot_hal_deps"],
}
sh_test {
@@ -295,6 +298,7 @@
cc_binary {
name: "snapshotctl",
+ defaults: ["libsnapshot_cow_defaults", "libsnapshot_hal_deps"],
srcs: [
"snapshotctl.cpp",
],
@@ -308,8 +312,6 @@
"update_metadata-protos",
],
shared_libs: [
- "android.hardware.boot@1.0",
- "android.hardware.boot@1.1",
"libbase",
"libext2_uuid",
"libext4_utils",
@@ -343,6 +345,9 @@
cc_defaults {
name: "libsnapshot_fuzzer_defaults",
+ defaults: [
+ "libsnapshot_cow_defaults",
+ ],
native_coverage : true,
srcs: [
// Compile the protobuf definition again with type full.
@@ -416,6 +421,7 @@
name: "cow_api_test",
defaults: [
"fs_mgr_defaults",
+ "libsnapshot_cow_defaults",
],
srcs: [
"cow_api_test.cpp",
@@ -471,6 +477,7 @@
"libsparse",
"libxz",
"libz",
+ "liblz4",
"libziparchive",
"update_metadata-protos",
],
@@ -486,6 +493,9 @@
cc_binary {
name: "estimate_cow_from_nonab_ota",
+ defaults: [
+ "libsnapshot_cow_defaults",
+ ],
host_supported: true,
device_supported: false,
cflags: [
@@ -519,6 +529,7 @@
name: "inspect_cow",
host_supported: true,
device_supported: true,
+ defaults: ["libsnapshot_cow_defaults"],
cflags: [
"-D_FILE_OFFSET_BITS=64",
"-Wall",
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 6ee8d4a..b3763ae 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -97,8 +97,8 @@
// This is non-zero when |state| == MERGING or MERGE_COMPLETED.
uint64 metadata_sectors = 8;
- // True if compression is enabled, false otherwise.
- bool compression_enabled = 9;
+ // True if using snapuserd, false otherwise.
+ bool using_snapuserd = 9;
// The old partition size (if none existed, this will be zero).
uint64 old_partition_size = 10;
@@ -184,7 +184,7 @@
uint64 metadata_sectors = 4;
// Whether compression/dm-user was used for any snapshots.
- bool compression_enabled = 5;
+ bool using_snapuserd = 5;
// Merge phase (if state == MERGING).
MergePhase merge_phase = 6;
diff --git a/fs_mgr/libsnapshot/cow_decompress.cpp b/fs_mgr/libsnapshot/cow_decompress.cpp
index faceafe..a4d2277 100644
--- a/fs_mgr/libsnapshot/cow_decompress.cpp
+++ b/fs_mgr/libsnapshot/cow_decompress.cpp
@@ -20,6 +20,7 @@
#include <android-base/logging.h>
#include <brotli/decode.h>
+#include <lz4.h>
#include <zlib.h>
namespace android {
@@ -260,5 +261,43 @@
return std::unique_ptr<IDecompressor>(new BrotliDecompressor());
}
+class Lz4Decompressor final : public IDecompressor {
+ public:
+ ~Lz4Decompressor() override = default;
+
+ bool Decompress(const size_t output_size) override {
+ size_t actual_buffer_size = 0;
+ auto&& output_buffer = sink_->GetBuffer(output_size, &actual_buffer_size);
+ if (actual_buffer_size != output_size) {
+ LOG(ERROR) << "Failed to allocate buffer of size " << output_size << " only got "
+ << actual_buffer_size << " bytes";
+ return false;
+ }
+ std::string input_buffer;
+ input_buffer.resize(stream_->Size());
+ size_t bytes_read = 0;
+ stream_->Read(input_buffer.data(), input_buffer.size(), &bytes_read);
+ if (bytes_read != input_buffer.size()) {
+ LOG(ERROR) << "Failed to read all input at once. Expected: " << input_buffer.size()
+ << " actual: " << bytes_read;
+ return false;
+ }
+ const int bytes_decompressed =
+ LZ4_decompress_safe(input_buffer.data(), static_cast<char*>(output_buffer),
+ input_buffer.size(), output_size);
+ if (bytes_decompressed != output_size) {
+ LOG(ERROR) << "Failed to decompress LZ4 block, expected output size: " << output_size
+ << ", actual: " << bytes_decompressed;
+ return false;
+ }
+ sink_->ReturnData(output_buffer, output_size);
+ return true;
+ }
+};
+
+std::unique_ptr<IDecompressor> IDecompressor::Lz4() {
+ return std::make_unique<Lz4Decompressor>();
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_decompress.h b/fs_mgr/libsnapshot/cow_decompress.h
index f485256..7f74eda 100644
--- a/fs_mgr/libsnapshot/cow_decompress.h
+++ b/fs_mgr/libsnapshot/cow_decompress.h
@@ -41,6 +41,7 @@
static std::unique_ptr<IDecompressor> Uncompressed();
static std::unique_ptr<IDecompressor> Gz();
static std::unique_ptr<IDecompressor> Brotli();
+ static std::unique_ptr<IDecompressor> Lz4();
// |output_bytes| is the expected total number of bytes to sink.
virtual bool Decompress(size_t output_bytes) = 0;
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 746feeb..653492c 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -775,6 +775,9 @@
case kCowCompressBrotli:
decompressor = IDecompressor::Brotli();
break;
+ case kCowCompressLz4:
+ decompressor = IDecompressor::Lz4();
+ break;
default:
LOG(ERROR) << "Unknown compression type: " << op.compression;
return false;
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 5ce1d3b..7281fc2 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -24,8 +24,10 @@
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <brotli/encode.h>
+#include <libsnapshot/cow_format.h>
#include <libsnapshot/cow_reader.h>
#include <libsnapshot/cow_writer.h>
+#include <lz4.h>
#include <zlib.h>
namespace android {
@@ -129,6 +131,8 @@
compression_ = kCowCompressGz;
} else if (options_.compression == "brotli") {
compression_ = kCowCompressBrotli;
+ } else if (options_.compression == "lz4") {
+ compression_ = kCowCompressLz4;
} else if (options_.compression == "none") {
compression_ = kCowCompressNone;
} else if (!options_.compression.empty()) {
@@ -403,35 +407,56 @@
std::basic_string<uint8_t> CowWriter::Compress(const void* data, size_t length) {
switch (compression_) {
case kCowCompressGz: {
- auto bound = compressBound(length);
- auto buffer = std::make_unique<uint8_t[]>(bound);
+ const auto bound = compressBound(length);
+ std::basic_string<uint8_t> buffer(bound, '\0');
uLongf dest_len = bound;
- auto rv = compress2(buffer.get(), &dest_len, reinterpret_cast<const Bytef*>(data),
+ auto rv = compress2(buffer.data(), &dest_len, reinterpret_cast<const Bytef*>(data),
length, Z_BEST_COMPRESSION);
if (rv != Z_OK) {
LOG(ERROR) << "compress2 returned: " << rv;
return {};
}
- return std::basic_string<uint8_t>(buffer.get(), dest_len);
+ buffer.resize(dest_len);
+ return buffer;
}
case kCowCompressBrotli: {
- auto bound = BrotliEncoderMaxCompressedSize(length);
+ const auto bound = BrotliEncoderMaxCompressedSize(length);
if (!bound) {
LOG(ERROR) << "BrotliEncoderMaxCompressedSize returned 0";
return {};
}
- auto buffer = std::make_unique<uint8_t[]>(bound);
+ std::basic_string<uint8_t> buffer(bound, '\0');
size_t encoded_size = bound;
auto rv = BrotliEncoderCompress(
BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, length,
- reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.get());
+ reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.data());
if (!rv) {
LOG(ERROR) << "BrotliEncoderCompress failed";
return {};
}
- return std::basic_string<uint8_t>(buffer.get(), encoded_size);
+ buffer.resize(encoded_size);
+ return buffer;
+ }
+ case kCowCompressLz4: {
+ const auto bound = LZ4_compressBound(length);
+ if (!bound) {
+ LOG(ERROR) << "LZ4_compressBound returned 0";
+ return {};
+ }
+ std::basic_string<uint8_t> buffer(bound, '\0');
+
+ const auto compressed_size = LZ4_compress_default(
+ static_cast<const char*>(data), reinterpret_cast<char*>(buffer.data()), length,
+ buffer.size());
+ if (compressed_size <= 0) {
+ LOG(ERROR) << "LZ4_compress_default failed, input size: " << length
+ << ", compression bound: " << bound << ", ret: " << compressed_size;
+ return {};
+ }
+ buffer.resize(compressed_size);
+ return buffer;
}
default:
LOG(ERROR) << "unhandled compression type: " << compression_;
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
index a6d96ed..0ab6103 100644
--- a/fs_mgr/libsnapshot/device_info.cpp
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -23,8 +23,9 @@
namespace snapshot {
#ifdef LIBSNAPSHOT_USE_HAL
-using android::hardware::boot::V1_0::BoolResult;
-using android::hardware::boot::V1_0::CommandResult;
+using android::hal::BootControlClient;
+using android::hal::BootControlVersion;
+using android::hal::CommandResult;
#endif
using namespace std::chrono_literals;
@@ -63,16 +64,16 @@
#ifdef LIBSNAPSHOT_USE_HAL
bool DeviceInfo::EnsureBootHal() {
if (!boot_control_) {
- auto hal = android::hardware::boot::V1_0::IBootControl::getService();
+ auto hal = BootControlClient::WaitForService();
if (!hal) {
LOG(ERROR) << "Could not find IBootControl HAL";
return false;
}
- boot_control_ = android::hardware::boot::V1_1::IBootControl::castFrom(hal);
- if (!boot_control_) {
+ if (hal->GetVersion() < BootControlVersion::BOOTCTL_V1_1) {
LOG(ERROR) << "Could not find IBootControl 1.1 HAL";
return false;
}
+ boot_control_ = std::move(hal);
}
return true;
}
@@ -83,8 +84,9 @@
if (!EnsureBootHal()) {
return false;
}
- if (!boot_control_->setSnapshotMergeStatus(status)) {
- LOG(ERROR) << "Unable to set the snapshot merge status";
+ const auto ret = boot_control_->SetSnapshotMergeStatus(status);
+ if (!ret.IsOk()) {
+ LOG(ERROR) << "Unable to set the snapshot merge status " << ret.errMsg;
return false;
}
return true;
@@ -108,9 +110,7 @@
return false;
}
- CommandResult result = {};
- auto cb = [&](CommandResult r) -> void { result = r; };
- boot_control_->setSlotAsUnbootable(slot, cb);
+ CommandResult result = boot_control_->MarkSlotUnbootable(slot);
if (!result.success) {
LOG(ERROR) << "Error setting slot " << slot << " unbootable: " << result.errMsg;
return false;
diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h
index 8aefb85..d06f1be 100644
--- a/fs_mgr/libsnapshot/device_info.h
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -17,7 +17,7 @@
#include <string>
#ifdef LIBSNAPSHOT_USE_HAL
-#include <android/hardware/boot/1.1/IBootControl.h>
+#include <BootControlClient.h>
#endif
#include <liblp/partition_opener.h>
#include <libsnapshot/snapshot.h>
@@ -26,7 +26,7 @@
namespace snapshot {
class DeviceInfo final : public SnapshotManager::IDeviceInfo {
- using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
+ using MergeStatus = ::aidl::android::hardware::boot::MergeStatus;
public:
std::string GetMetadataDir() const override;
@@ -50,7 +50,7 @@
android::fs_mgr::PartitionOpener opener_;
bool first_stage_init_ = false;
#ifdef LIBSNAPSHOT_USE_HAL
- android::sp<android::hardware::boot::V1_1::IBootControl> boot_control_;
+ std::unique_ptr<::android::hal::BootControlClient> boot_control_;
#endif
};
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 9f4ddbb..ba75a8d 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -154,9 +154,12 @@
static constexpr uint8_t kCowSequenceOp = 7;
static constexpr uint8_t kCowFooterOp = -1;
-static constexpr uint8_t kCowCompressNone = 0;
-static constexpr uint8_t kCowCompressGz = 1;
-static constexpr uint8_t kCowCompressBrotli = 2;
+enum CowCompressionAlgorithm : uint8_t {
+ kCowCompressNone = 0,
+ kCowCompressGz = 1,
+ kCowCompressBrotli = 2,
+ kCowCompressLz4 = 3
+};
static constexpr uint8_t kCowReadAheadNotStarted = 0;
static constexpr uint8_t kCowReadAheadInProgress = 1;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index e17b5c6..e7a2f02 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -155,7 +155,7 @@
android::base::borrowed_fd fd_;
CowHeader header_{};
CowFooter footer_{};
- int compression_ = 0;
+ CowCompressionAlgorithm compression_ = kCowCompressNone;
uint64_t next_op_pos_ = 0;
uint64_t next_data_pos_ = 0;
uint32_t cluster_size_ = 0;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 5fe5280..34c7baf 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -46,6 +46,10 @@
#define DEFINED_FRIEND_TEST
#endif
+namespace aidl::android::hardware::boot {
+enum class MergeStatus;
+}
+
namespace android {
namespace fiemap {
@@ -59,13 +63,6 @@
// Forward declare IBootControl types since we cannot include only the headers
// with Soong. Note: keep the enum width in sync.
-namespace hardware {
-namespace boot {
-namespace V1_1 {
-enum class MergeStatus : int32_t;
-} // namespace V1_1
-} // namespace boot
-} // namespace hardware
namespace snapshot {
@@ -95,6 +92,7 @@
class IDeviceInfo {
public:
using IImageManager = android::fiemap::IImageManager;
+ using MergeStatus = aidl::android::hardware::boot::MergeStatus;
virtual ~IDeviceInfo() {}
virtual std::string GetMetadataDir() const = 0;
@@ -103,8 +101,7 @@
virtual std::string GetSuperDevice(uint32_t slot) const = 0;
virtual const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const = 0;
virtual bool IsOverlayfsSetup() const = 0;
- virtual bool SetBootControlMergeStatus(
- android::hardware::boot::V1_1::MergeStatus status) = 0;
+ virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
virtual bool IsRecovery() const = 0;
virtual bool IsTestDevice() const { return false; }
@@ -311,7 +308,7 @@
using LpMetadata = android::fs_mgr::LpMetadata;
using MetadataBuilder = android::fs_mgr::MetadataBuilder;
using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
- using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
+ using MergeStatus = aidl::android::hardware::boot::MergeStatus;
using FiemapStatus = android::fiemap::FiemapStatus;
friend class SnapshotMergeStats;
@@ -644,6 +641,7 @@
MergeFailureCode CheckMergeConsistency(LockedFile* lock, const std::string& name,
const SnapshotStatus& update_status);
+ auto UpdateStateToStr(enum UpdateState state);
// Get status or table information about a device-mapper node with a single target.
enum class TableQuery {
Table,
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index c3b40dc..f850b94 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -17,10 +17,10 @@
#include <memory>
#include <optional>
#include <string>
+#include <unordered_map>
#include <unordered_set>
#include <android-base/file.h>
-#include <android/hardware/boot/1.1/IBootControl.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <libfiemap/image_manager.h>
@@ -33,10 +33,10 @@
namespace android {
namespace snapshot {
+using aidl::android::hardware::boot::MergeStatus;
using android::fs_mgr::IPropertyFetcher;
using android::fs_mgr::MetadataBuilder;
using android::fs_mgr::testing::MockPropertyFetcher;
-using android::hardware::boot::V1_1::MergeStatus;
using chromeos_update_engine::DeltaArchiveManifest;
using chromeos_update_engine::PartitionUpdate;
using testing::_;
@@ -166,27 +166,25 @@
android::dm::IDeviceMapper& impl_;
};
-class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher {
+class SnapshotTestPropertyFetcher : public android::fs_mgr::IPropertyFetcher {
public:
- SnapshotTestPropertyFetcher(const std::string& slot_suffix) {
- using testing::Return;
- ON_CALL(*this, GetProperty("ro.boot.slot_suffix", _)).WillByDefault(Return(slot_suffix));
- ON_CALL(*this, GetBoolProperty("ro.boot.dynamic_partitions", _))
- .WillByDefault(Return(true));
- ON_CALL(*this, GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
- .WillByDefault(Return(false));
- ON_CALL(*this, GetBoolProperty("ro.virtual_ab.enabled", _)).WillByDefault(Return(true));
- }
+ explicit SnapshotTestPropertyFetcher(const std::string& slot_suffix,
+ std::unordered_map<std::string, std::string>&& props = {});
+
+ std::string GetProperty(const std::string& key, const std::string& defaultValue) override;
+ bool GetBoolProperty(const std::string& key, bool defaultValue) override;
static void SetUp(const std::string& slot_suffix = "_a") { Reset(slot_suffix); }
-
static void TearDown() { Reset("_a"); }
private:
static void Reset(const std::string& slot_suffix) {
IPropertyFetcher::OverrideForTesting(
- std::make_unique<NiceMock<SnapshotTestPropertyFetcher>>(slot_suffix));
+ std::make_unique<SnapshotTestPropertyFetcher>(slot_suffix));
}
+
+ private:
+ std::unordered_map<std::string, std::string> properties_;
};
// Helper for error-spam-free cleanup.
@@ -197,9 +195,10 @@
// Expect space of |path| is multiple of 4K.
bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size = std::nullopt,
std::string* hash = nullptr);
-bool WriteRandomData(ICowWriter* writer, std::string* hash = nullptr);
std::string HashSnapshot(ISnapshotWriter* writer);
+std::string ToHexString(const uint8_t* buf, size_t len);
+
std::optional<std::string> GetHash(const std::string& path);
// Add partitions and groups described by |manifest|.
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index 5569da0..7057223 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -143,7 +143,7 @@
}
std::optional<uint64_t> PartitionCowCreator::GetCowSize() {
- if (compression_enabled) {
+ if (using_snapuserd) {
if (update == nullptr || !update->has_estimate_cow_size()) {
LOG(ERROR) << "Update manifest does not include a COW size";
return std::nullopt;
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.h b/fs_mgr/libsnapshot/partition_cow_creator.h
index 34b39ca..949e6c5 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.h
+++ b/fs_mgr/libsnapshot/partition_cow_creator.h
@@ -56,8 +56,8 @@
// Extra extents that are going to be invalidated during the update
// process.
std::vector<ChromeOSExtent> extra_extents = {};
- // True if compression is enabled.
- bool compression_enabled = false;
+ // True if snapuserd COWs are enabled.
+ bool using_snapuserd = false;
std::string compression_algorithm;
struct Return {
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index de35c13..cf26a16 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -249,7 +249,7 @@
.target_partition = system_b,
.current_metadata = builder_a.get(),
.current_suffix = "_a",
- .compression_enabled = true,
+ .using_snapuserd = true,
.update = &update};
auto ret = creator.Run();
@@ -275,7 +275,7 @@
.target_partition = system_b,
.current_metadata = builder_a.get(),
.current_suffix = "_a",
- .compression_enabled = true,
+ .using_snapuserd = true,
.update = nullptr};
auto ret = creator.Run();
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index c8684a2..bc3efd9 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -53,6 +53,7 @@
namespace android {
namespace snapshot {
+using aidl::android::hardware::boot::MergeStatus;
using android::base::unique_fd;
using android::dm::DeviceMapper;
using android::dm::DmDeviceState;
@@ -72,7 +73,6 @@
using android::fs_mgr::LpMetadata;
using android::fs_mgr::MetadataBuilder;
using android::fs_mgr::SlotNumberForSlotSuffix;
-using android::hardware::boot::V1_1::MergeStatus;
using chromeos_update_engine::DeltaArchiveManifest;
using chromeos_update_engine::Extent;
using chromeos_update_engine::FileDescriptor;
@@ -398,7 +398,7 @@
status->set_state(SnapshotState::CREATED);
status->set_sectors_allocated(0);
status->set_metadata_sectors(0);
- status->set_compression_enabled(cow_creator->compression_enabled);
+ status->set_using_snapuserd(cow_creator->using_snapuserd);
status->set_compression_algorithm(cow_creator->compression_algorithm);
if (!WriteSnapshotStatus(lock, *status)) {
@@ -788,7 +788,7 @@
}
}
- bool compression_enabled = false;
+ bool using_snapuserd = false;
std::vector<std::string> first_merge_group;
@@ -809,7 +809,7 @@
return false;
}
- compression_enabled |= snapshot_status.compression_enabled();
+ using_snapuserd |= snapshot_status.using_snapuserd();
if (DecideMergePhase(snapshot_status) == MergePhase::FIRST_PHASE) {
first_merge_group.emplace_back(snapshot);
}
@@ -817,7 +817,7 @@
SnapshotUpdateStatus initial_status = ReadSnapshotUpdateStatus(lock.get());
initial_status.set_state(UpdateState::Merging);
- initial_status.set_compression_enabled(compression_enabled);
+ initial_status.set_using_snapuserd(using_snapuserd);
if (!UpdateUsesUserSnapshots(lock.get())) {
initial_status.set_sectors_allocated(initial_target_values.sectors_allocated);
@@ -988,6 +988,29 @@
return true;
}
+auto SnapshotManager::UpdateStateToStr(const enum UpdateState state) {
+ switch (state) {
+ case None:
+ return "None";
+ case Initiated:
+ return "Initiated";
+ case Unverified:
+ return "Unverified";
+ case Merging:
+ return "Merging";
+ case MergeNeedsReboot:
+ return "MergeNeedsReboot";
+ case MergeCompleted:
+ return "MergeCompleted";
+ case MergeFailed:
+ return "MergeFailed";
+ case Cancelled:
+ return "Cancelled";
+ default:
+ return "Unknown";
+ }
+}
+
bool SnapshotManager::QuerySnapshotStatus(const std::string& dm_name, std::string* target_type,
DmTargetSnapshot::Status* status) {
DeviceMapper::TargetInfo target;
@@ -1016,7 +1039,7 @@
const std::function<bool()>& before_cancel) {
while (true) {
auto result = CheckMergeState(before_cancel);
- LOG(INFO) << "ProcessUpdateState handling state: " << result.state;
+ LOG(INFO) << "ProcessUpdateState handling state: " << UpdateStateToStr(result.state);
if (result.state == UpdateState::MergeFailed) {
AcknowledgeMergeFailure(result.failure_code);
@@ -1044,7 +1067,7 @@
}
auto result = CheckMergeState(lock.get(), before_cancel);
- LOG(INFO) << "CheckMergeState for snapshots returned: " << result.state;
+ LOG(INFO) << "CheckMergeState for snapshots returned: " << UpdateStateToStr(result.state);
if (result.state == UpdateState::MergeCompleted) {
// Do this inside the same lock. Failures get acknowledged without the
@@ -1109,7 +1132,8 @@
}
auto result = CheckTargetMergeState(lock, snapshot, update_status);
- LOG(INFO) << "CheckTargetMergeState for " << snapshot << " returned: " << result.state;
+ LOG(INFO) << "CheckTargetMergeState for " << snapshot
+ << " returned: " << UpdateStateToStr(result.state);
switch (result.state) {
case UpdateState::MergeFailed:
@@ -1340,7 +1364,7 @@
}
MergeFailureCode CheckMergeConsistency(const std::string& name, const SnapshotStatus& status) {
- if (!status.compression_enabled()) {
+ if (!status.using_snapuserd()) {
// Do not try to verify old-style COWs yet.
return MergeFailureCode::Ok;
}
@@ -1601,7 +1625,7 @@
// as unmap will fail since dm-user itself was a snapshot device prior
// to switching of tables. Unmap will fail as the device will be mounted
// by system partitions
- if (status.compression_enabled()) {
+ if (status.using_snapuserd()) {
auto dm_user_name = GetDmUserCowName(name, GetSnapshotDriver(lock));
UnmapDmUserDevice(dm_user_name);
}
@@ -2091,8 +2115,10 @@
}
bool SnapshotManager::UpdateUsesCompression(LockedFile* lock) {
+ // This returns true even if compression is "none", since update_engine is
+ // really just trying to see if snapuserd is in use.
SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
- return update_status.compression_enabled();
+ return update_status.using_snapuserd();
}
bool SnapshotManager::UpdateUsesIouring(LockedFile* lock) {
@@ -2151,8 +2177,17 @@
if (!suffix.empty() && !android::base::EndsWith(name, suffix)) {
continue;
}
- snapshots->emplace_back(std::move(name));
+
+ // Insert system and product partition at the beginning so that
+ // during snapshot-merge, these partitions are merged first.
+ if (name == "system_a" || name == "system_b" || name == "product_a" ||
+ name == "product_b") {
+ snapshots->insert(snapshots->begin(), std::move(name));
+ } else {
+ snapshots->emplace_back(std::move(name));
+ }
}
+
return true;
}
@@ -2241,8 +2276,8 @@
.block_device = super_device,
.metadata = metadata.get(),
.partition = &partition,
- .partition_opener = &opener,
.timeout_ms = timeout_ms,
+ .partition_opener = &opener,
};
if (!MapPartitionWithSnapshot(lock, std::move(params), SnapshotContext::Mount, nullptr)) {
return false;
@@ -2403,13 +2438,13 @@
remaining_time = GetRemainingTime(params.timeout_ms, begin);
if (remaining_time.count() < 0) return false;
- if (context == SnapshotContext::Update && live_snapshot_status->compression_enabled()) {
+ if (context == SnapshotContext::Update && live_snapshot_status->using_snapuserd()) {
// Stop here, we can't run dm-user yet, the COW isn't built.
created_devices.Release();
return true;
}
- if (live_snapshot_status->compression_enabled()) {
+ if (live_snapshot_status->using_snapuserd()) {
// Get the source device (eg the view of the partition from before it was resized).
std::string source_device_path;
if (live_snapshot_status->old_partition_size() > 0) {
@@ -2719,8 +2754,8 @@
.block_device = super_device,
.metadata = metadata.get(),
.partition_name = snapshot,
- .partition_opener = &opener,
.timeout_ms = timeout_ms,
+ .partition_opener = &opener,
};
if (!MapPartitionWithSnapshot(lock.get(), std::move(params), SnapshotContext::Mount,
nullptr)) {
@@ -2911,7 +2946,7 @@
// build fingerprint.
if (!(state == UpdateState::Initiated || state == UpdateState::None)) {
SnapshotUpdateStatus old_status = ReadSnapshotUpdateStatus(lock);
- status.set_compression_enabled(old_status.compression_enabled());
+ status.set_using_snapuserd(old_status.using_snapuserd());
status.set_source_build_fingerprint(old_status.source_build_fingerprint());
status.set_merge_phase(old_status.merge_phase());
status.set_userspace_snapshots(old_status.userspace_snapshots());
@@ -3165,18 +3200,42 @@
LOG(INFO) << " dap_metadata.cow_version(): " << dap_metadata.cow_version()
<< " writer.GetCowVersion(): " << writer.GetCowVersion();
- bool use_compression = IsCompressionEnabled() && dap_metadata.vabc_enabled() &&
- !device_->IsRecovery() && cow_format_support;
+ // Deduce supported features.
+ bool userspace_snapshots = CanUseUserspaceSnapshots();
+ bool legacy_compression = GetLegacyCompressionEnabledProperty();
+
+ std::string vabc_disable_reason;
+ if (!dap_metadata.vabc_enabled()) {
+ vabc_disable_reason = "not enabled metadata";
+ } else if (device_->IsRecovery()) {
+ vabc_disable_reason = "recovery";
+ } else if (!cow_format_support) {
+ vabc_disable_reason = "cow format not supported";
+ }
+
+ if (!vabc_disable_reason.empty()) {
+ if (userspace_snapshots) {
+ LOG(INFO) << "Userspace snapshots disabled: " << vabc_disable_reason;
+ }
+ if (legacy_compression) {
+ LOG(INFO) << "Compression disabled: " << vabc_disable_reason;
+ }
+ userspace_snapshots = false;
+ legacy_compression = false;
+ }
+
+ const bool using_snapuserd = userspace_snapshots || legacy_compression;
+ if (!using_snapuserd) {
+ LOG(INFO) << "Using legacy Virtual A/B (dm-snapshot)";
+ }
std::string compression_algorithm;
- if (use_compression) {
+ if (using_snapuserd) {
compression_algorithm = dap_metadata.vabc_compression_param();
if (compression_algorithm.empty()) {
// Older OTAs don't set an explicit compression type, so default to gz.
compression_algorithm = "gz";
}
- } else {
- compression_algorithm = "none";
}
PartitionCowCreator cow_creator{
@@ -3187,7 +3246,7 @@
.current_suffix = current_suffix,
.update = nullptr,
.extra_extents = {},
- .compression_enabled = use_compression,
+ .using_snapuserd = using_snapuserd,
.compression_algorithm = compression_algorithm,
};
@@ -3212,11 +3271,11 @@
return Return::Error();
}
- // If compression is enabled, we need to retain a copy of the old metadata
+ // If snapuserd is enabled, we need to retain a copy of the old metadata
// so we can access original blocks in case they are moved around. We do
// not want to rely on the old super metadata slot because we don't
// guarantee its validity after the slot switch is successful.
- if (cow_creator.compression_enabled) {
+ if (using_snapuserd) {
auto metadata = current_metadata->Export();
if (!metadata) {
LOG(ERROR) << "Could not export current metadata";
@@ -3232,70 +3291,36 @@
SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock.get());
status.set_state(update_state);
- status.set_compression_enabled(cow_creator.compression_enabled);
- if (cow_creator.compression_enabled) {
- if (!device()->IsTestDevice()) {
- bool userSnapshotsEnabled = IsUserspaceSnapshotsEnabled();
- const std::string UNKNOWN = "unknown";
- const std::string vendor_release = android::base::GetProperty(
- "ro.vendor.build.version.release_or_codename", UNKNOWN);
+ status.set_using_snapuserd(using_snapuserd);
- // No user-space snapshots if vendor partition is on Android 12
- if (vendor_release.find("12") != std::string::npos) {
- LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
- << vendor_release;
- userSnapshotsEnabled = false;
- }
+ if (userspace_snapshots) {
+ status.set_userspace_snapshots(true);
+ LOG(INFO) << "Virtual A/B using userspace snapshots";
- // Userspace snapshots is enabled only if compression is enabled
- status.set_userspace_snapshots(userSnapshotsEnabled);
- if (userSnapshotsEnabled) {
- is_snapshot_userspace_ = true;
- status.set_io_uring_enabled(IsIouringEnabled());
- LOG(INFO) << "Userspace snapshots enabled";
- } else {
- is_snapshot_userspace_ = false;
- LOG(INFO) << "Userspace snapshots disabled";
- }
+ if (GetIouringEnabledProperty()) {
+ status.set_io_uring_enabled(true);
+ LOG(INFO) << "io_uring for snapshots enabled";
+ }
+ } else if (legacy_compression) {
+ LOG(INFO) << "Virtual A/B using legacy snapuserd";
+ } else {
+ LOG(INFO) << "Virtual A/B using dm-snapshot";
+ }
- // Terminate stale daemon if any
- std::unique_ptr<SnapuserdClient> snapuserd_client =
- SnapuserdClient::Connect(kSnapuserdSocket, 5s);
- if (snapuserd_client) {
- snapuserd_client->DetachSnapuserd();
- snapuserd_client->CloseConnection();
- snapuserd_client = nullptr;
- }
+ is_snapshot_userspace_.emplace(userspace_snapshots);
- // Clear the cached client if any
- if (snapuserd_client_) {
- snapuserd_client_->CloseConnection();
- snapuserd_client_ = nullptr;
- }
- } else {
- bool userSnapshotsEnabled = true;
- const std::string UNKNOWN = "unknown";
- const std::string vendor_release = android::base::GetProperty(
- "ro.vendor.build.version.release_or_codename", UNKNOWN);
-
- // No user-space snapshots if vendor partition is on Android 12
- if (vendor_release.find("12") != std::string::npos) {
- LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
- << vendor_release;
- userSnapshotsEnabled = false;
- }
-
- userSnapshotsEnabled = (userSnapshotsEnabled && !IsDmSnapshotTestingEnabled());
- status.set_userspace_snapshots(userSnapshotsEnabled);
- if (!userSnapshotsEnabled) {
- is_snapshot_userspace_ = false;
- LOG(INFO) << "User-space snapshots disabled for testing";
- } else {
- is_snapshot_userspace_ = true;
- LOG(INFO) << "User-space snapshots enabled for testing";
- }
+ if (!device()->IsTestDevice() && using_snapuserd) {
+ // Terminate stale daemon if any
+ std::unique_ptr<SnapuserdClient> snapuserd_client = std::move(snapuserd_client_);
+ if (!snapuserd_client) {
+ snapuserd_client = SnapuserdClient::Connect(kSnapuserdSocket, 5s);
+ }
+ if (snapuserd_client) {
+ snapuserd_client->DetachSnapuserd();
+ snapuserd_client->CloseConnection();
}
}
+
if (!WriteSnapshotUpdateStatus(lock.get(), status)) {
LOG(ERROR) << "Unable to write new update state";
return Return::Error();
@@ -3488,7 +3513,7 @@
return Return::Error();
}
- if (it->second.compression_enabled()) {
+ if (it->second.using_snapuserd()) {
unique_fd fd(open(cow_path.c_str(), O_RDWR | O_CLOEXEC));
if (fd < 0) {
PLOG(ERROR) << "open " << cow_path << " failed for snapshot "
@@ -3534,8 +3559,8 @@
if (!ReadSnapshotStatus(lock.get(), params.GetPartitionName(), &status)) {
return false;
}
- if (status.compression_enabled()) {
- LOG(ERROR) << "Cannot use MapUpdateSnapshot with compressed snapshots";
+ if (status.using_snapuserd()) {
+ LOG(ERROR) << "Cannot use MapUpdateSnapshot with snapuserd";
return false;
}
@@ -3592,7 +3617,7 @@
return nullptr;
}
- if (status.compression_enabled()) {
+ if (status.using_snapuserd()) {
return OpenCompressedSnapshotWriter(lock.get(), source_device, params.GetPartitionName(),
status, paths);
}
@@ -3722,7 +3747,10 @@
auto update_status = ReadSnapshotUpdateStatus(file.get());
ss << "Update state: " << ReadUpdateState(file.get()) << std::endl;
- ss << "Compression: " << update_status.compression_enabled() << std::endl;
+ ss << "Using snapuserd: " << update_status.using_snapuserd() << std::endl;
+ ss << "Using userspace snapshots: " << update_status.userspace_snapshots() << std::endl;
+ ss << "Using io_uring: " << update_status.io_uring_enabled() << std::endl;
+ ss << "Using XOR compression: " << GetXorCompressionEnabledProperty() << std::endl;
ss << "Current slot: " << device_->GetSlotSuffix() << std::endl;
ss << "Boot indicator: booting from " << GetCurrentSlot() << " slot" << std::endl;
ss << "Rollback indicator: "
@@ -3943,7 +3971,7 @@
if (!ReadSnapshotStatus(lock, snapshot, &status)) {
return false;
}
- if (status.compression_enabled()) {
+ if (status.using_snapuserd()) {
continue;
}
@@ -4107,7 +4135,7 @@
if (!lock) return false;
auto status = ReadSnapshotUpdateStatus(lock.get());
- return status.state() != UpdateState::None && status.compression_enabled();
+ return status.state() != UpdateState::None && status.using_snapuserd();
}
bool SnapshotManager::DetachSnapuserdForSelinux(std::vector<std::string>* snapuserd_argv) {
@@ -4133,7 +4161,7 @@
}
MergePhase SnapshotManager::DecideMergePhase(const SnapshotStatus& status) {
- if (status.compression_enabled() && status.device_size() < status.old_partition_size()) {
+ if (status.using_snapuserd() && status.device_size() < status.old_partition_size()) {
return MergePhase::FIRST_PHASE;
}
return MergePhase::SECOND_PHASE;
@@ -4175,8 +4203,7 @@
SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
stats->report()->set_iouring_used(update_status.io_uring_enabled());
stats->report()->set_userspace_snapshots_used(update_status.userspace_snapshots());
- stats->report()->set_xor_compression_used(
- android::base::GetBoolProperty("ro.virtual_ab.compression.xor.enabled", false));
+ stats->report()->set_xor_compression_used(GetXorCompressionEnabledProperty());
}
bool SnapshotManager::DeleteDeviceIfExists(const std::string& name,
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index a648384..eb8246a 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -29,6 +29,7 @@
// by SnapshotManager.
#include "android/snapshot/snapshot_fuzz.pb.h"
+#include "libsnapshot/snapshot.h"
namespace android::snapshot {
@@ -94,6 +95,7 @@
class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo {
public:
+ using MergeStatus = ISnapshotManager::IDeviceInfo::MergeStatus;
// Client is responsible for maintaining the lifetime of |data|.
SnapshotFuzzDeviceInfo(SnapshotFuzzEnv* env, const FuzzDeviceInfoData& data,
std::unique_ptr<TestPartitionOpener>&& partition_opener,
@@ -118,7 +120,7 @@
std::string GetSlotSuffix() const override { return CurrentSlotIsA() ? "_a" : "_b"; }
std::string GetOtherSlotSuffix() const override { return CurrentSlotIsA() ? "_b" : "_a"; }
bool IsOverlayfsSetup() const override { return data_->is_overlayfs_setup(); }
- bool SetBootControlMergeStatus(android::hardware::boot::V1_1::MergeStatus) override {
+ bool SetBootControlMergeStatus(MergeStatus) override {
return data_->allow_set_boot_control_merge_status();
}
bool SetSlotAsUnbootable(unsigned int) override {
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 6a348b4..91f31b0 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -26,6 +26,7 @@
#include <future>
#include <iostream>
+#include <aidl/android/hardware/boot/MergeStatus.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
@@ -39,6 +40,7 @@
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
#include <liblp/builder.h>
+#include <openssl/sha.h>
#include <storage_literals/storage_literals.h>
#include <android/snapshot/snapshot.pb.h>
@@ -51,9 +53,11 @@
#include <libsnapshot/mock_device_info.h>
#include <libsnapshot/mock_snapshot.h>
-DEFINE_string(force_config, "", "Force testing mode (dmsnap, vab, vabc) ignoring device config.");
+DEFINE_string(force_mode, "",
+ "Force testing older modes (vab-legacy, vabc-legacy) ignoring device config.");
DEFINE_string(force_iouring_disable, "",
"Force testing mode (iouring_disabled) - disable io_uring");
+DEFINE_string(compression_method, "gz", "Default compression algorithm.");
namespace android {
namespace snapshot {
@@ -90,8 +94,6 @@
std::string fake_super;
void MountMetadata();
-bool ShouldUseCompression();
-bool IsDaemonRequired();
class SnapshotTest : public ::testing::Test {
public:
@@ -107,7 +109,7 @@
void SetUp() override {
SKIP_IF_NON_VIRTUAL_AB();
- SnapshotTestPropertyFetcher::SetUp();
+ SetupProperties();
InitializeState();
CleanupTestArtifacts();
FormatFakeSuper();
@@ -115,6 +117,38 @@
ASSERT_TRUE(sm->BeginUpdate());
}
+ void SetupProperties() {
+ std::unordered_map<std::string, std::string> properties;
+
+ ASSERT_TRUE(android::base::SetProperty("snapuserd.test.dm.snapshots", "0"))
+ << "Failed to disable property: virtual_ab.userspace.snapshots.enabled";
+ ASSERT_TRUE(android::base::SetProperty("snapuserd.test.io_uring.force_disable", "0"))
+ << "Failed to set property: snapuserd.test.io_uring.disabled";
+
+ if (FLAGS_force_mode == "vabc-legacy") {
+ ASSERT_TRUE(android::base::SetProperty("snapuserd.test.dm.snapshots", "1"))
+ << "Failed to disable property: virtual_ab.userspace.snapshots.enabled";
+ properties["ro.virtual_ab.compression.enabled"] = "true";
+ properties["ro.virtual_ab.userspace.snapshots.enabled"] = "false";
+ } else if (FLAGS_force_mode == "vab-legacy") {
+ properties["ro.virtual_ab.compression.enabled"] = "false";
+ properties["ro.virtual_ab.userspace.snapshots.enabled"] = "false";
+ }
+
+ if (FLAGS_force_iouring_disable == "iouring_disabled") {
+ ASSERT_TRUE(android::base::SetProperty("snapuserd.test.io_uring.force_disable", "1"))
+ << "Failed to set property: snapuserd.test.io_uring.disabled";
+ properties["ro.virtual_ab.io_uring.enabled"] = "false";
+ }
+
+ auto fetcher = std::make_unique<SnapshotTestPropertyFetcher>("_a", std::move(properties));
+ IPropertyFetcher::OverrideForTesting(std::move(fetcher));
+
+ if (GetLegacyCompressionEnabledProperty() || CanUseUserspaceSnapshots()) {
+ snapuserd_required_ = true;
+ }
+ }
+
void TearDown() override {
RETURN_IF_NON_VIRTUAL_AB();
@@ -357,8 +391,11 @@
DeltaArchiveManifest manifest;
auto dynamic_partition_metadata = manifest.mutable_dynamic_partition_metadata();
- dynamic_partition_metadata->set_vabc_enabled(IsCompressionEnabled());
+ dynamic_partition_metadata->set_vabc_enabled(snapuserd_required_);
dynamic_partition_metadata->set_cow_version(android::snapshot::kCowVersionMajor);
+ if (snapuserd_required_) {
+ dynamic_partition_metadata->set_vabc_compression_param(FLAGS_compression_method);
+ }
auto group = dynamic_partition_metadata->add_groups();
group->set_name("group");
@@ -396,7 +433,7 @@
if (!res) {
return res;
}
- } else if (!IsCompressionEnabled()) {
+ } else if (!snapuserd_required_) {
std::string ignore;
if (!MapUpdateSnapshot("test_partition_b", &ignore)) {
return AssertionFailure() << "Failed to map test_partition_b";
@@ -449,15 +486,16 @@
std::unique_ptr<SnapshotManager::LockedFile> lock_;
android::fiemap::IImageManager* image_manager_ = nullptr;
std::string fake_super_;
+ bool snapuserd_required_ = false;
};
TEST_F(SnapshotTest, CreateSnapshot) {
ASSERT_TRUE(AcquireLock());
PartitionCowCreator cow_creator;
- cow_creator.compression_enabled = ShouldUseCompression();
- if (cow_creator.compression_enabled) {
- cow_creator.compression_algorithm = "gz";
+ cow_creator.using_snapuserd = snapuserd_required_;
+ if (cow_creator.using_snapuserd) {
+ cow_creator.compression_algorithm = FLAGS_compression_method;
} else {
cow_creator.compression_algorithm = "none";
}
@@ -483,7 +521,7 @@
ASSERT_EQ(status.state(), SnapshotState::CREATED);
ASSERT_EQ(status.device_size(), kDeviceSize);
ASSERT_EQ(status.snapshot_size(), kDeviceSize);
- ASSERT_EQ(status.compression_enabled(), cow_creator.compression_enabled);
+ ASSERT_EQ(status.using_snapuserd(), cow_creator.using_snapuserd);
ASSERT_EQ(status.compression_algorithm(), cow_creator.compression_algorithm);
}
@@ -496,7 +534,7 @@
ASSERT_TRUE(AcquireLock());
PartitionCowCreator cow_creator;
- cow_creator.compression_enabled = ShouldUseCompression();
+ cow_creator.using_snapuserd = snapuserd_required_;
static const uint64_t kDeviceSize = 1024 * 1024;
SnapshotStatus status;
@@ -623,10 +661,10 @@
SnapshotStatus status;
ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
ASSERT_EQ(status.state(), SnapshotState::CREATED);
- if (ShouldUseCompression()) {
- ASSERT_EQ(status.compression_algorithm(), "gz");
+ if (snapuserd_required_) {
+ ASSERT_EQ(status.compression_algorithm(), FLAGS_compression_method);
} else {
- ASSERT_EQ(status.compression_algorithm(), "none");
+ ASSERT_EQ(status.compression_algorithm(), "");
}
DeviceMapper::TargetInfo target;
@@ -897,8 +935,11 @@
opener_ = std::make_unique<TestPartitionOpener>(fake_super);
auto dynamic_partition_metadata = manifest_.mutable_dynamic_partition_metadata();
- dynamic_partition_metadata->set_vabc_enabled(ShouldUseCompression());
+ dynamic_partition_metadata->set_vabc_enabled(snapuserd_required_);
dynamic_partition_metadata->set_cow_version(android::snapshot::kCowVersionMajor);
+ if (snapuserd_required_) {
+ dynamic_partition_metadata->set_vabc_compression_param(FLAGS_compression_method);
+ }
// Create a fake update package metadata.
// Not using full name "system", "vendor", "product" because these names collide with the
@@ -1030,7 +1071,7 @@
}
AssertionResult MapOneUpdateSnapshot(const std::string& name) {
- if (ShouldUseCompression()) {
+ if (snapuserd_required_) {
std::unique_ptr<ISnapshotWriter> writer;
return MapUpdateSnapshot(name, &writer);
} else {
@@ -1039,14 +1080,25 @@
}
}
- AssertionResult WriteSnapshotAndHash(const std::string& name) {
- if (ShouldUseCompression()) {
+ AssertionResult WriteSnapshots() {
+ for (const auto& partition : {sys_, vnd_, prd_}) {
+ auto res = WriteSnapshotAndHash(partition);
+ if (!res) {
+ return res;
+ }
+ }
+ return AssertionSuccess();
+ }
+
+ AssertionResult WriteSnapshotAndHash(PartitionUpdate* partition) {
+ std::string name = partition->partition_name() + "_b";
+ if (snapuserd_required_) {
std::unique_ptr<ISnapshotWriter> writer;
auto res = MapUpdateSnapshot(name, &writer);
if (!res) {
return res;
}
- if (!WriteRandomData(writer.get(), &hashes_[name])) {
+ if (!WriteRandomSnapshotData(writer.get(), &hashes_[name])) {
return AssertionFailure() << "Unable to write random data to snapshot " << name;
}
if (!writer->Finalize()) {
@@ -1070,6 +1122,42 @@
<< ", hash: " << hashes_[name];
}
+ bool WriteRandomSnapshotData(ICowWriter* writer, std::string* hash) {
+ unique_fd rand(open("/dev/urandom", O_RDONLY));
+ if (rand < 0) {
+ PLOG(ERROR) << "open /dev/urandom";
+ return false;
+ }
+
+ SHA256_CTX ctx;
+ SHA256_Init(&ctx);
+
+ if (!writer->options().max_blocks) {
+ LOG(ERROR) << "CowWriter must specify maximum number of blocks";
+ return false;
+ }
+ const auto num_blocks = writer->options().max_blocks.value();
+
+ const auto block_size = writer->options().block_size;
+ std::string block(block_size, '\0');
+ for (uint64_t i = 0; i < num_blocks; i++) {
+ if (!ReadFully(rand, block.data(), block.size())) {
+ PLOG(ERROR) << "read /dev/urandom";
+ return false;
+ }
+ if (!writer->AddRawBlocks(i, block.data(), block.size())) {
+ LOG(ERROR) << "Failed to add raw block " << i;
+ return false;
+ }
+ SHA256_Update(&ctx, block.data(), block.size());
+ }
+
+ uint8_t out[32];
+ SHA256_Final(out, &ctx);
+ *hash = ToHexString(out, sizeof(out));
+ return true;
+ }
+
// Generate a snapshot that moves all the upper blocks down to the start.
// It doesn't really matter the order, we just want copies that reference
// blocks that won't exist if the partition shrinks.
@@ -1178,9 +1266,7 @@
ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
// Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
- }
+ ASSERT_TRUE(WriteSnapshots());
// Assert that source partitions aren't affected.
for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
@@ -1208,7 +1294,7 @@
// Initiate the merge and wait for it to be completed.
ASSERT_TRUE(init->InitiateMerge());
- ASSERT_EQ(init->IsSnapuserdRequired(), IsDaemonRequired());
+ ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);
{
// We should have started in SECOND_PHASE since nothing shrinks.
ASSERT_TRUE(AcquireLock());
@@ -1235,8 +1321,8 @@
}
TEST_F(SnapshotUpdateTest, DuplicateOps) {
- if (!ShouldUseCompression()) {
- GTEST_SKIP() << "Compression-only test";
+ if (!snapuserd_required_) {
+ GTEST_SKIP() << "snapuserd-only test";
}
// Execute the update.
@@ -1244,9 +1330,7 @@
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
// Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
- }
+ ASSERT_TRUE(WriteSnapshots());
std::vector<PartitionUpdate*> partitions = {sys_, vnd_, prd_};
for (auto* partition : partitions) {
@@ -1279,9 +1363,9 @@
// Test that shrinking and growing partitions at the same time is handled
// correctly in VABC.
TEST_F(SnapshotUpdateTest, SpaceSwapUpdate) {
- if (!ShouldUseCompression()) {
+ if (!snapuserd_required_) {
// b/179111359
- GTEST_SKIP() << "Skipping Virtual A/B Compression test";
+ GTEST_SKIP() << "Skipping snapuserd test";
}
auto old_sys_size = GetSize(sys_);
@@ -1310,8 +1394,8 @@
ASSERT_EQ(status.old_partition_size(), 3145728);
}
- ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
- ASSERT_TRUE(WriteSnapshotAndHash("vnd_b"));
+ ASSERT_TRUE(WriteSnapshotAndHash(sys_));
+ ASSERT_TRUE(WriteSnapshotAndHash(vnd_));
ASSERT_TRUE(ShiftAllSnapshotBlocks("prd_b", old_prd_size));
sync();
@@ -1342,7 +1426,7 @@
// Initiate the merge and wait for it to be completed.
ASSERT_TRUE(init->InitiateMerge());
- ASSERT_EQ(init->IsSnapuserdRequired(), IsDaemonRequired());
+ ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);
{
// Check that the merge phase is FIRST_PHASE until at least one call
// to ProcessUpdateState() occurs.
@@ -1396,9 +1480,9 @@
// Test that a transient merge consistency check failure can resume properly.
TEST_F(SnapshotUpdateTest, ConsistencyCheckResume) {
- if (!ShouldUseCompression()) {
+ if (!snapuserd_required_) {
// b/179111359
- GTEST_SKIP() << "Skipping Virtual A/B Compression test";
+ GTEST_SKIP() << "Skipping snapuserd test";
}
auto old_sys_size = GetSize(sys_);
@@ -1414,8 +1498,8 @@
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
- ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
- ASSERT_TRUE(WriteSnapshotAndHash("vnd_b"));
+ ASSERT_TRUE(WriteSnapshotAndHash(sys_));
+ ASSERT_TRUE(WriteSnapshotAndHash(vnd_));
ASSERT_TRUE(ShiftAllSnapshotBlocks("prd_b", old_prd_size));
sync();
@@ -1450,7 +1534,7 @@
// Initiate the merge and wait for it to be completed.
ASSERT_TRUE(init->InitiateMerge());
- ASSERT_EQ(init->IsSnapuserdRequired(), IsDaemonRequired());
+ ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);
{
// Check that the merge phase is FIRST_PHASE until at least one call
// to ProcessUpdateState() occurs.
@@ -1576,9 +1660,7 @@
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
// Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
- }
+ ASSERT_TRUE(WriteSnapshots());
// Assert that source partitions aren't affected.
for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
@@ -1737,9 +1819,7 @@
ASSERT_FALSE(image_manager_->BackingImageExists("prd_b-cow-img"));
// Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
- }
+ ASSERT_TRUE(WriteSnapshots());
// Assert that source partitions aren't affected.
for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
@@ -2011,9 +2091,7 @@
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
// Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name)) << name;
- }
+ ASSERT_TRUE(WriteSnapshots());
ASSERT_TRUE(sm->FinishedSnapshotWrites(true /* wipe */));
@@ -2053,17 +2131,15 @@
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
// Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name)) << name;
- }
+ ASSERT_TRUE(WriteSnapshots());
// Create a stale snapshot that should not exist.
{
ASSERT_TRUE(AcquireLock());
PartitionCowCreator cow_creator = {
- .compression_enabled = ShouldUseCompression(),
- .compression_algorithm = ShouldUseCompression() ? "gz" : "none",
+ .using_snapuserd = snapuserd_required_,
+ .compression_algorithm = snapuserd_required_ ? FLAGS_compression_method : "",
};
SnapshotStatus status;
status.set_name("sys_a");
@@ -2138,7 +2214,7 @@
// Map and write some data to target partition.
ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
- ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
+ ASSERT_TRUE(WriteSnapshotAndHash(sys_));
// Finish update.
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
@@ -2159,8 +2235,8 @@
// Test for overflow bit after update
TEST_F(SnapshotUpdateTest, Overflow) {
- if (ShouldUseCompression()) {
- GTEST_SKIP() << "No overflow bit set for userspace COWs";
+ if (snapuserd_required_) {
+ GTEST_SKIP() << "No overflow bit set for snapuserd COWs";
}
const auto actual_write_size = GetSize(sys_);
@@ -2174,7 +2250,7 @@
// Map and write some data to target partitions.
ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
- ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
+ ASSERT_TRUE(WriteSnapshotAndHash(sys_));
std::vector<android::dm::DeviceMapper::TargetInfo> table;
ASSERT_TRUE(DeviceMapper::Instance().GetTableStatus("sys_b", &table));
@@ -2234,8 +2310,8 @@
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
// Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
+ for (const auto& partition : {sys_, vnd_, prd_, dlkm}) {
+ ASSERT_TRUE(WriteSnapshotAndHash(partition));
}
// Assert that source partitions aren't affected.
@@ -2294,8 +2370,8 @@
};
TEST_F(SnapshotUpdateTest, DaemonTransition) {
- if (!ShouldUseCompression()) {
- GTEST_SKIP() << "Skipping Virtual A/B Compression test";
+ if (!snapuserd_required_) {
+ GTEST_SKIP() << "Skipping snapuserd test";
}
// Ensure a connection to the second-stage daemon, but use the first-stage
@@ -2359,9 +2435,7 @@
// Execute the update.
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
- }
+ ASSERT_TRUE(WriteSnapshots());
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
ASSERT_TRUE(sm->MapAllSnapshots(10s));
@@ -2411,13 +2485,6 @@
// fit in super, but not |prd|.
constexpr uint64_t partition_size = 3788_KiB;
SetSize(sys_, partition_size);
- SetSize(vnd_, partition_size);
- SetSize(prd_, 18_MiB);
-
- // Make sure |prd| does not fit in super at all. On VABC, this means we
- // fake an extra large COW for |vnd| to fill up super.
- vnd_->set_estimate_cow_size(30_MiB);
- prd_->set_estimate_cow_size(30_MiB);
AddOperationForPartitions();
@@ -2429,23 +2496,7 @@
GTEST_SKIP() << "Test does not apply to userspace snapshots";
}
- // Test that partitions prioritize using space in super.
- auto tgt = MetadataBuilder::New(*opener_, "super", 1);
- ASSERT_NE(tgt, nullptr);
- ASSERT_NE(nullptr, tgt->FindPartition("sys_b-cow"));
- ASSERT_NE(nullptr, tgt->FindPartition("vnd_b-cow"));
- ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
-
- // Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
- }
-
- // Assert that source partitions aren't affected.
- for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
- ASSERT_TRUE(IsPartitionUnchanged(name));
- }
-
+ ASSERT_TRUE(WriteSnapshots());
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
ASSERT_TRUE(UnmapAll());
@@ -2750,37 +2801,17 @@
}
}
-bool IsDaemonRequired() {
- if (FLAGS_force_config == "dmsnap") {
- return false;
+void KillSnapuserd() {
+ auto status = android::base::GetProperty("init.svc.snapuserd", "stopped");
+ if (status == "stopped") {
+ return;
}
-
- const std::string UNKNOWN = "unknown";
- const std::string vendor_release =
- android::base::GetProperty("ro.vendor.build.version.release_or_codename", UNKNOWN);
-
- // No userspace snapshots if vendor partition is on Android 12
- // However, for GRF devices, snapuserd daemon will be on
- // vendor ramdisk in Android 12.
- if (vendor_release.find("12") != std::string::npos) {
- return true;
+ auto snapuserd_client = SnapuserdClient::Connect(kSnapuserdSocket, 5s);
+ if (!snapuserd_client) {
+ return;
}
-
- if (!FLAGS_force_config.empty()) {
- return true;
- }
-
- return IsUserspaceSnapshotsEnabled();
-}
-
-bool ShouldUseCompression() {
- if (FLAGS_force_config == "vab" || FLAGS_force_config == "dmsnap") {
- return false;
- }
- if (FLAGS_force_config == "vabc") {
- return true;
- }
- return IsCompressionEnabled();
+ snapuserd_client->DetachSnapuserd();
+ snapuserd_client->CloseConnection();
}
} // namespace snapshot
@@ -2793,35 +2824,20 @@
android::base::SetProperty("ctl.stop", "snapuserd");
- std::unordered_set<std::string> configs = {"", "dmsnap", "vab", "vabc"};
- if (configs.count(FLAGS_force_config) == 0) {
+ std::unordered_set<std::string> modes = {"", "vab-legacy", "vabc-legacy"};
+ if (modes.count(FLAGS_force_mode) == 0) {
std::cerr << "Unexpected force_config argument\n";
return 1;
}
- if (FLAGS_force_config == "dmsnap") {
- if (!android::base::SetProperty("snapuserd.test.dm.snapshots", "1")) {
- return testing::AssertionFailure()
- << "Failed to disable property: virtual_ab.userspace.snapshots.enabled";
- }
- }
-
- if (FLAGS_force_iouring_disable == "iouring_disabled") {
- if (!android::base::SetProperty("snapuserd.test.io_uring.force_disable", "1")) {
- return testing::AssertionFailure()
- << "Failed to disable property: snapuserd.test.io_uring.disabled";
- }
- }
+ // This is necessary if the configuration we're testing doesn't match the device.
+ android::snapshot::KillSnapuserd();
int ret = RUN_ALL_TESTS();
- if (FLAGS_force_config == "dmsnap") {
- android::base::SetProperty("snapuserd.test.dm.snapshots", "0");
- }
+ android::base::SetProperty("snapuserd.test.dm.snapshots", "0");
+ android::base::SetProperty("snapuserd.test.io_uring.force_disable", "0");
- if (FLAGS_force_iouring_disable == "iouring_disabled") {
- android::base::SetProperty("snapuserd.test.io_uring.force_disable", "0");
- }
-
+ android::snapshot::KillSnapuserd();
return ret;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index 57c599c..64e0b8a 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -87,6 +87,7 @@
"liblog",
"libsnapshot_cow",
"libz",
+ "liblz4",
"libext4_utils",
"liburing",
],
@@ -145,6 +146,7 @@
name: "cow_snapuserd_test",
defaults: [
"fs_mgr_defaults",
+ "libsnapshot_cow_defaults",
],
srcs: [
"dm-snapshot-merge/cow_snapuserd_test.cpp",
@@ -186,6 +188,7 @@
name: "snapuserd_test",
defaults: [
"fs_mgr_defaults",
+ "libsnapshot_cow_defaults",
],
srcs: [
"user-space-merge/snapuserd_test.cpp",
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 afc653f..8939b78 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -147,17 +147,18 @@
NotifyRAForMergeReady();
}
-void SnapshotHandler::CheckMergeCompletionStatus() {
+bool SnapshotHandler::CheckMergeCompletionStatus() {
if (!merge_initiated_) {
SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: "
<< reader_->get_num_total_data_ops();
- return;
+ return false;
}
struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
SNAP_LOG(INFO) << "Merge-status: Total-Merged-ops: " << ch->num_merge_ops
<< " Total-data-ops: " << reader_->get_num_total_data_ops();
+ return true;
}
bool SnapshotHandler::ReadMetadata() {
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 90fba75..42237ef 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -18,6 +18,9 @@
#include <stdint.h>
#include <stdlib.h>
#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <unistd.h>
#include <condition_variable>
#include <cstring>
@@ -56,6 +59,8 @@
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_ << ": "
@@ -306,7 +311,7 @@
const bool& IsAttached() const { return attached_; }
void AttachControlDevice() { attached_ = true; }
- void CheckMergeCompletionStatus();
+ bool CheckMergeCompletionStatus();
bool CommitMerge(int num_merge_ops);
void CloseFds() { cow_fd_ = {}; }
@@ -337,6 +342,8 @@
// State transitions for merge
void InitiateMerge();
+ void MonitorMerge();
+ void WakeupMonitorMergeThread();
void WaitForMergeComplete();
bool WaitForMergeBegin();
void NotifyRAForMergeReady();
@@ -365,6 +372,7 @@
void SetSocketPresent(bool socket) { is_socket_present_ = socket; }
void SetIouringEnabled(bool io_uring_enabled) { is_io_uring_enabled_ = io_uring_enabled; }
bool MergeInitiated() { return merge_initiated_; }
+ bool MergeMonitored() { return merge_monitored_; }
double GetMergePercentage() { return merge_completion_percentage_; }
// Merge Block State Transitions
@@ -431,6 +439,7 @@
double merge_completion_percentage_;
bool merge_initiated_ = false;
+ bool merge_monitored_ = false;
bool attached_ = false;
bool is_socket_present_;
bool is_io_uring_enabled_ = false;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
index c26a2cd..63f47d6 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
@@ -71,16 +71,16 @@
}
bool Worker::MergeReplaceZeroOps() {
- // Flush every 8192 ops. Since all ops are independent and there is no
+ // Flush after merging 2MB. Since all ops are independent and there is no
// dependency between COW ops, we will flush the data and the number
- // of ops merged in COW file for every 8192 ops. If there is a crash,
- // we will end up replaying some of the COW ops which were already merged.
- // That is ok.
+ // of ops merged in COW block device. If there is a crash, we will
+ // end up replaying some of the COW ops which were already merged. That is
+ // ok.
//
- // Why 8192 ops ? Increasing this may improve merge time 3-4 seconds but
- // we need to make sure that we checkpoint; 8k ops seems optimal. In-case
- // if there is a crash merge should always make forward progress.
- int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ) * 32;
+ // Although increasing this greater than 2MB may help in improving merge
+ // times; however, on devices with low memory, this can be problematic
+ // when there are multiple merge threads in parallel.
+ int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ) * 2;
int num_ops_merged = 0;
SNAP_LOG(INFO) << "MergeReplaceZeroOps started....";
@@ -543,6 +543,10 @@
return true;
}
+ if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
+ SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
+ }
+
SNAP_LOG(INFO) << "Merge starting..";
if (!Init()) {
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 fa2866f..b9e4255 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -727,6 +727,10 @@
InitializeIouring();
+ if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
+ SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
+ }
+
while (!RAIterDone()) {
if (!ReadAheadIOStart()) {
break;
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 b7f7f54..1bf33c8 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -60,6 +60,14 @@
return DaemonOps::INVALID;
}
+UserSnapshotServer::UserSnapshotServer() {
+ monitor_merge_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+ if (monitor_merge_event_fd_ == -1) {
+ PLOG(FATAL) << "monitor_merge_event_fd_: failed to create eventfd";
+ }
+ terminating_ = false;
+}
+
UserSnapshotServer::~UserSnapshotServer() {
// Close any client sockets that were added via AcceptClient().
for (size_t i = 1; i < watched_fds_.size(); i++) {
@@ -250,7 +258,7 @@
return Sendmsg(fd, "fail");
}
- if (!StartMerge(*iter)) {
+ if (!StartMerge(&lock, *iter)) {
return Sendmsg(fd, "fail");
}
@@ -307,7 +315,7 @@
}
handler->snapuserd()->CloseFds();
- handler->snapuserd()->CheckMergeCompletionStatus();
+ bool merge_completed = handler->snapuserd()->CheckMergeCompletionStatus();
handler->snapuserd()->UnmapBufferRegion();
auto misc_name = handler->misc_name();
@@ -315,7 +323,11 @@
{
std::lock_guard<std::mutex> lock(lock_);
- num_partitions_merge_complete_ += 1;
+ if (merge_completed) {
+ num_partitions_merge_complete_ += 1;
+ active_merge_threads_ -= 1;
+ WakeupMonitorMergeThread();
+ }
handler->SetThreadTerminated();
auto iter = FindHandler(&lock, handler->misc_name());
if (iter == dm_users_.end()) {
@@ -427,6 +439,9 @@
if (th.joinable()) th.join();
}
+
+ stop_monitor_merge_thread_ = true;
+ WakeupMonitorMergeThread();
}
void UserSnapshotServer::AddWatchedFd(android::base::borrowed_fd fd, int events) {
@@ -511,13 +526,24 @@
return true;
}
-bool UserSnapshotServer::StartMerge(const std::shared_ptr<UserSnapshotDmUserHandler>& handler) {
+bool UserSnapshotServer::StartMerge(std::lock_guard<std::mutex>* proof_of_lock,
+ const std::shared_ptr<UserSnapshotDmUserHandler>& handler) {
+ CHECK(proof_of_lock);
+
if (!handler->snapuserd()->IsAttached()) {
LOG(ERROR) << "Handler not attached to dm-user - Merge thread cannot be started";
return false;
}
- handler->snapuserd()->InitiateMerge();
+ handler->snapuserd()->MonitorMerge();
+
+ if (!is_merge_monitor_started_.has_value()) {
+ std::thread(&UserSnapshotServer::MonitorMerge, this).detach();
+ is_merge_monitor_started_ = true;
+ }
+
+ merge_handlers_.push(handler);
+ WakeupMonitorMergeThread();
return true;
}
@@ -599,6 +625,42 @@
return true;
}
+void UserSnapshotServer::WakeupMonitorMergeThread() {
+ uint64_t notify = 1;
+ ssize_t rc = TEMP_FAILURE_RETRY(write(monitor_merge_event_fd_.get(), ¬ify, sizeof(notify)));
+ if (rc < 0) {
+ PLOG(FATAL) << "failed to notify monitor merge thread";
+ }
+}
+
+void UserSnapshotServer::MonitorMerge() {
+ while (!stop_monitor_merge_thread_) {
+ uint64_t testVal;
+ ssize_t ret =
+ TEMP_FAILURE_RETRY(read(monitor_merge_event_fd_.get(), &testVal, sizeof(testVal)));
+ if (ret == -1) {
+ PLOG(FATAL) << "Failed to read from eventfd";
+ } else if (ret == 0) {
+ LOG(FATAL) << "Hit EOF on eventfd";
+ }
+
+ LOG(INFO) << "MonitorMerge: active-merge-threads: " << active_merge_threads_;
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ while (active_merge_threads_ < kMaxMergeThreads && merge_handlers_.size() > 0) {
+ auto handler = merge_handlers_.front();
+ merge_handlers_.pop();
+ LOG(INFO) << "Starting merge for partition: "
+ << handler->snapuserd()->GetMiscName();
+ handler->snapuserd()->InitiateMerge();
+ active_merge_threads_ += 1;
+ }
+ }
+ }
+
+ LOG(INFO) << "Exiting MonitorMerge: size: " << merge_handlers_.size();
+}
+
bool UserSnapshotServer::WaitForSocket() {
auto scope_guard = android::base::make_scope_guard([this]() -> void { JoinAllThreads(); });
@@ -655,6 +717,7 @@
if (!StartWithSocket(true)) {
return false;
}
+
return Run();
}
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 00734a9..c2af61f 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -15,6 +15,7 @@
#pragma once
#include <poll.h>
+#include <sys/eventfd.h>
#include <cstdio>
#include <cstring>
@@ -22,6 +23,8 @@
#include <future>
#include <iostream>
#include <mutex>
+#include <optional>
+#include <queue>
#include <sstream>
#include <string>
#include <thread>
@@ -34,6 +37,7 @@
namespace snapshot {
static constexpr uint32_t kMaxPacketSize = 512;
+static constexpr uint8_t kMaxMergeThreads = 2;
enum class DaemonOps {
INIT,
@@ -85,13 +89,19 @@
std::vector<struct pollfd> watched_fds_;
bool is_socket_present_ = false;
int num_partitions_merge_complete_ = 0;
+ int active_merge_threads_ = 0;
+ bool stop_monitor_merge_thread_ = false;
bool is_server_running_ = false;
bool io_uring_enabled_ = false;
+ std::optional<bool> is_merge_monitor_started_;
+
+ android::base::unique_fd monitor_merge_event_fd_;
std::mutex lock_;
using HandlerList = std::vector<std::shared_ptr<UserSnapshotDmUserHandler>>;
HandlerList dm_users_;
+ std::queue<std::shared_ptr<UserSnapshotDmUserHandler>> merge_handlers_;
void AddWatchedFd(android::base::borrowed_fd fd, int events);
void AcceptClient();
@@ -109,6 +119,8 @@
bool IsTerminating() { return terminating_; }
void RunThread(std::shared_ptr<UserSnapshotDmUserHandler> handler);
+ void MonitorMerge();
+
void JoinAllThreads();
bool StartWithSocket(bool start_listening);
@@ -122,7 +134,7 @@
bool UpdateVerification(std::lock_guard<std::mutex>* proof_of_lock);
public:
- UserSnapshotServer() { terminating_ = false; }
+ UserSnapshotServer();
~UserSnapshotServer();
bool Start(const std::string& socketname);
@@ -136,9 +148,11 @@
const std::string& backing_device,
const std::string& base_path_merge);
bool StartHandler(const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
- bool StartMerge(const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
+ bool StartMerge(std::lock_guard<std::mutex>* proof_of_lock,
+ const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
std::string GetMergeStatus(const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
+ void WakeupMonitorMergeThread();
void SetTerminating() { terminating_ = true; }
void ReceivedSocketSignal() { received_socket_signal_ = true; }
void SetServerRunning() { is_server_running_ = true; }
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
index d4e1d7c..28c9f68 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
@@ -165,6 +165,13 @@
using namespace android::dm;
using android::base::unique_fd;
+void SnapshotHandler::MonitorMerge() {
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ merge_monitored_ = true;
+ }
+}
+
// This is invoked once primarily by update-engine to initiate
// the merge
void SnapshotHandler::InitiateMerge() {
@@ -361,10 +368,16 @@
std::string SnapshotHandler::GetMergeStatus() {
bool merge_not_initiated = false;
+ bool merge_monitored = false;
bool merge_failed = false;
{
std::lock_guard<std::mutex> lock(lock_);
+
+ if (MergeMonitored()) {
+ merge_monitored = true;
+ }
+
if (!MergeInitiated()) {
merge_not_initiated = true;
}
@@ -387,6 +400,12 @@
return "snapshot-merge-complete";
}
+ // Merge monitor thread is tracking the merge but the merge thread
+ // is not started yet.
+ if (merge_monitored) {
+ return "snapshot-merge";
+ }
+
// Return the state as "snapshot". If the device was rebooted during
// merge, we will return the status as "snapshot". This is ok, as
// libsnapshot will explicitly resume the merge. This is slightly
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 71fe124..b05123a 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -18,10 +18,12 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parsebool.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
+#include <liblp/property_fetcher.h>
#include <openssl/sha.h>
#include <payload_consumer/file_descriptor.h>
@@ -128,48 +130,6 @@
return true;
}
-bool WriteRandomData(ICowWriter* writer, std::string* hash) {
- unique_fd rand(open("/dev/urandom", O_RDONLY));
- if (rand < 0) {
- PLOG(ERROR) << "open /dev/urandom";
- return false;
- }
-
- SHA256_CTX ctx;
- if (hash) {
- SHA256_Init(&ctx);
- }
-
- if (!writer->options().max_blocks) {
- LOG(ERROR) << "CowWriter must specify maximum number of blocks";
- return false;
- }
- uint64_t num_blocks = writer->options().max_blocks.value();
-
- size_t block_size = writer->options().block_size;
- std::string block(block_size, '\0');
- for (uint64_t i = 0; i < num_blocks; i++) {
- if (!ReadFully(rand, block.data(), block.size())) {
- PLOG(ERROR) << "read /dev/urandom";
- return false;
- }
- if (!writer->AddRawBlocks(i, block.data(), block.size())) {
- LOG(ERROR) << "Failed to add raw block " << i;
- return false;
- }
- if (hash) {
- SHA256_Update(&ctx, block.data(), block.size());
- }
- }
-
- if (hash) {
- uint8_t out[32];
- SHA256_Final(out, &ctx);
- *hash = ToHexString(out, sizeof(out));
- }
- return true;
-}
-
std::string HashSnapshot(ISnapshotWriter* writer) {
auto reader = writer->OpenReader();
if (!reader) {
@@ -320,5 +280,38 @@
return android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
}
+SnapshotTestPropertyFetcher::SnapshotTestPropertyFetcher(
+ const std::string& slot_suffix, std::unordered_map<std::string, std::string>&& props)
+ : properties_(std::move(props)) {
+ properties_["ro.boot.slot_suffix"] = slot_suffix;
+ properties_["ro.boot.dynamic_partitions"] = "true";
+ properties_["ro.boot.dynamic_partitions_retrofit"] = "false";
+ properties_["ro.virtual_ab.enabled"] = "true";
+}
+
+std::string SnapshotTestPropertyFetcher::GetProperty(const std::string& key,
+ const std::string& defaultValue) {
+ auto iter = properties_.find(key);
+ if (iter == properties_.end()) {
+ return android::base::GetProperty(key, defaultValue);
+ }
+ return iter->second;
+}
+
+bool SnapshotTestPropertyFetcher::GetBoolProperty(const std::string& key, bool defaultValue) {
+ auto iter = properties_.find(key);
+ if (iter == properties_.end()) {
+ return android::base::GetBoolProperty(key, defaultValue);
+ }
+ switch (android::base::ParseBool(iter->second)) {
+ case android::base::ParseBoolResult::kTrue:
+ return true;
+ case android::base::ParseBoolResult::kFalse:
+ return false;
+ default:
+ return defaultValue;
+ }
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index f01bec9..0a1be0d 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -26,6 +26,7 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <fs_mgr/roots.h>
+#include <liblp/property_fetcher.h>
using android::dm::kSectorSize;
using android::fiemap::FiemapStatus;
@@ -33,6 +34,7 @@
using android::fs_mgr::EnsurePathUnmounted;
using android::fs_mgr::Fstab;
using android::fs_mgr::GetEntryForPath;
+using android::fs_mgr::IPropertyFetcher;
using android::fs_mgr::MetadataBuilder;
using android::fs_mgr::Partition;
using android::fs_mgr::ReadDefaultFstab;
@@ -184,16 +186,50 @@
new_extent->set_num_blocks(num_blocks);
}
-bool IsCompressionEnabled() {
- return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false);
+bool GetLegacyCompressionEnabledProperty() {
+ auto fetcher = IPropertyFetcher::GetInstance();
+ return fetcher->GetBoolProperty("ro.virtual_ab.compression.enabled", false);
}
-bool IsUserspaceSnapshotsEnabled() {
- return android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
+bool GetUserspaceSnapshotsEnabledProperty() {
+ auto fetcher = IPropertyFetcher::GetInstance();
+ return fetcher->GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
}
-bool IsIouringEnabled() {
- return android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
+bool CanUseUserspaceSnapshots() {
+ if (!GetUserspaceSnapshotsEnabledProperty()) {
+ return false;
+ }
+
+ auto fetcher = IPropertyFetcher::GetInstance();
+
+ const std::string UNKNOWN = "unknown";
+ const std::string vendor_release =
+ fetcher->GetProperty("ro.vendor.build.version.release_or_codename", UNKNOWN);
+
+ // No user-space snapshots if vendor partition is on Android 12
+ if (vendor_release.find("12") != std::string::npos) {
+ LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
+ << vendor_release;
+ return false;
+ }
+
+ if (IsDmSnapshotTestingEnabled()) {
+ LOG(INFO) << "Userspace snapshots disabled for testing";
+ return false;
+ }
+
+ return true;
+}
+
+bool GetIouringEnabledProperty() {
+ auto fetcher = IPropertyFetcher::GetInstance();
+ return fetcher->GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
+}
+
+bool GetXorCompressionEnabledProperty() {
+ auto fetcher = IPropertyFetcher::GetInstance();
+ return fetcher->GetBoolProperty("ro.virtual_ab.compression.xor.enabled", false);
}
std::string GetOtherPartitionName(const std::string& name) {
@@ -205,7 +241,8 @@
}
bool IsDmSnapshotTestingEnabled() {
- return android::base::GetBoolProperty("snapuserd.test.dm.snapshots", false);
+ auto fetcher = IPropertyFetcher::GetInstance();
+ return fetcher->GetBoolProperty("snapuserd.test.dm.snapshots", false);
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 0ef3234..16aa81a 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -129,15 +129,16 @@
void AppendExtent(google::protobuf::RepeatedPtrField<chromeos_update_engine::Extent>* extents,
uint64_t start_block, uint64_t num_blocks);
-bool IsCompressionEnabled();
+bool GetLegacyCompressionEnabledProperty();
+bool GetUserspaceSnapshotsEnabledProperty();
+bool GetIouringEnabledProperty();
+bool GetXorCompressionEnabledProperty();
-bool IsUserspaceSnapshotsEnabled();
-
+bool CanUseUserspaceSnapshots();
bool IsDmSnapshotTestingEnabled();
-bool IsIouringEnabled();
-
// Swap the suffix of a partition name.
std::string GetOtherPartitionName(const std::string& name);
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/vts_ota_config_test.cpp b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
index afc2d81..02bcc34 100644
--- a/fs_mgr/libsnapshot/vts_ota_config_test.cpp
+++ b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
@@ -17,7 +17,14 @@
#include <android-base/properties.h>
#include <gtest/gtest.h>
+static int GetVsrLevel() {
+ return android::base::GetIntProperty("ro.vendor.api_level", -1);
+}
+
TEST(VAB, Enabled) {
ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.enabled", false));
+ if (GetVsrLevel() < __ANDROID_API_T__) {
+ GTEST_SKIP();
+ }
ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false));
}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 6c881c0..e33681c 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -1109,14 +1109,17 @@
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
std::string fstab_contents = R"fs(
+data /data f2fs noatime wait,latemount
system /system erofs ro wait,logical,first_stage_mount
system /system ext4 ro wait,logical,first_stage_mount
vendor /vendor ext4 ro wait,logical,first_stage_mount
-data /data f2fs noatime wait
)fs";
ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+ // If GSI is installed, ReadFstabFromFile() would have called TransformFstabForDsu() implicitly.
+ // In other words, TransformFstabForDsu() would be called two times if running CTS-on-GSI,
+ // which implies TransformFstabForDsu() should be idempotent.
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
@@ -1124,21 +1127,23 @@
auto entry = fstab.begin();
- EXPECT_EQ("/system", entry->mount_point);
- EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("/data", entry->mount_point);
+ EXPECT_EQ("userdata_gsi", entry->blk_device);
entry++;
EXPECT_EQ("/system", entry->mount_point);
EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("erofs", entry->fs_type);
+ entry++;
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("ext4", entry->fs_type);
entry++;
EXPECT_EQ("/vendor", entry->mount_point);
EXPECT_EQ("vendor", entry->blk_device);
entry++;
-
- EXPECT_EQ("/data", entry->mount_point);
- EXPECT_EQ("userdata_gsi", entry->blk_device);
- entry++;
}
TEST(fs_mgr, TransformFstabForDsu_synthesisExt4Entry) {
@@ -1147,7 +1152,7 @@
std::string fstab_contents = R"fs(
system /system erofs ro wait,logical,first_stage_mount
vendor /vendor ext4 ro wait,logical,first_stage_mount
-data /data f2fs noatime wait
+data /data f2fs noatime wait,latemount
)fs";
ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
@@ -1177,3 +1182,39 @@
EXPECT_EQ("userdata_gsi", entry->blk_device);
entry++;
}
+
+TEST(fs_mgr, TransformFstabForDsu_synthesisAllMissingEntries) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+data /data f2fs noatime wait,latemount
+vendor /vendor ext4 ro wait,logical,first_stage_mount
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
+ ASSERT_EQ(4U, fstab.size());
+
+ auto entry = fstab.begin();
+
+ EXPECT_EQ("/data", entry->mount_point);
+ EXPECT_EQ("userdata_gsi", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/vendor", entry->mount_point);
+ EXPECT_EQ("vendor", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("ext4", entry->fs_type);
+ entry++;
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("erofs", entry->fs_type);
+ entry++;
+}
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
index aac2cfd..b8b34e2 100644
--- a/fs_mgr/tests/vts_fs_test.cpp
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -23,13 +23,16 @@
#include <gtest/gtest.h>
#include <libdm/dm.h>
+using testing::Contains;
+using testing::Not;
+
static int GetVsrLevel() {
return android::base::GetIntProperty("ro.vendor.api_level", -1);
}
TEST(fs, ErofsSupported) {
- // S and higher for this test.
- if (GetVsrLevel() < __ANDROID_API_S__) {
+ // T-launch GKI kernels and higher must support EROFS.
+ if (GetVsrLevel() < __ANDROID_API_T__) {
GTEST_SKIP();
}
@@ -117,3 +120,30 @@
android::fs_mgr::Fstab fstab;
EXPECT_FALSE(android::fs_mgr::ReadFstabFromDt(&fstab, false));
}
+
+TEST(fs, NoLegacyVerifiedBoot) {
+ if (GetVsrLevel() < __ANDROID_API_T__) {
+ GTEST_SKIP();
+ }
+
+ const auto& default_fstab_path = android::fs_mgr::GetFstabPath();
+ EXPECT_FALSE(default_fstab_path.empty());
+
+ std::string fstab_str;
+ EXPECT_TRUE(android::base::ReadFileToString(default_fstab_path, &fstab_str,
+ /* follow_symlinks = */ true));
+
+ for (const auto& line : android::base::Split(fstab_str, "\n")) {
+ auto fields = android::base::Tokenize(line, " \t");
+ // Ignores empty lines and comments.
+ if (fields.empty() || android::base::StartsWith(fields.front(), '#')) {
+ continue;
+ }
+ // Each line in a fstab should have at least five entries.
+ // <src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>
+ ASSERT_GE(fields.size(), 5);
+ EXPECT_THAT(android::base::Split(fields[4], ","), Not(Contains("verify")))
+ << "AVB 1.0 isn't supported now, but the 'verify' flag is found:\n"
+ << " " << line;
+ }
+}
diff --git a/init/Android.bp b/init/Android.bp
index b4bc170..2dd9683 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -164,6 +164,7 @@
"libcgrouprc_format",
"libfsverity_init",
"liblmkd_utils",
+ "liblz4",
"libmini_keyctl_static",
"libmodprobe",
"libprocinfo",
@@ -362,6 +363,7 @@
"libext2_uuid",
"libprotobuf-cpp-lite",
"libsnapshot_cow",
+ "liblz4",
"libsnapshot_init",
"update_metadata-protos",
"libprocinfo",
diff --git a/init/TEST_MAPPING b/init/TEST_MAPPING
index fa1627c..36ca379 100644
--- a/init/TEST_MAPPING
+++ b/init/TEST_MAPPING
@@ -10,7 +10,7 @@
"name": "MicrodroidHostTestCases"
}
],
- "hwasan-postsubmit": [
+ "hwasan-presubmit": [
{
"name": "CtsInitTestCases"
},
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 9e1d93c..38f6f39 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -426,7 +426,7 @@
return ErrnoError() << "fchmodat() failed on " << options.target;
}
}
- if (fscrypt_is_native()) {
+ if (IsFbeEnabled()) {
if (!FscryptSetDirectoryPolicy(ref_basename, options.fscrypt_action, options.target)) {
return reboot_into_recovery(
{"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + options.target});
@@ -1175,7 +1175,7 @@
auto reboot = [reboot_reason, should_reboot_into_recovery](const std::string& message) {
// TODO (b/122850122): support this in gsi
if (should_reboot_into_recovery) {
- if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
+ if (IsFbeEnabled() && !android::gsi::IsGsiRunning()) {
LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
if (auto result = reboot_into_recovery(
{"--prompt_and_wipe_data", "--reason="s + reboot_reason});
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 042988e..07ce458 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -507,16 +507,16 @@
SaveRamdiskPathToSnapuserd();
}
- if (MountPartition(system_partition, false /* erase_same_mounts */)) {
- if (dsu_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
- LOG(ERROR) << "check_most_at_once forbidden on external media";
- return false;
- }
- SwitchRoot("/system");
- } else {
+ if (!MountPartition(system_partition, false /* erase_same_mounts */)) {
PLOG(ERROR) << "Failed to mount /system";
return false;
}
+ if (dsu_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
+ LOG(ERROR) << "check_at_most_once forbidden on external media";
+ return false;
+ }
+
+ SwitchRoot("/system");
return true;
}
@@ -651,14 +651,9 @@
return;
}
- std::string lp_names = "";
- std::vector<std::string> dsu_partitions;
- for (auto&& name : images->GetAllBackingImages()) {
- dsu_partitions.push_back(name);
- lp_names += name + ",";
- }
- // Publish the logical partition names for TransformFstabForDsu
- WriteFile(gsi::kGsiLpNamesFile, lp_names);
+ // Publish the logical partition names for TransformFstabForDsu() and ReadFstabFromFile().
+ const auto dsu_partitions = images->GetAllBackingImages();
+ WriteFile(gsi::kGsiLpNamesFile, android::base::Join(dsu_partitions, ","));
TransformFstabForDsu(&fstab_, active_dsu, dsu_partitions);
}
diff --git a/init/init.cpp b/init/init.cpp
index 4955bc5..5f516b7 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -54,6 +54,7 @@
#include <libavb/libavb.h>
#include <libgsi/libgsi.h>
#include <libsnapshot/snapshot.h>
+#include <logwrap/logwrap.h>
#include <processgroup/processgroup.h>
#include <processgroup/setup.h>
#include <selinux/android.h>
@@ -346,8 +347,8 @@
}
#endif // RECOVERY
parser.AddSectionParser("service",
- std::make_unique<ServiceParser>(&service_list, subcontext, std::nullopt,
- /*from_apex=*/true));
+ std::make_unique<ServiceParser>(&service_list, subcontext,
+ std::nullopt));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontext));
return parser;
@@ -442,6 +443,45 @@
return {};
}
+static Result<void> DoUnloadApex(const std::string& apex_name) {
+ std::string prop_name = "init.apex." + apex_name;
+ // TODO(b/232114573) remove services and actions read from the apex
+ // TODO(b/232799709) kill services from the apex
+ SetProperty(prop_name, "unloaded");
+ return {};
+}
+
+static Result<void> UpdateApexLinkerConfig(const std::string& apex_name) {
+ // Do not invoke linkerconfig when there's no bin/ in the apex.
+ const std::string bin_path = "/apex/" + apex_name + "/bin";
+ if (access(bin_path.c_str(), R_OK) != 0) {
+ return {};
+ }
+ const char* linkerconfig_binary = "/apex/com.android.runtime/bin/linkerconfig";
+ const char* linkerconfig_target = "/linkerconfig";
+ const char* arguments[] = {linkerconfig_binary, "--target", linkerconfig_target, "--apex",
+ apex_name.c_str(), "--strict"};
+
+ if (logwrap_fork_execvp(arraysize(arguments), arguments, nullptr, false, LOG_KLOG, false,
+ nullptr) != 0) {
+ return ErrnoError() << "failed to execute linkerconfig";
+ }
+ LOG(INFO) << "Generated linker configuration for " << apex_name;
+ return {};
+}
+
+static Result<void> DoLoadApex(const std::string& apex_name) {
+ std::string prop_name = "init.apex." + apex_name;
+ // TODO(b/232799709) read .rc files from the apex
+
+ if (auto result = UpdateApexLinkerConfig(apex_name); !result.ok()) {
+ return result.error();
+ }
+
+ SetProperty(prop_name, "loaded");
+ return {};
+}
+
enum class ControlTarget {
SERVICE, // function gets called for the named service
INTERFACE, // action gets called for every service that holds this interface
@@ -465,6 +505,17 @@
return control_message_functions;
}
+static Result<void> HandleApexControlMessage(std::string_view action, const std::string& name,
+ std::string_view message) {
+ if (action == "load") {
+ return DoLoadApex(name);
+ } else if (action == "unload") {
+ return DoUnloadApex(name);
+ } else {
+ return Error() << "Unknown control msg '" << message << "'";
+ }
+}
+
static bool HandleControlMessage(std::string_view message, const std::string& name,
pid_t from_pid) {
std::string cmdline_path = StringPrintf("proc/%d/cmdline", from_pid);
@@ -476,8 +527,20 @@
process_cmdline = "unknown process";
}
- Service* service = nullptr;
auto action = message;
+ if (ConsumePrefix(&action, "apex_")) {
+ if (auto result = HandleApexControlMessage(action, name, message); !result.ok()) {
+ LOG(ERROR) << "Control message: Could not ctl." << message << " for '" << name
+ << "' from pid: " << from_pid << " (" << process_cmdline
+ << "): " << result.error();
+ return false;
+ }
+ LOG(INFO) << "Control message: Processed ctl." << message << " for '" << name
+ << "' from pid: " << from_pid << " (" << process_cmdline << ")";
+ return true;
+ }
+
+ Service* service = nullptr;
if (ConsumePrefix(&action, "interface_")) {
service = ServiceList::GetInstance().FindInterface(name);
} else {
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 0dc6ff6..5651a83 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -35,6 +35,10 @@
#include "util.h"
using android::base::GetIntProperty;
+using android::base::GetProperty;
+using android::base::SetProperty;
+using android::base::WaitForProperty;
+using namespace std::literals;
namespace android {
namespace init {
@@ -334,6 +338,20 @@
EXPECT_EQ(2, num_executed);
}
+TEST(init, RespondToCtlApexMessages) {
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Skipping test, must be run as root.";
+ return;
+ }
+
+ std::string apex_name = "com.android.apex.cts.shim";
+ SetProperty("ctl.apex_unload", apex_name);
+ EXPECT_TRUE(WaitForProperty("init.apex." + apex_name, "unloaded", 10s));
+
+ SetProperty("ctl.apex_load", apex_name);
+ EXPECT_TRUE(WaitForProperty("init.apex." + apex_name, "loaded", 10s));
+}
+
TEST(init, RejectsCriticalAndOneshotService) {
if (GetIntProperty("ro.product.first_api_level", 10000) < 30) {
GTEST_SKIP() << "Test only valid for devices launching with R or later";
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index bce1cc3..fead371 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -190,15 +190,33 @@
return success;
}
+// Switch the mount namespace of the current process from bootstrap to default OR from default to
+// bootstrap. If the current mount namespace is neither bootstrap nor default, keep it that way.
Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace) {
if (IsRecoveryMode() || !IsApexUpdatable()) {
// we don't have multiple namespaces in recovery mode or if apex is not updatable
return {};
}
- const auto& ns_id = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_id : default_ns_id;
+
+ const std::string current_namespace_id = GetMountNamespaceId();
+ MountNamespace current_mount_namespace;
+ if (current_namespace_id == bootstrap_ns_id) {
+ current_mount_namespace = NS_BOOTSTRAP;
+ } else if (current_namespace_id == default_ns_id) {
+ current_mount_namespace = NS_DEFAULT;
+ } else {
+ // services with `namespace mnt` start in its own mount namespace. So we need to keep it.
+ return {};
+ }
+
+ // We're already in the target mount namespace.
+ if (current_mount_namespace == target_mount_namespace) {
+ return {};
+ }
+
const auto& ns_fd = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_fd : default_ns_fd;
const auto& ns_name = target_mount_namespace == NS_BOOTSTRAP ? "bootstrap" : "default";
- if (ns_id != GetMountNamespaceId() && ns_fd.get() != -1) {
+ if (ns_fd.get() != -1) {
if (setns(ns_fd.get(), CLONE_NEWNS) == -1) {
return ErrnoError() << "Failed to switch to " << ns_name << " mount namespace.";
}
diff --git a/init/security.cpp b/init/security.cpp
index 970696e..0e9f6c2 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -15,6 +15,7 @@
*/
#include "security.h"
+#include "util.h"
#include <errno.h>
#include <fcntl.h>
@@ -89,7 +90,7 @@
// Set /proc/sys/vm/mmap_rnd_bits and potentially
// /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
-// Returns -1 if unable to set these to an acceptable value.
+// Returns an error if unable to set these to an acceptable value.
//
// To support this sysctl, the following upstream commits are needed:
//
@@ -105,13 +106,20 @@
// uml does not support mmap_rnd_bits
return {};
#elif defined(__aarch64__)
- // arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE
- if (SetMmapRndBitsMin(33, 24, false) && SetMmapRndBitsMin(16, 16, true)) {
+ // arm64 architecture supports 18 - 33 rnd bits depending on pagesize and
+ // VA_SIZE. However the kernel might have been compiled with a narrower
+ // range using CONFIG_ARCH_MMAP_RND_BITS_MIN/MAX. To use the maximum
+ // supported number of bits, we start from the theoretical maximum of 33
+ // bits and try smaller values until we reach 24 bits which is the
+ // Android-specific minimum. Don't go lower even if the configured maximum
+ // is smaller than 24.
+ if (SetMmapRndBitsMin(33, 24, false) && (!Has32BitAbi() || SetMmapRndBitsMin(16, 16, true))) {
return {};
}
#elif defined(__x86_64__)
- // x86_64 supports 28 - 32 bits
- if (SetMmapRndBitsMin(32, 32, false) && SetMmapRndBitsMin(16, 16, true)) {
+ // 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))) {
return {};
}
#elif defined(__arm__) || defined(__i386__)
diff --git a/init/service.cpp b/init/service.cpp
index 01dd685..730b6b6 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -130,13 +130,13 @@
std::chrono::time_point<std::chrono::steady_clock> Service::exec_service_started_;
Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
- const std::vector<std::string>& args, bool from_apex)
- : Service(name, 0, 0, 0, {}, 0, "", subcontext_for_restart_commands, args, from_apex) {}
+ const std::string& filename, const std::vector<std::string>& args)
+ : Service(name, 0, 0, 0, {}, 0, "", subcontext_for_restart_commands, filename, args) {}
Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
const std::vector<gid_t>& supp_gids, int namespace_flags,
const std::string& seclabel, Subcontext* subcontext_for_restart_commands,
- const std::vector<std::string>& args, bool from_apex)
+ const std::string& filename, const std::vector<std::string>& args)
: name_(name),
classnames_({"default"}),
flags_(flags),
@@ -156,7 +156,7 @@
oom_score_adjust_(DEFAULT_OOM_SCORE_ADJUST),
start_order_(0),
args_(args),
- from_apex_(from_apex) {}
+ filename_(filename) {}
void Service::NotifyStateChange(const std::string& new_state) const {
if ((flags_ & SVC_TEMPORARY) != 0) {
@@ -315,7 +315,9 @@
#else
static bool is_apex_updatable = false;
#endif
- const bool is_process_updatable = !use_bootstrap_ns_ && is_apex_updatable;
+ const bool use_default_mount_ns =
+ mount_namespace_.has_value() && *mount_namespace_ == NS_DEFAULT;
+ const bool is_process_updatable = use_default_mount_ns && is_apex_updatable;
// If we crash > 4 times in 'fatal_crash_window_' minutes or before boot_completed,
// reboot into bootloader or set crashing property
@@ -474,10 +476,9 @@
}
// Enters namespaces, sets environment variables, writes PID files and runs the service executable.
-void Service::RunService(const std::optional<MountNamespace>& override_mount_namespace,
- const std::vector<Descriptor>& descriptors,
+void Service::RunService(const std::vector<Descriptor>& descriptors,
std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd) {
- if (auto result = EnterNamespaces(namespaces_, name_, override_mount_namespace); !result.ok()) {
+ if (auto result = EnterNamespaces(namespaces_, name_, mount_namespace_); !result.ok()) {
LOG(FATAL) << "Service '" << name_ << "' failed to set up namespaces: " << result.error();
}
@@ -581,26 +582,9 @@
scon = *result;
}
- // APEXd is always started in the "current" namespace because it is the process to set up
- // the current namespace.
- const bool is_apexd = args_[0] == "/system/bin/apexd";
-
- if (!IsDefaultMountNamespaceReady() && !is_apexd) {
- // If this service is started before APEXes and corresponding linker configuration
- // get available, mark it as pre-apexd one. Note that this marking is
- // permanent. So for example, if the service is re-launched (e.g., due
- // to crash), it is still recognized as pre-apexd... for consistency.
- use_bootstrap_ns_ = true;
- }
-
- // For pre-apexd services, override mount namespace as "bootstrap" one before starting.
- // Note: "ueventd" is supposed to be run in "default" mount namespace even if it's pre-apexd
- // to support loading firmwares from APEXes.
- std::optional<MountNamespace> override_mount_namespace;
- if (name_ == "ueventd") {
- override_mount_namespace = NS_DEFAULT;
- } else if (use_bootstrap_ns_) {
- override_mount_namespace = NS_BOOTSTRAP;
+ if (!mount_namespace_.has_value()) {
+ // remember from which mount namespace the service should start
+ SetMountNamespace();
}
post_data_ = ServiceList::GetInstance().IsPostData();
@@ -633,7 +617,7 @@
if (pid == 0) {
umask(077);
- RunService(override_mount_namespace, descriptors, std::move(pipefd));
+ RunService(descriptors, std::move(pipefd));
_exit(127);
}
@@ -684,6 +668,33 @@
return {};
}
+// Set mount namespace for the service.
+// The reason why remember the mount namespace:
+// If this service is started before APEXes and corresponding linker configuration
+// get available, mark it as pre-apexd one. Note that this marking is
+// permanent. So for example, if the service is re-launched (e.g., due
+// to crash), it is still recognized as pre-apexd... for consistency.
+void Service::SetMountNamespace() {
+ // APEXd is always started in the "current" namespace because it is the process to set up
+ // the current namespace. So, leave mount_namespace_ as empty.
+ if (args_[0] == "/system/bin/apexd") {
+ return;
+ }
+ // Services in the following list start in the "default" mount namespace.
+ // Note that they should use bootstrap bionic if they start before APEXes are ready.
+ static const std::set<std::string> kUseDefaultMountNamespace = {
+ "ueventd", // load firmwares from APEXes
+ "hwservicemanager", // load VINTF fragments from APEXes
+ "servicemanager", // load VINTF fragments from APEXes
+ };
+ if (kUseDefaultMountNamespace.find(name_) != kUseDefaultMountNamespace.end()) {
+ mount_namespace_ = NS_DEFAULT;
+ return;
+ }
+ // Use the "default" mount namespace only if it's ready
+ mount_namespace_ = IsDefaultMountNamespaceReady() ? NS_DEFAULT : NS_BOOTSTRAP;
+}
+
void Service::SetStartedInFirstStage(pid_t pid) {
LOG(INFO) << "adding first-stage service '" << name_ << "'...";
@@ -849,7 +860,7 @@
}
return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, namespace_flags, seclabel,
- nullptr, str_args, false);
+ nullptr, /*filename=*/"", str_args);
}
// This is used for snapuserd_proxy, which hands off a socket to snapuserd. It's
diff --git a/init/service.h b/init/service.h
index d233cbf..f7f32d9 100644
--- a/init/service.h
+++ b/init/service.h
@@ -32,6 +32,7 @@
#include "action.h"
#include "capabilities.h"
#include "keyword_map.h"
+#include "mount_namespace.h"
#include "parser.h"
#include "service_utils.h"
#include "subcontext.h"
@@ -65,12 +66,12 @@
public:
Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
- const std::vector<std::string>& args, bool from_apex = false);
+ const std::string& filename, const std::vector<std::string>& args);
Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
const std::vector<gid_t>& supp_gids, int namespace_flags, const std::string& seclabel,
- Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args,
- bool from_apex = false);
+ Subcontext* subcontext_for_restart_commands, const std::string& filename,
+ const std::vector<std::string>& args);
static Result<std::unique_ptr<Service>> MakeTemporaryOneshotService(
const std::vector<std::string>& args);
@@ -132,7 +133,7 @@
const std::vector<std::string>& args() const { return args_; }
bool is_updatable() const { return updatable_; }
bool is_post_data() const { return post_data_; }
- bool is_from_apex() const { return from_apex_; }
+ bool is_from_apex() const { return base::StartsWith(filename_, "/apex/"); }
void set_oneshot(bool value) {
if (value) {
flags_ |= SVC_ONESHOT;
@@ -151,10 +152,9 @@
Result<void> CheckConsole();
void ConfigureMemcg();
void RunService(
- const std::optional<MountNamespace>& override_mount_namespace,
const std::vector<Descriptor>& descriptors,
std::unique_ptr<std::array<int, 2>, void (*)(const std::array<int, 2>* pipe)> pipefd);
-
+ void SetMountNamespace();
static unsigned long next_start_order_;
static bool is_exec_service_running_;
static std::chrono::time_point<std::chrono::steady_clock> exec_service_started_;
@@ -219,13 +219,13 @@
std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
- bool use_bootstrap_ns_ = false;
+ std::optional<MountNamespace> mount_namespace_;
bool post_data_ = false;
std::optional<std::string> on_failure_reboot_target_;
- bool from_apex_ = false;
+ std::string filename_;
};
} // namespace init
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 9e914ee..32c57c4 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -647,7 +647,7 @@
}
}
- service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args, from_apex_);
+ service_ = std::make_unique<Service>(name, restart_action_subcontext, filename, str_args);
return {};
}
diff --git a/init/service_parser.h b/init/service_parser.h
index 0fd2da5..54503dd 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -31,13 +31,11 @@
public:
ServiceParser(
ServiceList* service_list, Subcontext* subcontext,
- const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy,
- bool from_apex = false)
+ const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy)
: service_list_(service_list),
subcontext_(subcontext),
interface_inheritance_hierarchy_(interface_inheritance_hierarchy),
- service_(nullptr),
- from_apex_(from_apex) {}
+ service_(nullptr) {}
Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line) override;
Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
@@ -92,7 +90,6 @@
std::optional<InterfaceInheritanceHierarchyMap> interface_inheritance_hierarchy_;
std::unique_ptr<Service> service_;
std::string filename_;
- bool from_apex_ = false;
};
} // namespace init
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 22ee844..87a2ce5 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -39,7 +39,7 @@
std::vector<std::string> dummy_args{"/bin/test"};
Service* service_in_old_memory =
- new (old_memory) Service("test_old_memory", nullptr, dummy_args);
+ new (old_memory) Service("test_old_memory", nullptr, /*filename=*/"", dummy_args);
EXPECT_EQ(0U, service_in_old_memory->flags());
EXPECT_EQ(0, service_in_old_memory->pid());
@@ -58,7 +58,8 @@
}
Service* service_in_old_memory2 = new (old_memory) Service(
- "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), 0U, "", nullptr, dummy_args);
+ "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), 0U, "",
+ nullptr, /*filename=*/"", dummy_args);
EXPECT_EQ(0U, service_in_old_memory2->flags());
EXPECT_EQ(0, service_in_old_memory2->pid());
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index bb3967e..961e006 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -251,11 +251,8 @@
}
bool Subcontext::PathMatchesSubcontext(const std::string& path) const {
- static const std::string kApexDir = "/apex/";
- if (StartsWith(path, kApexDir)) {
- auto begin = kApexDir.size();
- auto end = path.find('/', begin);
- auto apex_name = path.substr(begin, end - begin);
+ auto apex_name = GetApexNameFromFileName(path);
+ if (!apex_name.empty()) {
return std::find(apex_list_.begin(), apex_list_.end(), apex_name) != apex_list_.end();
}
for (const auto& prefix : path_prefixes_) {
@@ -381,6 +378,9 @@
}
void SubcontextTerminate() {
+ if (!subcontext) {
+ return;
+ }
subcontext_terminated_by_shutdown = true;
kill(subcontext->pid(), SIGTERM);
}
diff --git a/init/util.cpp b/init/util.cpp
index 1801d17..bfc3fb6 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -733,5 +733,20 @@
return is_microdroid;
}
+bool Has32BitAbi() {
+ static bool has = !android::base::GetProperty("ro.product.cpu.abilist32", "").empty();
+ return has;
+}
+
+std::string GetApexNameFromFileName(const std::string& path) {
+ static const std::string kApexDir = "/apex/";
+ if (StartsWith(path, kApexDir)) {
+ auto begin = kApexDir.size();
+ auto end = path.find('/', begin);
+ return path.substr(begin, end - begin);
+ }
+ return "";
+}
+
} // namespace init
} // namespace android
diff --git a/init/util.h b/init/util.h
index 47d4ff5..daec470 100644
--- a/init/util.h
+++ b/init/util.h
@@ -106,5 +106,8 @@
void SetDefaultMountNamespaceReady();
bool IsMicrodroid();
+bool Has32BitAbi();
+
+std::string GetApexNameFromFileName(const std::string& path);
} // namespace init
} // namespace android
diff --git a/libcutils/TEST_MAPPING b/libcutils/TEST_MAPPING
index cca7d93..6477502 100644
--- a/libcutils/TEST_MAPPING
+++ b/libcutils/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "libcutils_test"
}
],
- "hwasan-postsubmit": [
+ "hwasan-presubmit": [
{
"name": "libcutils_test"
}
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 3054d2b..b2ace34 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -440,12 +440,11 @@
}
// Another option to load kernel modules. load in independent modules in parallel
-// and then load modules which only have soft dependency, third update dependency list of other
-// remaining modules, repeat these steps until all modules are loaded.
+// and then update dependency list of other remaining modules, repeat these steps
+// until all modules are loaded.
bool Modprobe::LoadModulesParallel(int num_threads) {
bool ret = true;
std::map<std::string, std::set<std::string>> mod_with_deps;
- std::map<std::string, std::set<std::string>> mod_with_softdeps;
// Get dependencies
for (const auto& module : module_load_) {
@@ -458,26 +457,33 @@
// Get soft dependencies
for (const auto& [it_mod, it_softdep] : module_pre_softdep_) {
- mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_softdep);
+ if (mod_with_deps.find(MakeCanonical(it_softdep)) != mod_with_deps.end()) {
+ mod_with_deps[MakeCanonical(it_mod)].emplace(
+ GetDependencies(MakeCanonical(it_softdep))[0]);
+ }
}
// Get soft post dependencies
for (const auto& [it_mod, it_softdep] : module_post_softdep_) {
- mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_softdep);
+ if (mod_with_deps.find(MakeCanonical(it_softdep)) != mod_with_deps.end()) {
+ mod_with_deps[MakeCanonical(it_softdep)].emplace(
+ GetDependencies(MakeCanonical(it_mod))[0]);
+ }
}
while (!mod_with_deps.empty()) {
std::vector<std::thread> threads;
std::vector<std::string> mods_path_to_load;
- std::vector<std::string> mods_with_softdep_to_load;
std::mutex vector_lock;
- // Find independent modules and modules only having soft dependencies
+ // Find independent modules
for (const auto& [it_mod, it_dep] : mod_with_deps) {
- if (it_dep.size() == 1 && mod_with_softdeps[it_mod].empty()) {
- mods_path_to_load.emplace_back(*(it_dep.begin()));
- } else if (it_dep.size() == 1) {
- mods_with_softdep_to_load.emplace_back(it_mod);
+ if (it_dep.size() == 1) {
+ if (module_options_[it_mod].find("load_sequential=1") != std::string::npos) {
+ LoadWithAliases(it_mod, true);
+ } else {
+ mods_path_to_load.emplace_back(*(it_dep.begin()));
+ }
}
}
@@ -502,21 +508,10 @@
thread.join();
}
- // Since we cannot assure if these soft dependencies tree are overlap,
- // we loaded these modules one by one.
- for (auto dep = mods_with_softdep_to_load.rbegin(); dep != mods_with_softdep_to_load.rend();
- dep++) {
- ret &= LoadWithAliases(*dep, true);
- }
-
std::lock_guard guard(module_loaded_lock_);
// Remove loaded module form mod_with_deps and soft dependencies of other modules
for (const auto& module_loaded : module_loaded_) {
mod_with_deps.erase(module_loaded);
-
- for (auto& [mod, softdeps] : mod_with_softdeps) {
- softdeps.erase(module_loaded);
- }
}
// Remove loaded module form dependencies of other modules which are not loaded yet
diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c
index 11c116a..ad6898c 100644
--- a/libnetutils/dhcpclient.c
+++ b/libnetutils/dhcpclient.c
@@ -45,7 +45,7 @@
typedef unsigned long long msecs_t;
#if VERBOSE
-void dump_dhcp_msg();
+void dump_dhcp_msg(dhcp_msg *msg, int len);
#endif
msecs_t get_msecs(void)
diff --git a/libpackagelistparser/TEST_MAPPING b/libpackagelistparser/TEST_MAPPING
index d69a7fb..f4a7761 100644
--- a/libpackagelistparser/TEST_MAPPING
+++ b/libpackagelistparser/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "libpackagelistparser_test"
}
],
- "hwasan-postsubmit": [
+ "hwasan-presubmit": [
{
"name": "libpackagelistparser_test"
}
diff --git a/libstats/pull_rust/Android.bp b/libstats/pull_rust/Android.bp
index 4ffa98d..a3ef131 100644
--- a/libstats/pull_rust/Android.bp
+++ b/libstats/pull_rust/Android.bp
@@ -22,6 +22,10 @@
name: "libstatspull_bindgen",
wrapper_src: "statslog.h",
crate_name: "statspull_bindgen",
+ visibility: [
+ "//frameworks/proto_logging/stats/stats_log_api_gen",
+ "//packages/modules/Virtualization/libs/statslog_virtualization",
+ ],
source_stem: "bindings",
bindgen_flags: [
"--size_t-is-usize",
@@ -48,7 +52,7 @@
apex_available: [
"//apex_available:platform",
"com.android.virt",
- ]
+ ],
}
rust_library {
diff --git a/libstats/push_compat/statsd_writer.c b/libstats/push_compat/statsd_writer.c
index 04d3b46..4818d11 100644
--- a/libstats/push_compat/statsd_writer.c
+++ b/libstats/push_compat/statsd_writer.c
@@ -61,7 +61,7 @@
static int statsdOpen();
static void statsdClose();
static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
-static void statsdNoteDrop();
+static void statsdNoteDrop(int error, int tag);
struct android_log_transport_write statsdLoggerWrite = {
.name = "statsd",
diff --git a/libsync/libsync.map.txt b/libsync/libsync.map.txt
index aac6b57..32df91e 100644
--- a/libsync/libsync.map.txt
+++ b/libsync/libsync.map.txt
@@ -19,7 +19,7 @@
sync_merge; # introduced=26
sync_file_info; # introduced=26
sync_file_info_free; # introduced=26
- sync_wait; # llndk apex
+ sync_wait; # llndk systemapi
sync_fence_info; # llndk
sync_pt_info; # llndk
sync_fence_info_free; # llndk
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 4ddac3d..ed5b2a9 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -149,6 +149,29 @@
// Same for weak counts.
#define BAD_WEAK(c) ((c) == 0 || ((c) & (~MAX_COUNT)) != 0)
+// name kept because prebuilts used to use it from inlining sp<> code
+void sp_report_stack_pointer() { LOG_ALWAYS_FATAL("RefBase used with stack pointer argument"); }
+
+// Check whether address is definitely on the calling stack. We actually check whether it is on
+// the same 4K page as the frame pointer.
+//
+// Assumptions:
+// - Pages are never smaller than 4K (MIN_PAGE_SIZE)
+// - Malloced memory never shares a page with a stack.
+//
+// It does not appear safe to broaden this check to include adjacent pages; apparently this code
+// is used in environments where there may not be a guard page below (at higher addresses than)
+// the bottom of the stack.
+static void check_not_on_stack(const void* ptr) {
+ static constexpr int MIN_PAGE_SIZE = 0x1000; // 4K. Safer than including sys/user.h.
+ static constexpr uintptr_t MIN_PAGE_MASK = ~static_cast<uintptr_t>(MIN_PAGE_SIZE - 1);
+ uintptr_t my_frame_address =
+ reinterpret_cast<uintptr_t>(__builtin_frame_address(0 /* this frame */));
+ if (((reinterpret_cast<uintptr_t>(ptr) ^ my_frame_address) & MIN_PAGE_MASK) == 0) {
+ sp_report_stack_pointer();
+ }
+}
+
// ---------------------------------------------------------------------------
class RefBase::weakref_impl : public RefBase::weakref_type
@@ -432,6 +455,8 @@
return;
}
+ check_not_on_stack(this);
+
int32_t old __unused = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE, std::memory_order_relaxed);
// A decStrong() must still happen after us.
ALOG_ASSERT(old > INITIAL_STRONG_VALUE, "0x%x too small", old);
@@ -744,21 +769,27 @@
if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) {
delete mRefs;
}
- } else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
- // We never acquired a strong reference on this object.
+ } else {
+ int32_t strongs = mRefs->mStrong.load(std::memory_order_relaxed);
- // TODO: make this fatal, but too much code in Android manages RefBase with
- // new/delete manually (or using other mechanisms such as std::make_unique).
- // However, this is dangerous because it's also common for code to use the
- // sp<T>(T*) constructor, assuming that if the object is around, it is already
- // owned by an sp<>.
- ALOGW("RefBase: Explicit destruction, weak count = %d (in %p). Use sp<> to manage this "
- "object.",
- mRefs->mWeak.load(), this);
+ if (strongs == INITIAL_STRONG_VALUE) {
+ // We never acquired a strong reference on this object.
+
+ // It would be nice to make this fatal, but many places use RefBase on the stack.
+ // However, this is dangerous because it's also common for code to use the
+ // sp<T>(T*) constructor, assuming that if the object is around, it is already
+ // owned by an sp<>.
+ ALOGW("RefBase: Explicit destruction, weak count = %d (in %p). Use sp<> to manage this "
+ "object.",
+ mRefs->mWeak.load(), this);
#if CALLSTACK_ENABLED
- CallStack::logStack(LOG_TAG);
+ CallStack::logStack(LOG_TAG);
#endif
+ } else if (strongs != 0) {
+ LOG_ALWAYS_FATAL("RefBase: object %p with strong count %d deleted. Double owned?", this,
+ strongs);
+ }
}
// For debugging purposes, clear mRefs. Ineffective against outstanding wp's.
const_cast<weakref_impl*&>(mRefs) = nullptr;
@@ -766,6 +797,8 @@
void RefBase::extendObjectLifetime(int32_t mode)
{
+ check_not_on_stack(this);
+
// Must be happens-before ordered with respect to construction or any
// operation that could destroy the object.
mRefs->mFlags.fetch_or(mode, std::memory_order_relaxed);
diff --git a/libutils/RefBase_test.cpp b/libutils/RefBase_test.cpp
index 93f9654..aed3b9b 100644
--- a/libutils/RefBase_test.cpp
+++ b/libutils/RefBase_test.cpp
@@ -265,6 +265,21 @@
delete foo;
}
+TEST(RefBase, DoubleOwnershipDeath) {
+ bool isDeleted;
+ auto foo = sp<Foo>::make(&isDeleted);
+
+ // if something else thinks it owns foo, should die
+ EXPECT_DEATH(delete foo.get(), "");
+
+ EXPECT_FALSE(isDeleted);
+}
+
+TEST(RefBase, StackOwnershipDeath) {
+ bool isDeleted;
+ EXPECT_DEATH({ Foo foo(&isDeleted); foo.incStrong(nullptr); }, "");
+}
+
// Set up a situation in which we race with visit2AndRremove() to delete
// 2 strong references. Bar destructor checks that there are no early
// deletions and prior updates are visible to destructor.
diff --git a/libutils/StrongPointer.cpp b/libutils/StrongPointer.cpp
index ef46723..ba52502 100644
--- a/libutils/StrongPointer.cpp
+++ b/libutils/StrongPointer.cpp
@@ -21,7 +21,4 @@
namespace android {
void sp_report_race() { LOG_ALWAYS_FATAL("sp<> assignment detected data race"); }
-
-void sp_report_stack_pointer() { LOG_ALWAYS_FATAL("sp<> constructed with stack pointer argument"); }
-
}
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index e07f574..f3acd6f 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -779,6 +779,40 @@
} // namespace android
+namespace libutilsinternal {
+template <typename T, typename = void>
+struct is_complete_type : std::false_type {};
+
+template <typename T>
+struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
+} // namespace libutilsinternal
+
+namespace std {
+
+// Define `RefBase` specific versions of `std::make_shared` and
+// `std::make_unique` to block people from using them. Using them to allocate
+// `RefBase` objects results in double ownership. Use
+// `sp<T>::make(...)` instead.
+//
+// Note: We exclude incomplete types because `std::is_base_of` is undefined in
+// that case.
+
+template <typename T, typename... Args,
+ typename std::enable_if<libutilsinternal::is_complete_type<T>::value, bool>::value = true,
+ typename std::enable_if<std::is_base_of<android::RefBase, T>::value, bool>::value = true>
+shared_ptr<T> make_shared(Args...) { // SEE COMMENT ABOVE.
+ static_assert(!std::is_base_of<android::RefBase, T>::value, "Must use RefBase with sp<>");
+}
+
+template <typename T, typename... Args,
+ typename std::enable_if<libutilsinternal::is_complete_type<T>::value, bool>::value = true,
+ typename std::enable_if<std::is_base_of<android::RefBase, T>::value, bool>::value = true>
+unique_ptr<T> make_unique(Args...) { // SEE COMMENT ABOVE.
+ static_assert(!std::is_base_of<android::RefBase, T>::value, "Must use RefBase with sp<>");
+}
+
+} // namespace std
+
// ---------------------------------------------------------------------------
#endif // ANDROID_REF_BASE_H
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index bb1941b..54aa691 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -120,7 +120,6 @@
template<typename Y> friend class sp;
template<typename Y> friend class wp;
void set_pointer(T* ptr);
- static inline void check_not_on_stack(const void* ptr);
T* m_ptr;
};
@@ -185,32 +184,10 @@
// For code size reasons, we do not want these inlined or templated.
void sp_report_race();
-void sp_report_stack_pointer();
// ---------------------------------------------------------------------------
// No user serviceable parts below here.
-// Check whether address is definitely on the calling stack. We actually check whether it is on
-// the same 4K page as the frame pointer.
-//
-// Assumptions:
-// - Pages are never smaller than 4K (MIN_PAGE_SIZE)
-// - Malloced memory never shares a page with a stack.
-//
-// It does not appear safe to broaden this check to include adjacent pages; apparently this code
-// is used in environments where there may not be a guard page below (at higher addresses than)
-// the bottom of the stack.
-template <typename T>
-void sp<T>::check_not_on_stack(const void* ptr) {
- static constexpr int MIN_PAGE_SIZE = 0x1000; // 4K. Safer than including sys/user.h.
- static constexpr uintptr_t MIN_PAGE_MASK = ~static_cast<uintptr_t>(MIN_PAGE_SIZE - 1);
- uintptr_t my_frame_address =
- reinterpret_cast<uintptr_t>(__builtin_frame_address(0 /* this frame */));
- if (((reinterpret_cast<uintptr_t>(ptr) ^ my_frame_address) & MIN_PAGE_MASK) == 0) {
- sp_report_stack_pointer();
- }
-}
-
// TODO: Ideally we should find a way to increment the reference count before running the
// constructor, so that generating an sp<> to this in the constructor is no longer dangerous.
template <typename T>
@@ -219,14 +196,13 @@
T* t = new T(std::forward<Args>(args)...);
sp<T> result;
result.m_ptr = t;
- t->incStrong(t); // bypass check_not_on_stack for heap allocation
+ t->incStrong(t);
return result;
}
template <typename T>
sp<T> sp<T>::fromExisting(T* other) {
if (other) {
- check_not_on_stack(other);
other->incStrongRequireStrong(other);
sp<T> result;
result.m_ptr = other;
@@ -240,7 +216,6 @@
sp<T>::sp(T* other)
: m_ptr(other) {
if (other) {
- check_not_on_stack(other);
other->incStrong(this);
}
}
@@ -249,7 +224,6 @@
template <typename U>
sp<T>::sp(U* other) : m_ptr(other) {
if (other) {
- check_not_on_stack(other);
(static_cast<T*>(other))->incStrong(this);
}
}
@@ -258,7 +232,6 @@
sp<T>& sp<T>::operator=(T* other) {
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
if (other) {
- check_not_on_stack(other);
other->incStrong(this);
}
if (oldPtr) oldPtr->decStrong(this);
diff --git a/libvndksupport/libvndksupport.map.txt b/libvndksupport/libvndksupport.map.txt
index a44ed18..1d94b9d 100644
--- a/libvndksupport/libvndksupport.map.txt
+++ b/libvndksupport/libvndksupport.map.txt
@@ -1,8 +1,8 @@
LIBVNDKSUPPORT {
global:
- android_is_in_vendor_process; # llndk apex
- android_load_sphal_library; # llndk apex
- android_unload_sphal_library; # llndk apex
+ android_is_in_vendor_process; # llndk systemapi
+ android_load_sphal_library; # llndk systemapi
+ android_unload_sphal_library; # llndk systemapi
local:
*;
};
diff --git a/mkbootfs/mkbootfs.c b/mkbootfs/mkbootfs.c
index 58153f3..05d1940 100644
--- a/mkbootfs/mkbootfs.c
+++ b/mkbootfs/mkbootfs.c
@@ -24,8 +24,7 @@
** - device notes, pipes, etc are not supported (error)
*/
-void die(const char *why, ...)
-{
+static void die(const char* why, ...) {
va_list ap;
va_start(ap, why);
@@ -42,7 +41,7 @@
};
static struct fs_config_entry* canned_config = NULL;
-static char *target_out_path = NULL;
+static const char* target_out_path = NULL;
/* Each line in the canned file should be a path plus three ints (uid,
* gid, mode). */
@@ -273,8 +272,7 @@
}
}
-void archive(const char *start, const char *prefix)
-{
+static void archive(const char* start, const char* prefix) {
char in[8192];
char out[8192];
@@ -294,7 +292,7 @@
char line[CANNED_LINE_LENGTH];
FILE* f = fopen(filename, "r");
- if (f == NULL) die("failed to open canned file");
+ if (f == NULL) die("failed to open canned file '%s'", filename);
while (fgets(line, CANNED_LINE_LENGTH, f) != NULL) {
if (!line[0]) break;
@@ -332,6 +330,13 @@
int main(int argc, char *argv[])
{
+ if (argc == 1) {
+ fprintf(stderr,
+ "usage: %s [-d TARGET_OUTPUT_PATH] [-f CANNED_CONFIGURATION_PATH] DIRECTORIES...\n",
+ argv[0]);
+ exit(1);
+ }
+
argc--;
argv++;
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 29b8365..660f18c 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -90,32 +90,6 @@
# checker programs.
mkdir /dev/fscklogs 0770 root system
-# Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
-on early-init && property:ro.product.cpu.abilist32=*
- exec_start boringssl_self_test32
-on early-init && property:ro.product.cpu.abilist64=*
- exec_start boringssl_self_test64
-on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
- exec_start boringssl_self_test_apex32
-on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
- exec_start boringssl_self_test_apex64
-
-service boringssl_self_test32 /system/bin/boringssl_self_test32
- reboot_on_failure reboot,boringssl-self-check-failed
- stdio_to_kmsg
-
-service boringssl_self_test64 /system/bin/boringssl_self_test64
- reboot_on_failure reboot,boringssl-self-check-failed
- stdio_to_kmsg
-
-service boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32
- reboot_on_failure reboot,boringssl-self-check-failed
- stdio_to_kmsg
-
-service boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64
- reboot_on_failure reboot,boringssl-self-check-failed
- stdio_to_kmsg
-
on init
sysclktz 0
@@ -502,6 +476,33 @@
start hwservicemanager
start vndservicemanager
+# Run boringssl self test for each ABI. Any failures trigger reboot to firmware.
+on init && property:ro.product.cpu.abilist32=*
+ exec_start boringssl_self_test32
+on init && property:ro.product.cpu.abilist64=*
+ exec_start boringssl_self_test64
+on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
+ exec_start boringssl_self_test_apex32
+on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
+ exec_start boringssl_self_test_apex64
+
+service boringssl_self_test32 /system/bin/boringssl_self_test32
+ reboot_on_failure reboot,boringssl-self-check-failed
+ stdio_to_kmsg
+
+service boringssl_self_test64 /system/bin/boringssl_self_test64
+ reboot_on_failure reboot,boringssl-self-check-failed
+ stdio_to_kmsg
+
+service boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32
+ reboot_on_failure reboot,boringssl-self-check-failed
+ stdio_to_kmsg
+
+service boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64
+ reboot_on_failure reboot,boringssl-self-check-failed
+ stdio_to_kmsg
+
+
# Healthd can trigger a full boot from charger mode by signaling this
# property when the power button is held.
on property:sys.boot_from_charger_mode=1
@@ -786,7 +787,6 @@
mkdir /data/misc/carrierid 0770 system radio
mkdir /data/misc/apns 0770 system radio
mkdir /data/misc/emergencynumberdb 0770 system radio
- mkdir /data/misc/zoneinfo 0775 system system
mkdir /data/misc/network_watchlist 0774 system system
mkdir /data/misc/textclassifier 0771 system system
mkdir /data/misc/vpn 0770 system vpn
@@ -1021,10 +1021,6 @@
# completed and apexd.status becomes "ready".
exec_start apexd-snapshotde
- # Check any timezone data in /data is newer than the copy in the time zone data
- # module, delete if not.
- exec - system system -- /system/bin/tzdatacheck /apex/com.android.tzdata/etc/tz /data/misc/zoneinfo
-
# sys.memfd_use set to false by default, which keeps it disabled
# until it is confirmed that apps and vendor processes don't make
# IOCTLs on ashmem fds any more.
diff --git a/trusty/OWNERS b/trusty/OWNERS
index 5c4e03a..61b97c6 100644
--- a/trusty/OWNERS
+++ b/trusty/OWNERS
@@ -1,9 +1,11 @@
armellel@google.com
arve@android.com
+danielangell@google.com
gmar@google.com
marcone@google.com
mmaurer@google.com
ncbray@google.com
swillden@google.com
+thurston@google.com
trong@google.com
wenhaowang@google.com
diff --git a/trusty/metrics/metrics_test.cpp b/trusty/metrics/metrics_test.cpp
index 9897950..0c6db7f 100644
--- a/trusty/metrics/metrics_test.cpp
+++ b/trusty/metrics/metrics_test.cpp
@@ -61,6 +61,18 @@
virtual void SetUp() override {
auto ret = Open();
ASSERT_TRUE(ret.ok()) << ret.error();
+
+ /* Drain events (if any) and reset state */
+ DrainEvents();
+ crashed_app_.clear();
+ event_drop_count_ = 0;
+ }
+
+ void DrainEvents() {
+ while (WaitForEvent(1000 /* 1 second timeout */).ok()) {
+ auto ret = HandleEvent();
+ ASSERT_TRUE(ret.ok()) << ret.error();
+ }
}
void WaitForAndHandleEvent() {
@@ -79,6 +91,9 @@
TriggerCrash();
WaitForAndHandleEvent();
+ /* Check that no event was dropped. */
+ ASSERT_EQ(event_drop_count_, 0);
+
/* Check that correct TA crashed. */
ASSERT_EQ(crashed_app_, "36f5b435-5bd3-4526-8b76-200e3a7e79f3:crasher");
}
@@ -110,6 +125,9 @@
auto ret = HandleEvent();
ASSERT_TRUE(ret.ok()) << ret.error();
+ /* Check that no event was dropped. */
+ ASSERT_EQ(event_drop_count_, 0);
+
/* Check that correct TA crashed. */
ASSERT_EQ(crashed_app_, "36f5b435-5bd3-4526-8b76-200e3a7e79f3:crasher");
}
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index f015892..b970406 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -70,49 +70,6 @@
exit(code);
}
-static int drop_privs(void) {
- struct __user_cap_header_struct capheader;
- struct __user_cap_data_struct capdata[2];
-
- if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
- return -1;
- }
-
- /*
- * ensure we're running as the system user
- */
- if (setgid(AID_SYSTEM) != 0) {
- return -1;
- }
-
- if (setuid(AID_SYSTEM) != 0) {
- return -1;
- }
-
- /*
- * drop all capabilities except SYS_RAWIO
- */
- memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
- capheader.version = _LINUX_CAPABILITY_VERSION_3;
- capheader.pid = 0;
-
- capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].permitted = CAP_TO_MASK(CAP_SYS_RAWIO);
- capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].effective = CAP_TO_MASK(CAP_SYS_RAWIO);
-
- if (capset(&capheader, &capdata[0]) < 0) {
- return -1;
- }
-
- /*
- * No access for group and other. We need execute access for user to create
- * an accessible directory.
- */
- umask(S_IRWXG | S_IRWXO);
-
- return 0;
-}
-
static int handle_req(struct storage_msg* msg, const void* req, size_t req_len) {
int rc;
@@ -261,8 +218,11 @@
int main(int argc, char* argv[]) {
int rc;
- /* drop privileges */
- if (drop_privs() < 0) return EXIT_FAILURE;
+ /*
+ * No access for group and other. We need execute access for user to create
+ * an accessible directory.
+ */
+ umask(S_IRWXG | S_IRWXO);
/* parse arguments */
parse_args(argc, argv);
diff --git a/trusty/test/binder/aidl/ByteEnum.aidl b/trusty/test/binder/aidl/ByteEnum.aidl
new file mode 100644
index 0000000..d3a13ac
--- /dev/null
+++ b/trusty/test/binder/aidl/ByteEnum.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Hello, world!
+ */
+@Backing(type="byte")
+enum ByteEnum {
+ // Comment about FOO.
+ FOO = 1,
+ BAR = 2,
+ BAZ,
+}
diff --git a/trusty/test/binder/aidl/ITestService.aidl b/trusty/test/binder/aidl/ITestService.aidl
new file mode 100644
index 0000000..c6a99c8
--- /dev/null
+++ b/trusty/test/binder/aidl/ITestService.aidl
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import ByteEnum;
+import IntEnum;
+import LongEnum;
+
+interface ITestService {
+ const @utf8InCpp String PORT = "com.android.trusty.binder.test.service";
+
+ const int TEST_CONSTANT = 42;
+ const int TEST_CONSTANT2 = -42;
+ const int TEST_CONSTANT3 = +42;
+ const int TEST_CONSTANT4 = +4;
+ const int TEST_CONSTANT5 = -4;
+ const int TEST_CONSTANT6 = -0;
+ const int TEST_CONSTANT7 = +0;
+ const int TEST_CONSTANT8 = 0;
+ const int TEST_CONSTANT9 = 0x56;
+ const int TEST_CONSTANT10 = 0xa5;
+ const int TEST_CONSTANT11 = 0xFA;
+ const int TEST_CONSTANT12 = 0xffffffff;
+
+ const byte BYTE_TEST_CONSTANT = 17;
+ const long LONG_TEST_CONSTANT = 1L << 40;
+
+ const String STRING_TEST_CONSTANT = "foo";
+ const String STRING_TEST_CONSTANT2 = "bar";
+
+ // Test that primitives work as parameters and return types.
+ boolean RepeatBoolean(boolean token);
+ byte RepeatByte(byte token);
+ char RepeatChar(char token);
+ int RepeatInt(int token);
+ long RepeatLong(long token);
+ float RepeatFloat(float token);
+ double RepeatDouble(double token);
+ String RepeatString(String token);
+ ByteEnum RepeatByteEnum(ByteEnum token);
+ IntEnum RepeatIntEnum(IntEnum token);
+ LongEnum RepeatLongEnum(LongEnum token);
+
+ // Test that arrays work as parameters and return types.
+ boolean[] ReverseBoolean(in boolean[] input, out boolean[] repeated);
+ byte[] ReverseByte(in byte[] input, out byte[] repeated);
+ char[] ReverseChar(in char[] input, out char[] repeated);
+ int[] ReverseInt(in int[] input, out int[] repeated);
+ long[] ReverseLong(in long[] input, out long[] repeated);
+ float[] ReverseFloat(in float[] input, out float[] repeated);
+ double[] ReverseDouble(in double[] input, out double[] repeated);
+ String[] ReverseString(in String[] input, out String[] repeated);
+ ByteEnum[] ReverseByteEnum(in ByteEnum[] input, out ByteEnum[] repeated);
+ IntEnum[] ReverseIntEnum(in IntEnum[] input, out IntEnum[] repeated);
+ LongEnum[] ReverseLongEnum(in LongEnum[] input, out LongEnum[] repeated);
+}
diff --git a/trusty/test/binder/aidl/IntEnum.aidl b/trusty/test/binder/aidl/IntEnum.aidl
new file mode 100644
index 0000000..120e44f
--- /dev/null
+++ b/trusty/test/binder/aidl/IntEnum.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@JavaDerive(toString=true)
+@Backing(type="int")
+enum IntEnum {
+ FOO = 1000,
+ BAR = 2000,
+ BAZ,
+ /** @deprecated do not use this */
+ QUX,
+}
diff --git a/trusty/test/binder/aidl/LongEnum.aidl b/trusty/test/binder/aidl/LongEnum.aidl
new file mode 100644
index 0000000..0e9e933
--- /dev/null
+++ b/trusty/test/binder/aidl/LongEnum.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@Backing(type="long")
+enum LongEnum {
+ FOO = 100000000000,
+ BAR = 200000000000,
+ BAZ,
+}
diff --git a/trusty/test/binder/aidl/rules.mk b/trusty/test/binder/aidl/rules.mk
new file mode 100644
index 0000000..6154abb
--- /dev/null
+++ b/trusty/test/binder/aidl/rules.mk
@@ -0,0 +1,26 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_AIDLS := \
+ $(LOCAL_DIR)/ByteEnum.aidl \
+ $(LOCAL_DIR)/IntEnum.aidl \
+ $(LOCAL_DIR)/ITestService.aidl \
+ $(LOCAL_DIR)/LongEnum.aidl \
+
+include make/aidl.mk