Merge "Snap for 8952093 from 3de2320e5e2618a6ad3385883cf8d0afbe72b2c6 to sdk-release" into sdk-release
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..49ebdbf 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",
@@ -342,9 +346,7 @@
target: {
not_windows: {
required: [
- "e2fsdroid",
"mke2fs.conf",
- "sload_f2fs",
],
},
windows: {
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 10bed6d..cde0cb2 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -19,9 +19,7 @@
#
my_dist_files := $(HOST_OUT_EXECUTABLES)/mke2fs
-my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid
my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs_casefold
-my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs
$(call dist-for-goals,dist_files sdk,$(my_dist_files))
my_dist_files :=
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/device/commands.cpp b/fastboot/device/commands.cpp
index 524196c..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;
@@ -317,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");
@@ -329,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");
}
@@ -358,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];
@@ -682,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 0de00b1..b6eb2cd 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -29,7 +29,7 @@
#include <fs_mgr.h>
#include <liblp/liblp.h>
-#include "constants.h"
+#include "BootControlClient.h"
#include "fastboot_device.h"
#include "flashing.h"
#include "utility.h"
@@ -40,13 +40,10 @@
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;
@@ -211,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;
}
@@ -222,7 +219,7 @@
*message = "Missing argument";
return false;
}
- Slot slot;
+ int32_t slot = -1;
if (!GetSlotNumber(args[0], &slot)) {
*message = "Invalid slot";
return false;
@@ -232,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";
@@ -246,7 +243,7 @@
*message = "Missing argument";
return false;
}
- Slot slot;
+ int32_t slot = -1;
if (!GetSlotNumber(args[0], &slot)) {
*message = "Invalid slot";
return false;
@@ -256,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";
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 c0e3161..1f54f5b 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -889,7 +889,7 @@
// attempted_idx: On return, will indicate which fstab entry
// succeeded. In case of failure, it will be the start_idx.
// Sets errno to match the 1st mount failure on failure.
-static bool mount_with_alternatives(const Fstab& fstab, int start_idx, int* end_idx,
+static bool mount_with_alternatives(Fstab& fstab, int start_idx, int* end_idx,
int* attempted_idx) {
unsigned long i;
int mount_errno = 0;
@@ -909,6 +909,14 @@
continue;
}
+ // fstab[start_idx].blk_device is already updated to /dev/dm-<N> by
+ // AVB related functions. Copy it from start_idx to the current index i.
+ if ((i != start_idx) && fstab[i].fs_mgr_flags.logical &&
+ fstab[start_idx].fs_mgr_flags.logical &&
+ (fstab[i].logical_partition_name == fstab[start_idx].logical_partition_name)) {
+ fstab[i].blk_device = fstab[start_idx].blk_device;
+ }
+
int fs_stat = prepare_fs_for_mount(fstab[i].blk_device, fstab[i]);
if (fs_stat & FS_STAT_INVALID_MAGIC) {
LERROR << __FUNCTION__
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 08200f6..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,35 +482,6 @@
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;
- }
- return false;
-}
-
template <typename Pred>
std::vector<FstabEntry*> GetEntriesByPred(Fstab* fstab, const Pred& pred) {
if (fstab == nullptr) {
@@ -555,6 +498,34 @@
} // 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;
@@ -613,34 +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();
- }
-
- // Convert RO partitions.
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
@@ -696,11 +661,6 @@
}
}
}
-
- // Always append userdata last for stable ordering.
- if (EraseFstabEntry(fstab, "/data")) {
- fstab->emplace_back(userdata);
- }
}
void EnableMandatoryFlags(Fstab* fstab) {
@@ -742,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;
}
@@ -853,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();
@@ -886,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..354d02a 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -81,10 +81,17 @@
return filesystems.find("\t" + filesystem + "\n") != std::string::npos;
}
+const auto kLowerdirOption = "lowerdir="s;
+const auto kUpperdirOption = "upperdir="s;
+
} // namespace
#if ALLOW_ADBD_DISABLE_VERITY == 0 // If we are a user build, provide stubs
+bool fs_mgr_wants_overlayfs(FstabEntry*) {
+ return false;
+}
+
Fstab fs_mgr_overlayfs_candidate_list(const Fstab&) {
return {};
}
@@ -93,7 +100,7 @@
return false;
}
-bool fs_mgr_overlayfs_setup(const char*, const char*, bool* change, bool) {
+bool fs_mgr_overlayfs_setup(const char*, bool* change, bool) {
if (change) *change = false;
return false;
}
@@ -324,9 +331,6 @@
return "";
}
-const auto kLowerdirOption = "lowerdir="s;
-const auto kUpperdirOption = "upperdir="s;
-
static inline bool KernelSupportsUserXattrs() {
struct utsname uts;
uname(&uts);
@@ -366,48 +370,6 @@
return ret;
}
-bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true) {
- Fstab fstab;
- auto save_errno = errno;
- if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
- return false;
- }
- errno = save_errno;
- const auto lowerdir = kLowerdirOption + mount_point;
- for (const auto& entry : fstab) {
- if (overlay_only && "overlay" != entry.fs_type && "overlayfs" != entry.fs_type) continue;
- if (mount_point != entry.mount_point) continue;
- if (!overlay_only) return true;
- const auto options = android::base::Split(entry.fs_options, ",");
- for (const auto& opt : options) {
- if (opt == lowerdir) {
- return true;
- }
- }
- }
- return false;
-}
-
-bool fs_mgr_wants_overlayfs(FstabEntry* entry) {
- // Don't check entries that are managed by vold.
- if (entry->fs_mgr_flags.vold_managed || entry->fs_mgr_flags.recovery_only) return false;
-
- // *_other doesn't want overlayfs.
- if (entry->fs_mgr_flags.slot_select_other) return false;
-
- // Only concerned with readonly partitions.
- if (!(entry->flags & MS_RDONLY)) return false;
-
- // If unbindable, do not allow overlayfs as this could expose us to
- // security issues. On Android, this could also be used to turn off
- // the ability to overlay an otherwise acceptable filesystem since
- // /system and /vendor are never bound(sic) to.
- if (entry->flags & MS_UNBINDABLE) return false;
-
- if (!fs_mgr_overlayfs_enabled(entry)) return false;
-
- return true;
-}
constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
bool fs_mgr_overlayfs_setup_dir(const std::string& dir, std::string* overlay, bool* change) {
@@ -642,6 +604,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 +1106,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) {
@@ -1274,9 +1246,45 @@
} // namespace
+bool fs_mgr_wants_overlayfs(FstabEntry* entry) {
+ // Don't check entries that are managed by vold.
+ if (entry->fs_mgr_flags.vold_managed || entry->fs_mgr_flags.recovery_only) return false;
+
+ // *_other doesn't want overlayfs.
+ if (entry->fs_mgr_flags.slot_select_other) return false;
+
+ // Only concerned with readonly partitions.
+ if (!(entry->flags & MS_RDONLY)) return false;
+
+ // If unbindable, do not allow overlayfs as this could expose us to
+ // security issues. On Android, this could also be used to turn off
+ // the ability to overlay an otherwise acceptable filesystem since
+ // /system and /vendor are never bound(sic) to.
+ if (entry->flags & MS_UNBINDABLE) return false;
+
+ if (!fs_mgr_overlayfs_enabled(entry)) return false;
+
+ return true;
+}
+
Fstab fs_mgr_overlayfs_candidate_list(const Fstab& fstab) {
+ android::fs_mgr::Fstab mounts;
+ if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
+ PLOG(ERROR) << "Failed to read /proc/mounts";
+ return {};
+ }
+
Fstab candidates;
for (const auto& entry : fstab) {
+ // Filter out partitions whose type doesn't match what's mounted.
+ // This avoids spammy behavior on devices which can mount different
+ // filesystems for each partition.
+ auto proc_mount_point = (entry.mount_point == "/system") ? "/" : entry.mount_point;
+ auto mounted = GetEntryForMountPoint(&mounts, proc_mount_point);
+ if (!mounted || mounted->fs_type != entry.fs_type) {
+ continue;
+ }
+
FstabEntry new_entry = entry;
if (!fs_mgr_overlayfs_already_mounted(entry.mount_point) &&
!fs_mgr_wants_overlayfs(&new_entry)) {
@@ -1349,8 +1357,7 @@
// Returns false if setup not permitted, errno set to last error.
// If something is altered, set *change.
-bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool* change,
- bool force) {
+bool fs_mgr_overlayfs_setup(const char* mount_point, bool* change, bool force) {
if (change) *change = false;
auto ret = false;
if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return ret;
@@ -1387,7 +1394,6 @@
std::string dir;
for (const auto& overlay_mount_point : OverlayMountPoints()) {
- if (backing && backing[0] && (overlay_mount_point != backing)) continue;
if (overlay_mount_point == kScratchMountPoint) {
if (!fs_mgr_overlayfs_setup_scratch(fstab, change)) continue;
} else {
@@ -1683,6 +1689,28 @@
#endif // ALLOW_ADBD_DISABLE_VERITY != 0
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only) {
+ Fstab fstab;
+ auto save_errno = errno;
+ if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+ return false;
+ }
+ errno = save_errno;
+ const auto lowerdir = kLowerdirOption + mount_point;
+ for (const auto& entry : fstab) {
+ if (overlay_only && "overlay" != entry.fs_type && "overlayfs" != entry.fs_type) continue;
+ if (mount_point != entry.mount_point) continue;
+ if (!overlay_only) return true;
+ const auto options = android::base::Split(entry.fs_options, ",");
+ for (const auto& opt : options) {
+ if (opt == lowerdir) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev) {
struct statfs fs;
if ((statfs((mount_point + "/lost+found").c_str(), &fs) == -1) ||
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index deaf5f7..01517b0 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -43,6 +43,8 @@
#include <libgsi/libgsid.h>
using namespace std::literals;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::FstabEntry;
namespace {
@@ -62,22 +64,12 @@
::exit(exit_status);
}
-bool remountable_partition(const android::fs_mgr::FstabEntry& entry) {
- if (entry.fs_mgr_flags.vold_managed) return false;
- if (entry.fs_mgr_flags.recovery_only) return false;
- if (entry.fs_mgr_flags.slot_select_other) return false;
- if (!(entry.flags & MS_RDONLY)) return false;
- if (entry.fs_type == "vfat") return false;
- return true;
-}
-
const std::string system_mount_point(const android::fs_mgr::FstabEntry& entry) {
if (entry.mount_point == "/") return "/system";
return entry.mount_point;
}
-const android::fs_mgr::FstabEntry* is_wrapped(const android::fs_mgr::Fstab& overlayfs_candidates,
- const android::fs_mgr::FstabEntry& entry) {
+const FstabEntry* GetWrappedEntry(const Fstab& overlayfs_candidates, const FstabEntry& entry) {
auto mount_point = system_mount_point(entry);
auto it = std::find_if(overlayfs_candidates.begin(), overlayfs_candidates.end(),
[&mount_point](const auto& entry) {
@@ -99,12 +91,8 @@
logd(id, severity, tag, file, line, message);
}
-[[noreturn]] void reboot(bool overlayfs = false) {
- if (overlayfs) {
- LOG(INFO) << "Successfully setup overlayfs\nrebooting device";
- } else {
- LOG(INFO) << "Successfully disabled verity\nrebooting device";
- }
+[[noreturn]] void reboot() {
+ LOG(INFO) << "Rebooting device for new settings to take effect";
::sync();
android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,remount");
::sleep(60);
@@ -140,15 +128,369 @@
BAD_OVERLAY,
NO_MOUNTS,
REMOUNT_FAILED,
- MUST_REBOOT,
BINDER_ERROR,
CHECKPOINTING,
GSID_ERROR,
- CLEAN_SCRATCH_FILES,
};
-static int do_remount(int argc, char* argv[]) {
- RemountStatus retval = REMOUNT_SUCCESS;
+static bool ReadFstab(const char* fstab_file, android::fs_mgr::Fstab* fstab) {
+ if (fstab_file) {
+ return android::fs_mgr::ReadFstabFromFile(fstab_file, fstab);
+ }
+ if (!android::fs_mgr::ReadDefaultFstab(fstab)) {
+ return false;
+ }
+
+ // Manufacture a / entry from /proc/mounts if missing.
+ if (!GetEntryForMountPoint(fstab, "/system") && !GetEntryForMountPoint(fstab, "/")) {
+ android::fs_mgr::Fstab mounts;
+ if (android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
+ if (auto entry = GetEntryForMountPoint(&mounts, "/")) {
+ if (entry->fs_type != "rootfs") fstab->emplace_back(*entry);
+ }
+ }
+ }
+ return true;
+}
+
+static RemountStatus VerifyCheckpointing() {
+ if (!android::base::GetBoolProperty("ro.virtual_ab.enabled", false) &&
+ !android::base::GetBoolProperty("ro.virtual_ab.retrofit", false)) {
+ return REMOUNT_SUCCESS;
+ }
+
+ // Virtual A/B devices can use /data as backing storage; make sure we're
+ // not checkpointing.
+ auto vold = GetVold();
+ bool checkpointing = false;
+ if (!vold->isCheckpointing(&checkpointing).isOk()) {
+ LOG(ERROR) << "Could not determine checkpointing status.";
+ return BINDER_ERROR;
+ }
+ if (checkpointing) {
+ LOG(ERROR) << "Cannot use remount when a checkpoint is in progress.";
+ return CHECKPOINTING;
+ }
+ return REMOUNT_SUCCESS;
+}
+
+static bool IsRemountable(Fstab& candidates, const FstabEntry& entry) {
+ if (entry.fs_mgr_flags.vold_managed || entry.fs_mgr_flags.recovery_only ||
+ entry.fs_mgr_flags.slot_select_other) {
+ return false;
+ }
+ if (!(entry.flags & MS_RDONLY)) {
+ return false;
+ }
+ if (entry.fs_type == "vfat") {
+ return false;
+ }
+ if (auto candidate_entry = GetEntryForMountPoint(&candidates, entry.mount_point)) {
+ return candidate_entry->fs_type == entry.fs_type;
+ }
+ if (GetWrappedEntry(candidates, entry)) {
+ return false;
+ }
+ return true;
+}
+
+static Fstab::const_iterator FindPartition(const Fstab& fstab, const std::string& partition) {
+ Fstab mounts;
+ if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
+ LOG(ERROR) << "Failed to read /proc/mounts";
+ return fstab.end();
+ }
+
+ for (auto iter = fstab.begin(); iter != fstab.end(); iter++) {
+ const auto mount_point = system_mount_point(*iter);
+ if (partition == mount_point || partition == android::base::Basename(mount_point)) {
+ // In case fstab has multiple entries, pick the one that matches the
+ // actual mounted filesystem type.
+ auto proc_mount_point = (iter->mount_point == "/system") ? "/" : iter->mount_point;
+ auto mounted = GetEntryForMountPoint(&mounts, proc_mount_point);
+ if (mounted && mounted->fs_type == iter->fs_type) {
+ return iter;
+ }
+ }
+ }
+ return fstab.end();
+}
+
+static Fstab GetAllRemountablePartitions(Fstab& fstab) {
+ auto candidates = fs_mgr_overlayfs_candidate_list(fstab);
+
+ Fstab partitions;
+ for (const auto& entry : fstab) {
+ if (IsRemountable(candidates, entry)) {
+ partitions.emplace_back(entry);
+ }
+ }
+ return partitions;
+}
+
+static RemountStatus GetRemountList(const Fstab& fstab, const std::vector<std::string>& argv,
+ Fstab* partitions) {
+ auto candidates = fs_mgr_overlayfs_candidate_list(fstab);
+
+ for (const auto& arg : argv) {
+ std::string partition = arg;
+ if (partition == "/") {
+ partition = "/system";
+ }
+
+ auto it = FindPartition(fstab, partition);
+ if (it == fstab.end()) {
+ LOG(ERROR) << "Unknown partition " << arg;
+ return UNKNOWN_PARTITION;
+ }
+
+ const FstabEntry* entry = &*it;
+ if (auto wrap = GetWrappedEntry(candidates, *entry); wrap != nullptr) {
+ LOG(INFO) << "partition " << arg << " covered by overlayfs for " << wrap->mount_point
+ << ", switching";
+ entry = wrap;
+ }
+
+ // If it's already remounted, include it so it gets gracefully skipped
+ // later on.
+ if (!fs_mgr_overlayfs_already_mounted(entry->mount_point) &&
+ !IsRemountable(candidates, *entry)) {
+ LOG(ERROR) << "Invalid partition " << arg;
+ return INVALID_PARTITION;
+ }
+ if (GetEntryForMountPoint(partitions, entry->mount_point) != nullptr) {
+ continue;
+ }
+ partitions->emplace_back(*entry);
+ }
+
+ return REMOUNT_SUCCESS;
+}
+
+struct RemountCheckResult {
+ bool reboot_later = false;
+ bool setup_overlayfs = false;
+ bool disabled_verity = false;
+ bool verity_error = false;
+ bool remounted_anything = false;
+};
+
+static RemountStatus CheckVerity(const FstabEntry& entry, RemountCheckResult* result) {
+ if (!fs_mgr_is_verity_enabled(entry)) {
+ return REMOUNT_SUCCESS;
+ }
+ if (android::base::GetProperty("ro.boot.vbmeta.device_state", "") == "locked") {
+ return VERITY_PARTITION;
+ }
+
+ bool ok = false;
+
+ std::unique_ptr<AvbOps, decltype(&::avb_ops_user_free)> ops(avb_ops_user_new(),
+ &::avb_ops_user_free);
+ if (ops) {
+ auto suffix = android::base::GetProperty("ro.boot.slot_suffix", "");
+ ok = avb_user_verity_set(ops.get(), suffix.c_str(), false);
+ }
+ if (!ok && fs_mgr_set_blk_ro(entry.blk_device, false)) {
+ fec::io fh(entry.blk_device.c_str(), O_RDWR);
+ ok = fh && fh.set_verity_status(false);
+ }
+ if (!ok) {
+ return VERITY_PARTITION;
+ }
+ result->disabled_verity = true;
+ result->reboot_later = true;
+ return REMOUNT_SUCCESS;
+}
+
+static RemountStatus CheckVerityAndOverlayfs(Fstab* partitions, RemountCheckResult* result) {
+ RemountStatus status = REMOUNT_SUCCESS;
+ for (auto it = partitions->begin(); it != partitions->end();) {
+ auto& entry = *it;
+ const auto& mount_point = entry.mount_point;
+
+ if (auto rv = CheckVerity(entry, result); rv != REMOUNT_SUCCESS) {
+ LOG(ERROR) << "Skipping verified partition " << mount_point << " for remount";
+ status = rv;
+ it = partitions->erase(it);
+ continue;
+ }
+
+ if (fs_mgr_wants_overlayfs(&entry)) {
+ bool change = false;
+ bool force = result->disabled_verity;
+ if (!fs_mgr_overlayfs_setup(mount_point.c_str(), &change, force)) {
+ LOG(ERROR) << "Overlayfs setup for " << mount_point << " failed, skipping";
+ status = BAD_OVERLAY;
+ it = partitions->erase(it);
+ continue;
+ }
+ if (change) {
+ LOG(INFO) << "Using overlayfs for " << mount_point;
+ result->reboot_later = true;
+ result->setup_overlayfs = true;
+ }
+ }
+ it++;
+ }
+ return status;
+}
+
+static RemountStatus EnableDsuIfNeeded() {
+ auto gsid = android::gsi::GetGsiService();
+ if (!gsid) {
+ return REMOUNT_SUCCESS;
+ }
+
+ auto dsu_running = false;
+ if (auto status = gsid->isGsiRunning(&dsu_running); !status.isOk()) {
+ LOG(ERROR) << "Failed to get DSU running state: " << status;
+ return BINDER_ERROR;
+ }
+ auto dsu_enabled = false;
+ if (auto status = gsid->isGsiEnabled(&dsu_enabled); !status.isOk()) {
+ LOG(ERROR) << "Failed to get DSU enabled state: " << status;
+ return BINDER_ERROR;
+ }
+ if (dsu_running && !dsu_enabled) {
+ std::string dsu_slot;
+ if (auto status = gsid->getActiveDsuSlot(&dsu_slot); !status.isOk()) {
+ LOG(ERROR) << "Failed to get active DSU slot: " << status;
+ return BINDER_ERROR;
+ }
+ LOG(INFO) << "DSU is running but disabled, enable DSU so that we stay within the "
+ "DSU guest system after reboot";
+ int error = 0;
+ if (auto status = gsid->enableGsi(/* oneShot = */ true, dsu_slot, &error);
+ !status.isOk() || error != android::gsi::IGsiService::INSTALL_OK) {
+ LOG(ERROR) << "Failed to enable DSU: " << status << ", error code: " << error;
+ return !status.isOk() ? BINDER_ERROR : GSID_ERROR;
+ }
+ LOG(INFO) << "Successfully enabled DSU (one-shot mode)";
+ }
+ return REMOUNT_SUCCESS;
+}
+
+static RemountStatus RemountPartition(Fstab& fstab, Fstab& mounts, FstabEntry& entry) {
+ // unlock the r/o key for the mount point device
+ if (entry.fs_mgr_flags.logical) {
+ fs_mgr_update_logical_partition(&entry);
+ }
+ auto blk_device = entry.blk_device;
+ auto mount_point = entry.mount_point;
+
+ auto found = false;
+ for (auto it = mounts.rbegin(); it != mounts.rend(); ++it) {
+ auto& rentry = *it;
+ if (mount_point == rentry.mount_point) {
+ blk_device = rentry.blk_device;
+ found = true;
+ break;
+ }
+ // Find overlayfs mount point?
+ if ((mount_point == "/" && rentry.mount_point == "/system") ||
+ (mount_point == "/system" && rentry.mount_point == "/")) {
+ blk_device = rentry.blk_device;
+ mount_point = "/system";
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ PLOG(INFO) << "skip unmounted partition dev:" << blk_device << " mnt:" << mount_point;
+ return REMOUNT_SUCCESS;
+ }
+ if (blk_device == "/dev/root") {
+ auto from_fstab = GetEntryForMountPoint(&fstab, mount_point);
+ if (from_fstab) blk_device = from_fstab->blk_device;
+ }
+ fs_mgr_set_blk_ro(blk_device, false);
+
+ // Find system-as-root mount point?
+ if ((mount_point == "/system") && !GetEntryForMountPoint(&mounts, mount_point) &&
+ GetEntryForMountPoint(&mounts, "/")) {
+ mount_point = "/";
+ }
+
+ // Now remount!
+ if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
+ nullptr) == 0) {
+ return REMOUNT_SUCCESS;
+ }
+ if ((errno == EINVAL) && (mount_point != entry.mount_point)) {
+ mount_point = entry.mount_point;
+ if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
+ nullptr) == 0) {
+ return REMOUNT_SUCCESS;
+ }
+ }
+
+ PLOG(ERROR) << "failed to remount partition dev:" << blk_device << " mnt:" << mount_point;
+ return REMOUNT_FAILED;
+}
+
+static int do_remount(Fstab& fstab, const std::vector<std::string>& partition_args,
+ RemountCheckResult* check_result) {
+ Fstab partitions;
+ if (partition_args.empty()) {
+ partitions = GetAllRemountablePartitions(fstab);
+ } else {
+ if (auto rv = GetRemountList(fstab, partition_args, &partitions); rv != REMOUNT_SUCCESS) {
+ return rv;
+ }
+ }
+
+ // Check verity and optionally setup overlayfs backing.
+ auto retval = CheckVerityAndOverlayfs(&partitions, check_result);
+
+ if (partitions.empty() || check_result->disabled_verity) {
+ if (partitions.empty()) {
+ LOG(WARNING) << "No remountable partitions were found.";
+ }
+ return retval;
+ }
+
+ // Mount overlayfs.
+ errno = 0;
+ if (!fs_mgr_overlayfs_mount_all(&partitions) && errno) {
+ PLOG(ERROR) << "Can not mount overlayfs for partitions";
+ return BAD_OVERLAY;
+ }
+
+ // Get actual mounts _after_ overlayfs has been added.
+ android::fs_mgr::Fstab mounts;
+ if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts) || mounts.empty()) {
+ PLOG(ERROR) << "Failed to read /proc/mounts";
+ return NO_MOUNTS;
+ }
+
+ // Remount selected partitions.
+ for (auto& entry : partitions) {
+ if (auto rv = RemountPartition(fstab, mounts, entry); rv != REMOUNT_SUCCESS) {
+ retval = rv;
+ } else {
+ check_result->remounted_anything = true;
+ }
+ }
+ return retval;
+}
+
+static int do_clean_scratch_files() {
+ android::fs_mgr::CleanupOldScratchFiles();
+ return 0;
+}
+
+int main(int argc, char* argv[]) {
+ android::base::InitLogging(argv, MyLogger);
+ if (argc > 0 && android::base::Basename(argv[0]) == "clean_scratch_files"s) {
+ return do_clean_scratch_files();
+ }
+
+ // Make sure we are root.
+ if (::getuid() != 0) {
+ LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+ return NOT_ROOT;
+ }
// If somehow this executable is delivered on a "user" build, it can
// not function, so providing a clear message to the caller rather than
@@ -159,7 +501,8 @@
}
const char* fstab_file = nullptr;
- auto can_reboot = false;
+ auto auto_reboot = false;
+ std::vector<std::string> partition_args;
struct option longopts[] = {
{"fstab", required_argument, nullptr, 'T'},
@@ -175,7 +518,7 @@
usage(SUCCESS);
break;
case 'R':
- can_reboot = true;
+ auto_reboot = true;
break;
case 'T':
if (fstab_file) {
@@ -188,7 +531,7 @@
verbose = true;
break;
case 'C':
- return CLEAN_SCRATCH_FILES;
+ return do_clean_scratch_files();
default:
LOG(ERROR) << "Bad Argument -" << char(opt);
usage(BADARG);
@@ -196,310 +539,51 @@
}
}
- // Make sure we are root.
- if (::getuid() != 0) {
- LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
- return NOT_ROOT;
+ for (; argc > optind; ++optind) {
+ partition_args.emplace_back(argv[optind]);
+ }
+
+ // Make sure checkpointing is disabled if necessary.
+ if (auto rv = VerifyCheckpointing(); rv != REMOUNT_SUCCESS) {
+ return rv;
}
// Read the selected fstab.
- android::fs_mgr::Fstab fstab;
- auto fstab_read = false;
- if (fstab_file) {
- fstab_read = android::fs_mgr::ReadFstabFromFile(fstab_file, &fstab);
- } else {
- fstab_read = android::fs_mgr::ReadDefaultFstab(&fstab);
- // Manufacture a / entry from /proc/mounts if missing.
- if (!GetEntryForMountPoint(&fstab, "/system") && !GetEntryForMountPoint(&fstab, "/")) {
- android::fs_mgr::Fstab mounts;
- if (android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
- if (auto entry = GetEntryForMountPoint(&mounts, "/")) {
- if (entry->fs_type != "rootfs") fstab.emplace_back(*entry);
- }
- }
- }
- }
- if (!fstab_read || fstab.empty()) {
+ Fstab fstab;
+ if (!ReadFstab(fstab_file, &fstab) || fstab.empty()) {
PLOG(ERROR) << "Failed to read fstab";
return NO_FSTAB;
}
- if (android::base::GetBoolProperty("ro.virtual_ab.enabled", false) &&
- !android::base::GetBoolProperty("ro.virtual_ab.retrofit", false)) {
- // Virtual A/B devices can use /data as backing storage; make sure we're
- // not checkpointing.
- auto vold = GetVold();
- bool checkpointing = false;
- if (!vold->isCheckpointing(&checkpointing).isOk()) {
- LOG(ERROR) << "Could not determine checkpointing status.";
- return BINDER_ERROR;
- }
- if (checkpointing) {
- LOG(ERROR) << "Cannot use remount when a checkpoint is in progress.";
- return CHECKPOINTING;
- }
+ RemountCheckResult check_result;
+ int result = do_remount(fstab, partition_args, &check_result);
+
+ if (check_result.disabled_verity && check_result.setup_overlayfs) {
+ LOG(INFO) << "Verity disabled; overlayfs enabled.";
+ } else if (check_result.disabled_verity) {
+ LOG(INFO) << "Verity disabled.";
+ } else if (check_result.setup_overlayfs) {
+ LOG(INFO) << "Overlayfs enabled.";
}
- // Generate the list of supported overlayfs mount points.
- auto overlayfs_candidates = fs_mgr_overlayfs_candidate_list(fstab);
-
- // Generate the all remountable partitions sub-list
- android::fs_mgr::Fstab all;
- for (auto const& entry : fstab) {
- if (!remountable_partition(entry)) continue;
- if (overlayfs_candidates.empty() ||
- GetEntryForMountPoint(&overlayfs_candidates, entry.mount_point) ||
- (is_wrapped(overlayfs_candidates, entry) == nullptr)) {
- all.emplace_back(entry);
- }
- }
-
- // Parse the unique list of valid partition arguments.
- android::fs_mgr::Fstab partitions;
- for (; argc > optind; ++optind) {
- auto partition = std::string(argv[optind]);
- if (partition.empty()) continue;
- if (partition == "/") partition = "/system";
- auto find_part = [&partition](const auto& entry) {
- const auto mount_point = system_mount_point(entry);
- if (partition == mount_point) return true;
- if (partition == android::base::Basename(mount_point)) return true;
- return false;
- };
- // Do we know about the partition?
- auto it = std::find_if(fstab.begin(), fstab.end(), find_part);
- if (it == fstab.end()) {
- LOG(ERROR) << "Unknown partition " << argv[optind] << ", skipping";
- retval = UNKNOWN_PARTITION;
- continue;
- }
- // Is that one covered by an existing overlayfs?
- auto wrap = is_wrapped(overlayfs_candidates, *it);
- if (wrap) {
- LOG(INFO) << "partition " << argv[optind] << " covered by overlayfs for "
- << wrap->mount_point << ", switching";
- partition = system_mount_point(*wrap);
- }
- // Is it a remountable partition?
- it = std::find_if(all.begin(), all.end(), find_part);
- if (it == all.end()) {
- LOG(ERROR) << "Invalid partition " << argv[optind] << ", skipping";
- retval = INVALID_PARTITION;
- continue;
- }
- if (GetEntryForMountPoint(&partitions, it->mount_point) == nullptr) {
- partitions.emplace_back(*it);
- }
- }
-
- if (partitions.empty() && !retval) {
- partitions = all;
- }
-
- // Check verity and optionally setup overlayfs backing.
- auto reboot_later = false;
- auto user_please_reboot_later = false;
- auto setup_overlayfs = false;
- auto just_disabled_verity = false;
- for (auto it = partitions.begin(); it != partitions.end();) {
- auto& entry = *it;
- auto& mount_point = entry.mount_point;
- if (fs_mgr_is_verity_enabled(entry)) {
- retval = VERITY_PARTITION;
- auto ret = false;
- if (android::base::GetProperty("ro.boot.vbmeta.device_state", "") != "locked") {
- if (AvbOps* ops = avb_ops_user_new()) {
- ret = avb_user_verity_set(
- ops, android::base::GetProperty("ro.boot.slot_suffix", "").c_str(),
- false);
- avb_ops_user_free(ops);
- }
- if (!ret && fs_mgr_set_blk_ro(entry.blk_device, false)) {
- fec::io fh(entry.blk_device.c_str(), O_RDWR);
- ret = fh && fh.set_verity_status(false);
- }
- if (ret) {
- LOG(WARNING) << "Disabling verity for " << mount_point;
- just_disabled_verity = true;
- reboot_later = can_reboot;
- user_please_reboot_later = true;
- }
+ if (check_result.reboot_later) {
+ if (auto_reboot) {
+ // If (1) remount requires a reboot to take effect, (2) system is currently
+ // running a DSU guest and (3) DSU is disabled, then enable DSU so that the
+ // next reboot would not take us back to the host system but stay within
+ // the guest system.
+ if (auto rv = EnableDsuIfNeeded(); rv != REMOUNT_SUCCESS) {
+ LOG(ERROR) << "Unable to automatically enable DSU";
+ return rv;
}
- if (!ret) {
- LOG(ERROR) << "Skipping " << mount_point << " for remount";
- it = partitions.erase(it);
- continue;
- }
+ reboot();
+ } else {
+ LOG(INFO) << "Now reboot your device for settings to take effect";
}
-
- auto change = false;
- errno = 0;
- if (fs_mgr_overlayfs_setup(nullptr, mount_point.c_str(), &change, just_disabled_verity)) {
- if (change) {
- LOG(INFO) << "Using overlayfs for " << mount_point;
- reboot_later = can_reboot;
- user_please_reboot_later = true;
- setup_overlayfs = true;
- }
- } else if (errno) {
- PLOG(ERROR) << "Overlayfs setup for " << mount_point << " failed, skipping";
- retval = BAD_OVERLAY;
- it = partitions.erase(it);
- continue;
- }
- ++it;
+ return REMOUNT_SUCCESS;
}
-
- // If (1) remount requires a reboot to take effect, (2) system is currently
- // running a DSU guest and (3) DSU is disabled, then enable DSU so that the
- // next reboot would not take us back to the host system but stay within
- // the guest system.
- if (reboot_later) {
- if (auto gsid = android::gsi::GetGsiService()) {
- auto dsu_running = false;
- if (auto status = gsid->isGsiRunning(&dsu_running); !status.isOk()) {
- LOG(ERROR) << "Failed to get DSU running state: " << status;
- return BINDER_ERROR;
- }
- auto dsu_enabled = false;
- if (auto status = gsid->isGsiEnabled(&dsu_enabled); !status.isOk()) {
- LOG(ERROR) << "Failed to get DSU enabled state: " << status;
- return BINDER_ERROR;
- }
- if (dsu_running && !dsu_enabled) {
- std::string dsu_slot;
- if (auto status = gsid->getActiveDsuSlot(&dsu_slot); !status.isOk()) {
- LOG(ERROR) << "Failed to get active DSU slot: " << status;
- return BINDER_ERROR;
- }
- LOG(INFO) << "DSU is running but disabled, enable DSU so that we stay within the "
- "DSU guest system after reboot";
- int error = 0;
- if (auto status = gsid->enableGsi(/* oneShot = */ true, dsu_slot, &error);
- !status.isOk() || error != android::gsi::IGsiService::INSTALL_OK) {
- LOG(ERROR) << "Failed to enable DSU: " << status << ", error code: " << error;
- return !status.isOk() ? BINDER_ERROR : GSID_ERROR;
- }
- LOG(INFO) << "Successfully enabled DSU (one-shot mode)";
- }
- }
- }
-
- if (partitions.empty() || just_disabled_verity) {
- if (reboot_later) reboot(setup_overlayfs);
- if (user_please_reboot_later) {
- return MUST_REBOOT;
- }
- LOG(WARNING) << "No partitions to remount";
- return retval;
- }
-
- // Mount overlayfs.
- errno = 0;
- if (!fs_mgr_overlayfs_mount_all(&partitions) && errno) {
- retval = BAD_OVERLAY;
- PLOG(ERROR) << "Can not mount overlayfs for partitions";
- }
-
- // Get actual mounts _after_ overlayfs has been added.
- android::fs_mgr::Fstab mounts;
- if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts) || mounts.empty()) {
- PLOG(ERROR) << "Failed to read /proc/mounts";
- retval = NO_MOUNTS;
- }
-
- // Remount selected partitions.
- for (auto& entry : partitions) {
- // unlock the r/o key for the mount point device
- if (entry.fs_mgr_flags.logical) {
- fs_mgr_update_logical_partition(&entry);
- }
- auto blk_device = entry.blk_device;
- auto mount_point = entry.mount_point;
-
- auto found = false;
- for (auto it = mounts.rbegin(); it != mounts.rend(); ++it) {
- auto& rentry = *it;
- if (mount_point == rentry.mount_point) {
- blk_device = rentry.blk_device;
- found = true;
- break;
- }
- // Find overlayfs mount point?
- if ((mount_point == "/" && rentry.mount_point == "/system") ||
- (mount_point == "/system" && rentry.mount_point == "/")) {
- blk_device = rentry.blk_device;
- mount_point = "/system";
- found = true;
- break;
- }
- }
- if (!found) {
- PLOG(INFO) << "skip unmounted partition dev:" << blk_device << " mnt:" << mount_point;
- continue;
- }
- if (blk_device == "/dev/root") {
- auto from_fstab = GetEntryForMountPoint(&fstab, mount_point);
- if (from_fstab) blk_device = from_fstab->blk_device;
- }
- fs_mgr_set_blk_ro(blk_device, false);
-
- // Find system-as-root mount point?
- if ((mount_point == "/system") && !GetEntryForMountPoint(&mounts, mount_point) &&
- GetEntryForMountPoint(&mounts, "/")) {
- mount_point = "/";
- }
-
- // Now remount!
- if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
- nullptr) == 0) {
- continue;
- }
- if ((errno == EINVAL) && (mount_point != entry.mount_point)) {
- mount_point = entry.mount_point;
- if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
- nullptr) == 0) {
- continue;
- }
- }
- PLOG(ERROR) << "failed to remount partition dev:" << blk_device << " mnt:" << mount_point;
- // If errno is EROFS at this point, we are dealing with r/o
- // filesystem types like squashfs, erofs or ext4 dedupe. We will
- // consider such a device that does not have CONFIG_OVERLAY_FS
- // in the kernel as a misconfigured.
- if (errno == EROFS) {
- LOG(ERROR) << "Consider providing all the dependencies to enable overlayfs";
- }
- retval = REMOUNT_FAILED;
- }
-
- if (reboot_later) reboot(setup_overlayfs);
- if (user_please_reboot_later) {
- LOG(INFO) << "Now reboot your device for settings to take effect";
- return 0;
- }
-
- return retval;
-}
-
-static int do_clean_scratch_files() {
- android::fs_mgr::CleanupOldScratchFiles();
- return 0;
-}
-
-int main(int argc, char* argv[]) {
- android::base::InitLogging(argv, MyLogger);
- if (argc > 0 && android::base::Basename(argv[0]) == "clean_scratch_files"s) {
- return do_clean_scratch_files();
- }
- int result = do_remount(argc, argv);
- if (result == MUST_REBOOT) {
- LOG(INFO) << "Now reboot your device for settings to take effect";
- result = 0;
- } else if (result == REMOUNT_SUCCESS) {
+ if (result == REMOUNT_SUCCESS) {
printf("remount succeeded\n");
- } else if (result == CLEAN_SCRATCH_FILES) {
- return do_clean_scratch_files();
} else {
printf("remount failed\n");
}
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 6caab1f..ec1d78f 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -26,12 +26,14 @@
android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
+bool fs_mgr_wants_overlayfs(android::fs_mgr::FstabEntry* entry);
bool fs_mgr_overlayfs_mount_all(android::fs_mgr::Fstab* fstab);
-bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
- bool* change = nullptr, bool force = true);
+bool fs_mgr_overlayfs_setup(const char* mount_point = nullptr, bool* change = nullptr,
+ bool force = true);
bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
bool fs_mgr_overlayfs_is_setup();
bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev);
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true);
std::string fs_mgr_get_context(const std::string& mount_point);
enum class OverlayfsValidResult {
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..c8a0249 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -38,7 +38,7 @@
: fd_(-1),
header_(),
fd_size_(0),
- merge_op_blocks_(std::make_shared<std::vector<uint32_t>>()),
+ block_pos_index_(std::make_shared<std::vector<int>>()),
reader_flag_(reader_flag) {}
static void SHA256(const void*, size_t, uint8_t[]) {
@@ -58,13 +58,12 @@
cow->fd_size_ = fd_size_;
cow->last_label_ = last_label_;
cow->ops_ = ops_;
- cow->merge_op_blocks_ = merge_op_blocks_;
cow->merge_op_start_ = merge_op_start_;
- cow->block_map_ = block_map_;
cow->num_total_data_ops_ = num_total_data_ops_;
cow->num_ordered_ops_to_merge_ = num_ordered_ops_to_merge_;
cow->has_seq_ops_ = has_seq_ops_;
cow->data_loc_ = data_loc_;
+ cow->block_pos_index_ = block_pos_index_;
return cow;
}
@@ -415,10 +414,10 @@
// Replace-op-4, Zero-op-9, Replace-op-5 }
//==============================================================
bool CowReader::PrepMergeOps() {
- auto merge_op_blocks = std::make_shared<std::vector<uint32_t>>();
+ auto merge_op_blocks = std::make_unique<std::vector<uint32_t>>();
std::vector<int> other_ops;
auto seq_ops_set = std::unordered_set<uint32_t>();
- auto block_map = std::make_shared<std::unordered_map<uint32_t, int>>();
+ auto block_map = std::make_unique<std::unordered_map<uint32_t, int>>();
size_t num_seqs = 0;
size_t read;
@@ -477,13 +476,18 @@
merge_op_blocks->insert(merge_op_blocks->end(), other_ops.begin(), other_ops.end());
+ for (auto block : *merge_op_blocks) {
+ block_pos_index_->push_back(block_map->at(block));
+ }
+
num_total_data_ops_ = merge_op_blocks->size();
if (header_.num_merge_ops > 0) {
merge_op_start_ = header_.num_merge_ops;
}
- block_map_ = block_map;
- merge_op_blocks_ = merge_op_blocks;
+ block_map->clear();
+ merge_op_blocks->clear();
+
return true;
}
@@ -589,9 +593,7 @@
class CowRevMergeOpIter final : public ICowOpIter {
public:
explicit CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
- std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
- std::shared_ptr<std::unordered_map<uint32_t, int>> map,
- uint64_t start);
+ std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);
bool Done() override;
const CowOperation& Get() override;
@@ -602,17 +604,15 @@
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
- std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
- std::shared_ptr<std::unordered_map<uint32_t, int>> map_;
- std::vector<uint32_t>::reverse_iterator block_riter_;
+ std::vector<int>::reverse_iterator block_riter_;
+ std::shared_ptr<std::vector<int>> cow_op_index_vec_;
uint64_t start_;
};
class CowMergeOpIter final : public ICowOpIter {
public:
explicit CowMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
- std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
- std::shared_ptr<std::unordered_map<uint32_t, int>> map, uint64_t start);
+ std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);
bool Done() override;
const CowOperation& Get() override;
@@ -623,26 +623,21 @@
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
- std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
- std::shared_ptr<std::unordered_map<uint32_t, int>> map_;
- std::vector<uint32_t>::iterator block_iter_;
+ std::vector<int>::iterator block_iter_;
+ std::shared_ptr<std::vector<int>> cow_op_index_vec_;
uint64_t start_;
};
CowMergeOpIter::CowMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
- std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
- std::shared_ptr<std::unordered_map<uint32_t, int>> map,
- uint64_t start) {
+ std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start) {
ops_ = ops;
- merge_op_blocks_ = merge_op_blocks;
- map_ = map;
start_ = start;
-
- block_iter_ = merge_op_blocks->begin() + start;
+ cow_op_index_vec_ = block_pos_index;
+ block_iter_ = cow_op_index_vec_->begin() + start;
}
bool CowMergeOpIter::RDone() {
- return block_iter_ == merge_op_blocks_->begin();
+ return block_iter_ == cow_op_index_vec_->begin();
}
void CowMergeOpIter::Prev() {
@@ -651,7 +646,7 @@
}
bool CowMergeOpIter::Done() {
- return block_iter_ == merge_op_blocks_->end();
+ return block_iter_ == cow_op_index_vec_->end();
}
void CowMergeOpIter::Next() {
@@ -661,23 +656,20 @@
const CowOperation& CowMergeOpIter::Get() {
CHECK(!Done());
- return ops_->data()[map_->at(*block_iter_)];
+ return ops_->data()[*block_iter_];
}
CowRevMergeOpIter::CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
- std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
- std::shared_ptr<std::unordered_map<uint32_t, int>> map,
+ std::shared_ptr<std::vector<int>> block_pos_index,
uint64_t start) {
ops_ = ops;
- merge_op_blocks_ = merge_op_blocks;
- map_ = map;
start_ = start;
-
- block_riter_ = merge_op_blocks->rbegin();
+ cow_op_index_vec_ = block_pos_index;
+ block_riter_ = cow_op_index_vec_->rbegin();
}
bool CowRevMergeOpIter::RDone() {
- return block_riter_ == merge_op_blocks_->rbegin();
+ return block_riter_ == cow_op_index_vec_->rbegin();
}
void CowRevMergeOpIter::Prev() {
@@ -686,7 +678,7 @@
}
bool CowRevMergeOpIter::Done() {
- return block_riter_ == merge_op_blocks_->rend() - start_;
+ return block_riter_ == cow_op_index_vec_->rend() - start_;
}
void CowRevMergeOpIter::Next() {
@@ -696,7 +688,7 @@
const CowOperation& CowRevMergeOpIter::Get() {
CHECK(!Done());
- return ops_->data()[map_->at(*block_riter_)];
+ return ops_->data()[*block_riter_];
}
std::unique_ptr<ICowOpIter> CowReader::GetOpIter() {
@@ -704,12 +696,12 @@
}
std::unique_ptr<ICowOpIter> CowReader::GetRevMergeOpIter(bool ignore_progress) {
- return std::make_unique<CowRevMergeOpIter>(ops_, merge_op_blocks_, block_map_,
+ return std::make_unique<CowRevMergeOpIter>(ops_, block_pos_index_,
ignore_progress ? 0 : merge_op_start_);
}
std::unique_ptr<ICowOpIter> CowReader::GetMergeOpIter(bool ignore_progress) {
- return std::make_unique<CowMergeOpIter>(ops_, merge_op_blocks_, block_map_,
+ return std::make_unique<CowMergeOpIter>(ops_, block_pos_index_,
ignore_progress ? 0 : merge_op_start_);
}
@@ -775,6 +767,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_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index f4d5c72..fbdd6b9 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -170,9 +170,8 @@
uint64_t fd_size_;
std::optional<uint64_t> last_label_;
std::shared_ptr<std::vector<CowOperation>> ops_;
- std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
uint64_t merge_op_start_{};
- std::shared_ptr<std::unordered_map<uint32_t, int>> block_map_;
+ std::shared_ptr<std::vector<int>> block_pos_index_;
uint64_t num_total_data_ops_{};
uint64_t num_ordered_ops_to_merge_{};
bool has_seq_ops_{};
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..3ea1d65 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();
@@ -315,7 +349,7 @@
}
AssertionResult DeleteDevice(const std::string& device) {
- if (!dm_.DeleteDeviceIfExists(device)) {
+ if (!sm->DeleteDeviceIfExists(device, 1s)) {
return AssertionFailure() << "Can't delete " << device;
}
return AssertionSuccess();
@@ -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/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 9542bc1..91024d1 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -1319,26 +1319,9 @@
B="`adb_cat /system/priv-app/hello`" ||
die "system priv-app hello"
check_eq "${A}" "${B}" /system/priv-app before reboot
-SYSTEM_DEVT=`adb_sh stat --format=%D /system/hello </dev/null`
-VENDOR_DEVT=`adb_sh stat --format=%D /vendor/hello </dev/null`
SYSTEM_INO=`adb_sh stat --format=%i /system/hello </dev/null`
VENDOR_INO=`adb_sh stat --format=%i /vendor/hello </dev/null`
-BASE_SYSTEM_DEVT=`adb_sh stat --format=%D /system/bin/stat </dev/null`
-BASE_VENDOR_DEVT=`adb_sh stat --format=%D /vendor/bin/stat </dev/null`
-check_eq "${SYSTEM_DEVT%[0-9a-fA-F][0-9a-fA-F]}" "${VENDOR_DEVT%[0-9a-fA-F][0-9a-fA-F]}" vendor and system devt
check_ne "${SYSTEM_INO}" "${VENDOR_INO}" vendor and system inode
-if ${overlayfs_needed}; then
- check_ne "${SYSTEM_DEVT}" "${BASE_SYSTEM_DEVT}" system devt
- check_ne "${VENDOR_DEVT}" "${BASE_VENDOR_DEVT}" vendor devt
-else
- check_eq "${SYSTEM_DEVT}" "${BASE_SYSTEM_DEVT}" system devt
- check_eq "${VENDOR_DEVT}" "${BASE_VENDOR_DEVT}" vendor devt
-fi
-check_ne "${BASE_SYSTEM_DEVT}" "${BASE_VENDOR_DEVT}" --warning system/vendor devt
-[ -n "${SYSTEM_DEVT%[0-9a-fA-F][0-9a-fA-F]}" ] ||
- echo "${YELLOW}[ WARNING ]${NORMAL} system devt ${SYSTEM_DEVT} major 0" >&2
-[ -n "${VENDOR_DEVT%[0-9a-fA-F][0-9a-fA-F]}" ] ||
- echo "${YELLOW}[ WARNING ]${NORMAL} vendor devt ${VENDOR_DEVT} major 0" >&2
# Download libc.so, append some garbage, push back, and check if the file
# is updated.
@@ -1411,13 +1394,8 @@
echo "${GREEN}[ OK ]${NORMAL} ${i} content remains after reboot" >&2
done
-check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" system devt after reboot
-check_eq "${VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/hello </dev/null`" vendor devt after reboot
check_eq "${SYSTEM_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
check_eq "${VENDOR_INO}" "`adb_sh stat --format=%i /vendor/hello </dev/null`" vendor inode after reboot
-check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" --warning base system devt after reboot
-check_eq "${BASE_VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/bin/stat </dev/null`" --warning base vendor devt after reboot
-check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" --warning devt for su after reboot
# Feed log with selinux denials as a result of overlays
adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null || true
@@ -1542,10 +1520,7 @@
--warning vendor content after flash vendor
fi
- check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" system devt after reboot
check_eq "${SYSTEM_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
- check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" --warning base system devt after reboot
- check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" --warning devt for su after reboot
fi
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index e34e06e..e33681c 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -1127,6 +1127,10 @@
auto entry = fstab.begin();
+ 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);
@@ -1140,10 +1144,6 @@
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) {
@@ -1200,6 +1200,10 @@
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++;
@@ -1213,8 +1217,4 @@
EXPECT_EQ("system_gsi", entry->blk_device);
EXPECT_EQ("erofs", entry->fs_type);
entry++;
-
- EXPECT_EQ("/data", entry->mount_point);
- EXPECT_EQ("userdata_gsi", entry->blk_device);
- entry++;
}
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
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/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 4bbbc20..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;
}
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/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/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index 3831ef2..304248a 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -410,7 +410,7 @@
// Make sure we do this only one time. No need for std::call_once because
// init is a single-threaded process
if (access(CGROUPS_RC_PATH, F_OK) == 0) {
- LOG(WARNING) << "Attempt to call SetupCgroups more than once";
+ LOG(WARNING) << "Attempt to call CgroupSetup() more than once";
return true;
}
diff --git a/libstats/pull_lazy/TEST_MAPPING b/libstats/pull_lazy/TEST_MAPPING
index 92f1e6a..0282a03 100644
--- a/libstats/pull_lazy/TEST_MAPPING
+++ b/libstats/pull_lazy/TEST_MAPPING
@@ -4,7 +4,7 @@
"name" : "libstatspull_lazy_test"
}
],
- "hwasan-postsubmit" : [
+ "hwasan-presubmit" : [
{
"name" : "libstatspull_lazy_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/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/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 6d89f17..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
diff --git a/set-verity-state/Android.bp b/set-verity-state/Android.bp
index f0df350..34bded8 100644
--- a/set-verity-state/Android.bp
+++ b/set-verity-state/Android.bp
@@ -9,12 +9,10 @@
srcs: ["set-verity-state.cpp"],
shared_libs: [
"libbase",
+ "libbinder",
"libcrypto",
"libcrypto_utils",
- "libcutils",
- "libfec",
"libfs_mgr_binder",
- "liblog",
"libutils",
],
static_libs: [
diff --git a/set-verity-state/OWNERS b/set-verity-state/OWNERS
new file mode 100644
index 0000000..e849450
--- /dev/null
+++ b/set-verity-state/OWNERS
@@ -0,0 +1,3 @@
+dvander@google.com
+yochiang@google.com
+bowgotsai@google.com
diff --git a/set-verity-state/set-verity-state.cpp b/set-verity-state/set-verity-state.cpp
index 52a7f74..52757b6 100644
--- a/set-verity-state/set-verity-state.cpp
+++ b/set-verity-state/set-verity-state.cpp
@@ -14,242 +14,173 @@
* limitations under the License.
*/
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <libavb_user/libavb_user.h>
-#include <stdarg.h>
#include <stdio.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <unistd.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <fs_mgr.h>
+#include <binder/ProcessState.h>
#include <fs_mgr_overlayfs.h>
-#include <fstab/fstab.h>
-#include <log/log_properties.h>
+#include <libavb_user/libavb_user.h>
-#include "fec/io.h"
+using namespace std::string_literals;
+
+namespace {
#ifdef ALLOW_DISABLE_VERITY
-static const bool kAllowDisableVerity = true;
+const bool kAllowDisableVerity = true;
#else
-static const bool kAllowDisableVerity = false;
+const bool kAllowDisableVerity = false;
#endif
-using android::base::unique_fd;
-
-static void suggest_run_adb_root() {
- if (getuid() != 0) printf("Maybe run adb root?\n");
-}
-
-static bool make_block_device_writable(const std::string& dev) {
- unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
- if (fd == -1) {
- return false;
- }
-
- int OFF = 0;
- bool result = (ioctl(fd.get(), BLKROSET, &OFF) != -1);
- return result;
-}
-
-/* Turn verity on/off */
-static bool set_verity_enabled_state(const char* block_device, const char* mount_point,
- bool enable) {
- if (!make_block_device_writable(block_device)) {
- printf("Could not make block device %s writable (%s).\n", block_device, strerror(errno));
- return false;
- }
-
- fec::io fh(block_device, O_RDWR);
-
- if (!fh) {
- printf("Could not open block device %s (%s).\n", block_device, strerror(errno));
- suggest_run_adb_root();
- return false;
- }
-
- fec_verity_metadata metadata;
-
- if (!fh.get_verity_metadata(metadata)) {
- printf("Couldn't find verity metadata!\n");
- return false;
- }
-
- if (!enable && metadata.disabled) {
- printf("Verity already disabled on %s\n", mount_point);
- return false;
- }
-
- if (enable && !metadata.disabled) {
- printf("Verity already enabled on %s\n", mount_point);
- return false;
- }
-
- if (!fh.set_verity_status(enable)) {
- printf("Could not set verity %s flag on device %s with error %s\n",
- enable ? "enabled" : "disabled", block_device, strerror(errno));
- return false;
- }
-
- auto change = false;
- errno = 0;
- if (enable ? fs_mgr_overlayfs_teardown(mount_point, &change)
- : fs_mgr_overlayfs_setup(nullptr, mount_point, &change)) {
- if (change) {
- printf("%s overlayfs for %s\n", enable ? "disabling" : "using", mount_point);
- }
- } else if (errno) {
- int expected_errno = enable ? EBUSY : ENOENT;
- if (errno != expected_errno) {
- printf("Overlayfs %s for %s failed with error %s\n", enable ? "teardown" : "setup",
- mount_point, strerror(errno));
- }
- }
- printf("Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
- return true;
-}
-
/* Helper function to get A/B suffix, if any. If the device isn't
* using A/B the empty string is returned. Otherwise either "_a",
* "_b", ... is returned.
*/
-static std::string get_ab_suffix() {
+std::string get_ab_suffix() {
return android::base::GetProperty("ro.boot.slot_suffix", "");
}
-static bool is_avb_device_locked() {
+bool is_avb_device_locked() {
return android::base::GetProperty("ro.boot.vbmeta.device_state", "") == "locked";
}
-static bool overlayfs_setup(bool enable) {
+bool is_debuggable() {
+ return android::base::GetBoolProperty("ro.debuggable", false);
+}
+
+bool is_using_avb() {
+ // Figure out if we're using VB1.0 or VB2.0 (aka AVB) - by
+ // contract, androidboot.vbmeta.digest is set by the bootloader
+ // when using AVB).
+ return !android::base::GetProperty("ro.boot.vbmeta.digest", "").empty();
+}
+
+bool overlayfs_setup(bool enable) {
auto change = false;
errno = 0;
- if (enable ? fs_mgr_overlayfs_teardown(nullptr, &change)
- : fs_mgr_overlayfs_setup(nullptr, nullptr, &change)) {
+ if (enable ? fs_mgr_overlayfs_setup(nullptr, &change)
+ : fs_mgr_overlayfs_teardown(nullptr, &change)) {
if (change) {
- printf("%s overlayfs\n", enable ? "disabling" : "using");
+ LOG(INFO) << (enable ? "Enabled" : "Disabled") << " overlayfs";
}
- } else if (errno) {
- printf("Overlayfs %s failed with error %s\n", enable ? "teardown" : "setup", strerror(errno));
- suggest_run_adb_root();
+ } else {
+ LOG(ERROR) << "Failed to " << (enable ? "enable" : "disable") << " overlayfs";
}
return change;
}
+struct SetVerityStateResult {
+ bool success = false;
+ bool want_reboot = false;
+};
+
/* Use AVB to turn verity on/off */
-static bool set_avb_verity_enabled_state(AvbOps* ops, bool enable_verity) {
+SetVerityStateResult SetVerityState(bool enable_verity) {
std::string ab_suffix = get_ab_suffix();
- bool verity_enabled;
+ bool verity_enabled = false;
if (is_avb_device_locked()) {
- printf("Device is locked. Please unlock the device first\n");
- return false;
+ LOG(ERROR) << "Device must be bootloader unlocked to change verity state";
+ return {};
}
- if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
- printf("Error getting verity state. Try adb root first?\n");
- return false;
+ std::unique_ptr<AvbOps, decltype(&avb_ops_user_free)> ops(avb_ops_user_new(), &avb_ops_user_free);
+ if (!ops) {
+ LOG(ERROR) << "Error getting AVB ops";
+ return {};
+ }
+
+ if (!avb_user_verity_get(ops.get(), ab_suffix.c_str(), &verity_enabled)) {
+ LOG(ERROR) << "Error getting verity state";
+ return {};
}
if ((verity_enabled && enable_verity) || (!verity_enabled && !enable_verity)) {
- printf("verity is already %s\n", verity_enabled ? "enabled" : "disabled");
- return false;
+ LOG(INFO) << "Verity is already " << (verity_enabled ? "enabled" : "disabled");
+ return {.success = true, .want_reboot = false};
}
- if (!avb_user_verity_set(ops, ab_suffix.c_str(), enable_verity)) {
- printf("Error setting verity\n");
- return false;
+ if (!avb_user_verity_set(ops.get(), ab_suffix.c_str(), enable_verity)) {
+ LOG(ERROR) << "Error setting verity state";
+ return {};
}
- overlayfs_setup(enable_verity);
- printf("Successfully %s verity\n", enable_verity ? "enabled" : "disabled");
- return true;
+ LOG(INFO) << "Successfully " << (enable_verity ? "enabled" : "disabled") << " verity";
+ return {.success = true, .want_reboot = true};
}
+void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
+ const char* file, unsigned int line, const char* message) {
+ // Hide log starting with '[fs_mgr]' unless it's an error.
+ if (severity == android::base::ERROR || message[0] != '[') {
+ fprintf(stderr, "%s\n", message);
+ }
+ static auto logd = android::base::LogdLogger();
+ logd(id, severity, tag, file, line, message);
+}
+
+} // namespace
+
int main(int argc, char* argv[]) {
+ android::base::InitLogging(argv, MyLogger);
+
if (argc == 0) {
LOG(FATAL) << "set-verity-state called with empty argv";
}
- std::optional<bool> enable_opt;
+ bool enable_verity = false;
std::string procname = android::base::Basename(argv[0]);
if (procname == "enable-verity") {
- enable_opt = true;
+ enable_verity = true;
} else if (procname == "disable-verity") {
- enable_opt = false;
+ enable_verity = false;
+ } else if (argc == 2 && (argv[1] == "1"s || argv[1] == "0"s)) {
+ enable_verity = (argv[1] == "1"s);
+ } else {
+ printf("usage: %s [1|0]\n", argv[0]);
+ return 1;
}
- if (!enable_opt.has_value()) {
- if (argc != 2) {
- printf("usage: %s [1|0]\n", argv[0]);
- return 1;
- }
-
- if (strcmp(argv[1], "1") == 0) {
- enable_opt = true;
- } else if (strcmp(argv[1], "0") == 0) {
- enable_opt = false;
- } else {
- printf("usage: %s [1|0]\n", argv[0]);
- return 1;
- }
+ if (!kAllowDisableVerity || !is_debuggable()) {
+ errno = EPERM;
+ PLOG(ERROR) << "Cannot disable/enable verity on user build";
+ return 1;
}
- bool enable = enable_opt.value();
-
- bool any_changed = false;
-
- // Figure out if we're using VB1.0 or VB2.0 (aka AVB) - by
- // contract, androidboot.vbmeta.digest is set by the bootloader
- // when using AVB).
- bool using_avb = !android::base::GetProperty("ro.boot.vbmeta.digest", "").empty();
-
- // If using AVB, dm-verity is used on any build so we want it to
- // be possible to disable/enable on any build (except USER). For
- // VB1.0 dm-verity is only enabled on certain builds.
- if (!using_avb) {
- if (!kAllowDisableVerity) {
- printf("%s only works for userdebug builds\n", argv[0]);
- }
-
- if (!android::base::GetBoolProperty("ro.secure", false)) {
- overlayfs_setup(enable);
- printf("verity not enabled - ENG build\n");
- return 0;
- }
+ if (getuid() != 0) {
+ errno = EACCES;
+ PLOG(ERROR) << "Must be running as root (adb root)";
+ return 1;
}
- // Should never be possible to disable dm-verity on a USER build
- // regardless of using AVB or VB1.0.
- if (!__android_log_is_debuggable()) {
- printf("verity cannot be disabled/enabled - USER build\n");
- return 0;
+ if (!is_using_avb()) {
+ LOG(ERROR) << "Expected AVB device, VB1.0 is no longer supported";
+ return 1;
}
- if (using_avb) {
- // Yep, the system is using AVB.
- AvbOps* ops = avb_ops_user_new();
- if (ops == nullptr) {
- printf("Error getting AVB ops\n");
- return 1;
- }
- if (set_avb_verity_enabled_state(ops, enable)) {
- any_changed = true;
- }
- avb_ops_user_free(ops);
- }
- if (!any_changed) any_changed = overlayfs_setup(enable);
+ int exit_code = 0;
+ bool want_reboot = false;
- if (any_changed) {
- printf("Now reboot your device for settings to take effect\n");
+ auto ret = SetVerityState(enable_verity);
+ if (ret.success) {
+ want_reboot |= ret.want_reboot;
+ } else {
+ exit_code = 1;
}
- return 0;
+ // Disable any overlayfs unconditionally if we want verity enabled.
+ // Enable overlayfs only if verity is successfully disabled or is already disabled.
+ if (enable_verity || ret.success) {
+ // Start a threadpool to service waitForService() callbacks as
+ // fs_mgr_overlayfs_* might call waitForService() to get the image service.
+ android::ProcessState::self()->startThreadPool();
+ want_reboot |= overlayfs_setup(!enable_verity);
+ }
+
+ if (want_reboot) {
+ printf("Reboot the device for new settings to take effect\n");
+ }
+
+ return exit_code;
}
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/interface/include/trusty/interface/storage.h b/trusty/storage/interface/include/trusty/interface/storage.h
index 3f1dcb8..255ade1 100644
--- a/trusty/storage/interface/include/trusty/interface/storage.h
+++ b/trusty/storage/interface/include/trusty/interface/storage.h
@@ -70,6 +70,9 @@
* @STORAGE_ERR_TRANSACT returned by various operations to indicate that current transaction
* is in error state. Such state could be only cleared by sending
* STORAGE_END_TRANSACTION message.
+ * @STORAGE_ERR_SYNC_FAILURE indicates that the current operation failed to sync
+ * to disk. Only returned if STORAGE_MSG_FLAG_PRE_COMMIT or
+ * STORAGE_MSG_FLAG_POST_COMMIT was set for the request.
*/
enum storage_err {
STORAGE_NO_ERROR = 0,
@@ -80,6 +83,7 @@
STORAGE_ERR_NOT_FOUND = 5,
STORAGE_ERR_EXIST = 6,
STORAGE_ERR_TRANSACT = 7,
+ STORAGE_ERR_SYNC_FAILURE = 8,
};
/**
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 2620034..b970406 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -70,56 +70,14 @@
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;
- if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) && (msg->cmd != STORAGE_RPMB_SEND)) {
+ if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) && msg->cmd != STORAGE_RPMB_SEND &&
+ msg->cmd != STORAGE_FILE_WRITE) {
/*
- * handling post commit messages on non rpmb commands are not
- * implemented as there is no use case for this yet.
+ * handling post commit messages on commands other than rpmb and write
+ * operations are not implemented as there is no use case for this yet.
*/
ALOGE("cmd 0x%x: post commit option is not implemented\n", msg->cmd);
msg->result = STORAGE_ERR_UNIMPLEMENTED;
@@ -129,7 +87,7 @@
if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT) {
rc = storage_sync_checkpoint();
if (rc < 0) {
- msg->result = STORAGE_ERR_GENERIC;
+ msg->result = STORAGE_ERR_SYNC_FAILURE;
return ipc_respond(msg, NULL, 0);
}
}
@@ -260,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/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
index c00c399..c531cfd 100644
--- a/trusty/storage/proxy/storage.c
+++ b/trusty/storage/proxy/storage.c
@@ -407,6 +407,14 @@
goto err_response;
}
+ if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
+ rc = storage_sync_checkpoint();
+ if (rc < 0) {
+ msg->result = STORAGE_ERR_SYNC_FAILURE;
+ goto err_response;
+ }
+ }
+
msg->result = STORAGE_NO_ERROR;
err_response:
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