Merge "libsnapshot: Add function to verify merge sequence"
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index d6ebb0d..2c878f0 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -439,6 +439,30 @@
{"reboot,forcedsilent", 191},
{"reboot,forcednonsilent", 192},
{"reboot,thermal,tj", 193},
+ {"reboot,emergency", 194},
+ {"reboot,factory", 195},
+ {"reboot,fastboot", 196},
+ {"reboot,gsa,hard", 197},
+ {"reboot,gsa,soft", 198},
+ {"reboot,master_dc,fault_n", 199},
+ {"reboot,master_dc,reset", 200},
+ {"reboot,ocp", 201},
+ {"reboot,pin", 202},
+ {"reboot,rom_recovery", 203},
+ {"reboot,uvlo", 204},
+ {"reboot,uvlo,pmic,if", 205},
+ {"reboot,uvlo,pmic,main", 206},
+ {"reboot,uvlo,pmic,sub", 207},
+ {"reboot,warm", 208},
+ {"watchdog,aoc", 209},
+ {"watchdog,apc", 210},
+ {"watchdog,apc,bl,debug,early", 211},
+ {"watchdog,apc,bl,early", 212},
+ {"watchdog,apc,early", 213},
+ {"watchdog,apm", 214},
+ {"watchdog,gsa,hard", 215},
+ {"watchdog,gsa,soft", 216},
+ {"watchdog,pmucal", 217},
};
// Converts a string value representing the reason the system booted to an
diff --git a/debuggerd/crash_test.cpp b/debuggerd/crash_test.cpp
index c15145f..ce0f91f 100644
--- a/debuggerd/crash_test.cpp
+++ b/debuggerd/crash_test.cpp
@@ -16,6 +16,13 @@
#include <stdint.h>
-extern "C" void crash() {
+#include "crash_test.h"
+
+extern "C" {
+
+JITDescriptor __dex_debug_descriptor = {.version = 1};
+
+void crash() {
*reinterpret_cast<volatile char*>(0xdead) = '1';
}
+}
diff --git a/debuggerd/crash_test.h b/debuggerd/crash_test.h
new file mode 100644
index 0000000..2a8bea3
--- /dev/null
+++ b/debuggerd/crash_test.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+// Only support V1 of these structures.
+// See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html
+// for information on the JIT Compilation Interface.
+// Also, see libunwindstack/GlobalDebugImpl.h for the full definition of
+// these structures.
+struct JITCodeEntry {
+ uintptr_t next;
+ uintptr_t prev;
+ uintptr_t symfile_addr;
+ uint64_t symfile_size;
+};
+
+struct JITDescriptor {
+ uint32_t version;
+ uint32_t action_flag;
+ uintptr_t relevant_entry;
+ uintptr_t first_entry;
+};
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 68c9aca..4394274 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -57,6 +57,7 @@
#include <libminijail.h>
#include <scoped_minijail.h>
+#include "crash_test.h"
#include "debuggerd/handler.h"
#include "libdebuggerd/utility.h"
#include "protocol.h"
@@ -2064,3 +2065,124 @@
match_str = android::base::StringPrintf(R"(\n--->%s.*\n)", format_pointer(ptr).c_str());
ASSERT_MATCH(result, match_str);
}
+
+static constexpr uint32_t kDexData[] = {
+ 0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,
+ 0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,
+ 0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,
+ 0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,
+ 0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,
+ 0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,
+ 0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
+ 0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,
+ 0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,
+ 0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,
+ 0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,
+ 0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,
+ 0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
+ 0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,
+ 0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,
+ 0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,
+ 0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
+};
+
+TEST_F(CrasherTest, verify_dex_pc_with_function_name) {
+ StartProcess([]() {
+ TemporaryDir td;
+ std::string tmp_so_name;
+ if (!CopySharedLibrary(td.path, &tmp_so_name)) {
+ _exit(1);
+ }
+
+ // In order to cause libunwindstack to look for this __dex_debug_descriptor
+ // move the library to which has a basename of libart.so.
+ std::string art_so_name = android::base::Dirname(tmp_so_name) + "/libart.so";
+ ASSERT_EQ(0, rename(tmp_so_name.c_str(), art_so_name.c_str()));
+ void* handle = dlopen(art_so_name.c_str(), RTLD_NOW | RTLD_LOCAL);
+ if (handle == nullptr) {
+ _exit(1);
+ }
+
+ void* ptr =
+ mmap(nullptr, sizeof(kDexData), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ ASSERT_TRUE(ptr != MAP_FAILED);
+ memcpy(ptr, kDexData, sizeof(kDexData));
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, sizeof(kDexData), "dex");
+
+ JITCodeEntry dex_entry = {.symfile_addr = reinterpret_cast<uintptr_t>(ptr),
+ .symfile_size = sizeof(kDexData)};
+
+ JITDescriptor* dex_debug =
+ reinterpret_cast<JITDescriptor*>(dlsym(handle, "__dex_debug_descriptor"));
+ ASSERT_TRUE(dex_debug != nullptr);
+ dex_debug->version = 1;
+ dex_debug->action_flag = 0;
+ dex_debug->relevant_entry = 0;
+ dex_debug->first_entry = reinterpret_cast<uintptr_t>(&dex_entry);
+
+ // This sets the magic dex pc value for register 0, using the value
+ // of register 1 + 0x102.
+ asm(".cfi_escape "
+ "0x16 /* DW_CFA_val_expression */, 0, 0x0a /* size */,"
+ "0x0c /* DW_OP_const4u */, 0x44, 0x45, 0x58, 0x31, /* magic = 'DEX1' */"
+ "0x13 /* DW_OP_drop */,"
+ "0x92 /* DW_OP_bregx */, 1, 0x82, 0x02 /* 2-byte SLEB128 */");
+
+ // For each different architecture, set register one to the dex ptr mmap
+ // created above. Then do a nullptr dereference to force a crash.
+#if defined(__arm__)
+ asm volatile(
+ "mov r1, %[base]\n"
+ "mov r2, 0\n"
+ "str r3, [r2]\n"
+ : [base] "+r"(ptr)
+ :
+ : "r1", "r2", "r3", "memory");
+#elif defined(__aarch64__)
+ asm volatile(
+ "mov x1, %[base]\n"
+ "mov x2, 0\n"
+ "str x3, [x2]\n"
+ : [base] "+r"(ptr)
+ :
+ : "x1", "x2", "x3", "memory");
+#elif defined(__i386__)
+ asm volatile(
+ "mov %[base], %%ecx\n"
+ "movl $0, %%edi\n"
+ "movl 0(%%edi), %%edx\n"
+ : [base] "+r"(ptr)
+ :
+ : "edi", "ecx", "edx", "memory");
+#elif defined(__x86_64__)
+ asm volatile(
+ "mov %[base], %%rdx\n"
+ "movq 0, %%rdi\n"
+ "movq 0(%%rdi), %%rcx\n"
+ : [base] "+r"(ptr)
+ :
+ : "rcx", "rdx", "rdi", "memory");
+#else
+#error "Unsupported architecture"
+#endif
+ _exit(0);
+ });
+
+ unique_fd output_fd;
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+
+ int intercept_result;
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ // Verify the process crashed properly.
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x0)");
+
+ // Now verify that the dex_pc frame includes a proper function name.
+ ASSERT_MATCH(result, R"( \[anon:dex\] \(Main\.\<init\>\+2)");
+}
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 0c93c90..99f636e 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -28,6 +28,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
+#include <sys/sysinfo.h>
#include <time.h>
#include <memory>
@@ -596,14 +597,6 @@
}
}
-static std::optional<uint64_t> read_uptime_secs() {
- std::string uptime;
- if (!android::base::ReadFileToString("/proc/uptime", &uptime)) {
- return {};
- }
- return strtoll(uptime.c_str(), nullptr, 10);
-}
-
void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
const ProcessInfo& process_info, const OpenFilesList* open_files) {
@@ -614,28 +607,24 @@
result.set_revision(android::base::GetProperty("ro.revision", "unknown"));
result.set_timestamp(get_timestamp());
- std::optional<uint64_t> system_uptime = read_uptime_secs();
- if (system_uptime) {
- android::procinfo::ProcessInfo proc_info;
- std::string error;
- if (android::procinfo::GetProcessInfo(target_thread, &proc_info, &error)) {
- uint64_t starttime = proc_info.starttime / sysconf(_SC_CLK_TCK);
- result.set_process_uptime(*system_uptime - starttime);
- } else {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read process info: %s",
- error.c_str());
- }
- } else {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read /proc/uptime: %s",
- strerror(errno));
- }
-
const ThreadInfo& main_thread = threads.at(target_thread);
result.set_pid(main_thread.pid);
result.set_tid(main_thread.tid);
result.set_uid(main_thread.uid);
result.set_selinux_label(main_thread.selinux_label);
+ struct sysinfo si;
+ sysinfo(&si);
+ android::procinfo::ProcessInfo proc_info;
+ std::string error;
+ if (android::procinfo::GetProcessInfo(main_thread.pid, &proc_info, &error)) {
+ uint64_t starttime = proc_info.starttime / sysconf(_SC_CLK_TCK);
+ result.set_process_uptime(si.uptime - starttime);
+ } else {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read process info: %s",
+ error.c_str());
+ }
+
auto cmd_line = result.mutable_command_line();
for (const auto& arg : main_thread.command_line) {
*cmd_line->Add() = arg;
diff --git a/debuggerd/seccomp_policy/crash_dump.arm.policy b/debuggerd/seccomp_policy/crash_dump.arm.policy
index 4eac0e9..8fd03c4 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm.policy
@@ -20,6 +20,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+sysinfo: 1
process_vm_readv: 1
tgkill: 1
rt_sigprocmask: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.arm64.policy b/debuggerd/seccomp_policy/crash_dump.arm64.policy
index 21887ab..858a338 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy
@@ -19,6 +19,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+sysinfo: 1
process_vm_readv: 1
tgkill: 1
rt_sigprocmask: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index 90843fc..152697c 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -25,6 +25,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+sysinfo: 1
process_vm_readv: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86.policy b/debuggerd/seccomp_policy/crash_dump.x86.policy
index 4eac0e9..8fd03c4 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86.policy
@@ -20,6 +20,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+sysinfo: 1
process_vm_readv: 1
tgkill: 1
rt_sigprocmask: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86_64.policy b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
index 1585cc6..281e231 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86_64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
@@ -19,6 +19,7 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
+sysinfo: 1
process_vm_readv: 1
tgkill: 1
rt_sigprocmask: 1
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 7e01b85..332fcf5 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -55,7 +55,33 @@
// that prefix.
std::optional<std::string> ExtractBlockDeviceName(const std::string& path);
-class DeviceMapper final {
+// This interface is for testing purposes. See DeviceMapper proper for what these methods do.
+class IDeviceMapper {
+ public:
+ virtual ~IDeviceMapper() {}
+
+ struct TargetInfo {
+ struct dm_target_spec spec;
+ std::string data;
+ TargetInfo() {}
+ TargetInfo(const struct dm_target_spec& spec, const std::string& data)
+ : spec(spec), data(data) {}
+
+ bool IsOverflowSnapshot() const;
+ };
+
+ virtual bool CreateDevice(const std::string& name, const DmTable& table, std::string* path,
+ const std::chrono::milliseconds& timeout_ms) = 0;
+ virtual DmDeviceState GetState(const std::string& name) const = 0;
+ virtual bool LoadTableAndActivate(const std::string& name, const DmTable& table) = 0;
+ virtual bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) = 0;
+ virtual bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) = 0;
+ virtual bool GetDmDevicePathByName(const std::string& name, std::string* path) = 0;
+ virtual bool GetDeviceString(const std::string& name, std::string* dev) = 0;
+ virtual bool DeleteDeviceIfExists(const std::string& name) = 0;
+};
+
+class DeviceMapper final : public IDeviceMapper {
public:
class DmBlockDevice final {
public:
@@ -95,7 +121,7 @@
// Removes a device mapper device with the given name.
// Returns 'true' on success, false otherwise.
bool DeleteDevice(const std::string& name);
- bool DeleteDeviceIfExists(const std::string& name);
+ bool DeleteDeviceIfExists(const std::string& name) override;
// Removes a device mapper device with the given name and waits for |timeout_ms| milliseconds
// for the corresponding block device to be deleted.
bool DeleteDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms);
@@ -114,7 +140,7 @@
// Returns the current state of the underlying device mapper device
// with given name.
// One of INVALID, SUSPENDED or ACTIVE.
- DmDeviceState GetState(const std::string& name) const;
+ DmDeviceState GetState(const std::string& name) const override;
// Puts the given device to the specified status, which must be either:
// - SUSPENDED: suspend the device, or
@@ -158,7 +184,7 @@
// not |path| is available. It is the caller's responsibility to ensure
// there are no races.
bool CreateDevice(const std::string& name, const DmTable& table, std::string* path,
- const std::chrono::milliseconds& timeout_ms);
+ const std::chrono::milliseconds& timeout_ms) override;
// Create a device and activate the given table, without waiting to acquire
// a valid path. If the caller will use GetDmDevicePathByName(), it should
@@ -170,7 +196,7 @@
// process. A device with the given name must already exist.
//
// Returns 'true' on success, false otherwise.
- bool LoadTableAndActivate(const std::string& name, const DmTable& table);
+ bool LoadTableAndActivate(const std::string& name, const DmTable& table) override;
// Returns true if a list of available device mapper targets registered in the kernel was
// successfully read and stored in 'targets'. Returns 'false' otherwise.
@@ -216,7 +242,7 @@
// Returns a major:minor string for the named device-mapper node, that can
// be used as inputs to DmTargets that take a block device.
- bool GetDeviceString(const std::string& name, std::string* dev);
+ bool GetDeviceString(const std::string& name, std::string* dev) override;
// The only way to create a DeviceMapper object.
static DeviceMapper& Instance();
@@ -231,20 +257,11 @@
// contain one TargetInfo for each target in the table. If the device does
// not exist, or there were too many targets, the call will fail and return
// false.
- struct TargetInfo {
- struct dm_target_spec spec;
- std::string data;
- TargetInfo() {}
- TargetInfo(const struct dm_target_spec& spec, const std::string& data)
- : spec(spec), data(data) {}
-
- bool IsOverflowSnapshot() const;
- };
- bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table);
+ bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) override;
// Identical to GetTableStatus, except also retrives the active table for the device
// mapper device from the kernel.
- bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table);
+ bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) override;
static std::string GetTargetType(const struct dm_target_spec& spec);
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 0cbd9db..a0ad208 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -152,6 +152,7 @@
static_libs: [
"libavb",
"libdm",
+ "libext2_uuid",
"libfs_avb",
"libfstab",
],
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 0288d85..6f874a6 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -779,7 +779,7 @@
nullptr /* out_public_key_data */, &verify_result);
ASSERT_EQ(0, close(hash_modified_fd.release()));
EXPECT_NE(nullptr, vbmeta);
- EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+ // EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); // b/187303962.
EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
// Modifies the auxiliary data block.
@@ -795,7 +795,7 @@
nullptr /* out_public_key_data */, &verify_result);
ASSERT_EQ(0, close(aux_modified_fd.release()));
EXPECT_NE(nullptr, vbmeta);
- EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+ // EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); // b/187303962.
EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
// Resets previous modification by setting offset to -1, and checks the verification can pass.
@@ -807,7 +807,7 @@
nullptr /* out_public_key_data */, &verify_result);
ASSERT_EQ(0, close(ok_fd.release()));
EXPECT_NE(nullptr, vbmeta);
- EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+ // EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); // b/187303962.
EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
}
diff --git a/fs_mgr/liblp/OWNERS b/fs_mgr/liblp/OWNERS
new file mode 100644
index 0000000..6a95eb2
--- /dev/null
+++ b/fs_mgr/liblp/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 391836
+dvander@google.com
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
index 14ce0ee..a6d96ed 100644
--- a/fs_mgr/libsnapshot/device_info.cpp
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -139,5 +139,9 @@
}
}
+android::dm::IDeviceMapper& DeviceInfo::GetDeviceMapper() {
+ return android::dm::DeviceMapper::Instance();
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h
index 7999c99..8aefb85 100644
--- a/fs_mgr/libsnapshot/device_info.h
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -40,6 +40,7 @@
bool IsRecovery() const override;
std::unique_ptr<IImageManager> OpenImageManager() const override;
bool IsFirstStageInit() const override;
+ android::dm::IDeviceMapper& GetDeviceMapper() override;
void set_first_stage_init(bool value) { first_stage_init_ = value; }
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index d9a0cd9..55f4ed7 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -110,6 +110,7 @@
virtual bool IsTestDevice() const { return false; }
virtual bool IsFirstStageInit() const = 0;
virtual std::unique_ptr<IImageManager> OpenImageManager() const = 0;
+ virtual android::dm::IDeviceMapper& GetDeviceMapper() = 0;
// Helper method for implementing OpenImageManager.
std::unique_ptr<IImageManager> OpenImageManager(const std::string& gsid_dir) const;
@@ -611,6 +612,14 @@
MergeFailureCode CheckMergeConsistency(LockedFile* lock, const std::string& name,
const SnapshotStatus& update_status);
+ // Get status or table information about a device-mapper node with a single target.
+ enum class TableQuery {
+ Table,
+ Status,
+ };
+ bool GetSingleTarget(const std::string& dm_name, TableQuery query,
+ android::dm::DeviceMapper::TargetInfo* target);
+
// Interact with status files under /metadata/ota/snapshots.
bool WriteSnapshotStatus(LockedFile* lock, const SnapshotStatus& status);
bool ReadSnapshotStatus(LockedFile* lock, const std::string& name, SnapshotStatus* status);
@@ -773,9 +782,9 @@
bool DeleteDeviceIfExists(const std::string& name,
const std::chrono::milliseconds& timeout_ms = {});
- std::string gsid_dir_;
- std::string metadata_dir_;
+ android::dm::IDeviceMapper& dm_;
std::unique_ptr<IDeviceInfo> device_;
+ std::string metadata_dir_;
std::unique_ptr<IImageManager> images_;
bool use_first_stage_snapuserd_ = false;
bool in_factory_data_reset_ = false;
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 4e7ccf1..1f57bbc 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -99,6 +99,9 @@
std::unique_ptr<IImageManager> OpenImageManager() const override {
return IDeviceInfo::OpenImageManager("ota/test");
}
+ android::dm::IDeviceMapper& GetDeviceMapper() override {
+ return android::dm::DeviceMapper::Instance();
+ }
bool IsSlotUnbootable(uint32_t slot) { return unbootable_slots_.count(slot) != 0; }
diff --git a/fs_mgr/libsnapshot/scripts/Android.bp b/fs_mgr/libsnapshot/scripts/Android.bp
index 3e09cc3..829f5bc 100644
--- a/fs_mgr/libsnapshot/scripts/Android.bp
+++ b/fs_mgr/libsnapshot/scripts/Android.bp
@@ -14,6 +14,11 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
python_binary_host {
name: "dump_snapshot_proto",
main: "dump_snapshot_proto.py",
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 52324ba..3d8ae29 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -114,9 +114,8 @@
return sm;
}
-SnapshotManager::SnapshotManager(IDeviceInfo* device) : device_(device) {
- metadata_dir_ = device_->GetMetadataDir();
-}
+SnapshotManager::SnapshotManager(IDeviceInfo* device)
+ : dm_(device->GetDeviceMapper()), device_(device), metadata_dir_(device_->GetMetadataDir()) {}
static std::string GetCowName(const std::string& snapshot_name) {
return snapshot_name + "-cow";
@@ -402,8 +401,6 @@
const std::chrono::milliseconds& timeout_ms, std::string* path) {
CHECK(lock);
- auto& dm = DeviceMapper::Instance();
-
// Use an extra decoration for first-stage init, so we can transition
// to a new table entry in second-stage.
std::string misc_name = name;
@@ -423,7 +420,7 @@
DmTable table;
table.Emplace<DmTargetUser>(0, base_sectors, misc_name);
- if (!dm.CreateDevice(name, table, path, timeout_ms)) {
+ if (!dm_.CreateDevice(name, table, path, timeout_ms)) {
return false;
}
if (!WaitForDevice(*path, timeout_ms)) {
@@ -490,8 +487,6 @@
uint64_t snapshot_sectors = status.snapshot_size() / kSectorSize;
- auto& dm = DeviceMapper::Instance();
-
// Note that merging is a global state. We do track whether individual devices
// have completed merging, but the start of the merge process is considered
// atomic.
@@ -518,10 +513,17 @@
break;
}
+ if (mode == SnapshotStorageMode::Persistent && status.state() == SnapshotState::MERGING) {
+ LOG(ERROR) << "Snapshot: " << name
+ << " has snapshot status Merging but mode set to Persistent."
+ << " Changing mode to Snapshot-Merge.";
+ mode = SnapshotStorageMode::Merge;
+ }
+
DmTable table;
table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_device, mode,
kSnapshotChunkSize);
- if (!dm.CreateDevice(name, table, dev_path, timeout_ms)) {
+ if (!dm_.CreateDevice(name, table, dev_path, timeout_ms)) {
LOG(ERROR) << "Could not create snapshot device: " << name;
return false;
}
@@ -652,7 +654,6 @@
auto other_suffix = device_->GetOtherSlotSuffix();
- auto& dm = DeviceMapper::Instance();
for (const auto& snapshot : snapshots) {
if (android::base::EndsWith(snapshot, other_suffix)) {
// Allow the merge to continue, but log this unexpected case.
@@ -664,7 +665,7 @@
// the same time. This is a fairly serious error. We could forcefully
// map everything here, but it should have been mapped during first-
// stage init.
- if (dm.GetState(snapshot) == DmDeviceState::INVALID) {
+ if (dm_.GetState(snapshot) == DmDeviceState::INVALID) {
LOG(ERROR) << "Cannot begin merge; device " << snapshot << " is not mapped.";
return false;
}
@@ -797,10 +798,8 @@
}
MergeFailureCode SnapshotManager::RewriteSnapshotDeviceTable(const std::string& name) {
- auto& dm = DeviceMapper::Instance();
-
std::vector<DeviceMapper::TargetInfo> old_targets;
- if (!dm.GetTableInfo(name, &old_targets)) {
+ if (!dm_.GetTableInfo(name, &old_targets)) {
LOG(ERROR) << "Could not read snapshot device table: " << name;
return MergeFailureCode::GetTableInfo;
}
@@ -818,7 +817,7 @@
DmTable table;
table.Emplace<DmTargetSnapshot>(0, old_targets[0].spec.length, base_device, cow_device,
SnapshotStorageMode::Merge, kSnapshotChunkSize);
- if (!dm.LoadTableAndActivate(name, table)) {
+ if (!dm_.LoadTableAndActivate(name, table)) {
LOG(ERROR) << "Could not swap device-mapper tables on snapshot device " << name;
return MergeFailureCode::ActivateNewTable;
}
@@ -826,24 +825,18 @@
return MergeFailureCode::Ok;
}
-enum class TableQuery {
- Table,
- Status,
-};
-
-static bool GetSingleTarget(const std::string& dm_name, TableQuery query,
- DeviceMapper::TargetInfo* target) {
- auto& dm = DeviceMapper::Instance();
- if (dm.GetState(dm_name) == DmDeviceState::INVALID) {
+bool SnapshotManager::GetSingleTarget(const std::string& dm_name, TableQuery query,
+ DeviceMapper::TargetInfo* target) {
+ if (dm_.GetState(dm_name) == DmDeviceState::INVALID) {
return false;
}
std::vector<DeviceMapper::TargetInfo> targets;
bool result;
if (query == TableQuery::Status) {
- result = dm.GetTableStatus(dm_name, &targets);
+ result = dm_.GetTableStatus(dm_name, &targets);
} else {
- result = dm.GetTableInfo(dm_name, &targets);
+ result = dm_.GetTableInfo(dm_name, &targets);
}
if (!result) {
LOG(ERROR) << "Could not query device: " << dm_name;
@@ -886,6 +879,10 @@
if (target_type) {
*target_type = DeviceMapper::GetTargetType(target.spec);
}
+ if (!status->error.empty()) {
+ LOG(ERROR) << "Snapshot: " << dm_name << " returned error code: " << status->error;
+ return false;
+ }
return true;
}
@@ -1169,11 +1166,9 @@
return MergeFailureCode::Ok;
}
- auto& dm = DeviceMapper::Instance();
-
std::string cow_image_name = GetMappedCowDeviceName(name, status);
std::string cow_image_path;
- if (!dm.GetDmDevicePathByName(cow_image_name, &cow_image_path)) {
+ if (!dm_.GetDmDevicePathByName(cow_image_name, &cow_image_path)) {
LOG(ERROR) << "Failed to get path for cow device: " << cow_image_name;
return MergeFailureCode::GetCowPathConsistencyCheck;
}
@@ -1349,8 +1344,6 @@
bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
const SnapshotStatus& status) {
- auto& dm = DeviceMapper::Instance();
-
// Verify we have a snapshot-merge device.
DeviceMapper::TargetInfo target;
if (!GetSingleTarget(name, TableQuery::Table, &target)) {
@@ -1389,7 +1382,7 @@
return false;
}
- if (!dm.LoadTableAndActivate(name, table)) {
+ if (!dm_.LoadTableAndActivate(name, table)) {
return false;
}
@@ -1452,7 +1445,7 @@
std::vector<std::string>* snapuserd_argv) {
LOG(INFO) << "Performing transition for snapuserd.";
- // Don't use EnsuerSnapuserdConnected() because this is called from init,
+ // Don't use EnsureSnapuserdConnected() because this is called from init,
// and attempting to do so will deadlock.
if (!snapuserd_client_ && transition != InitTransition::SELINUX_DETACH) {
snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
@@ -1462,8 +1455,6 @@
}
}
- auto& dm = DeviceMapper::Instance();
-
auto lock = LockExclusive();
if (!lock) return false;
@@ -1477,7 +1468,7 @@
size_t ok_cows = 0;
for (const auto& snapshot : snapshots) {
std::string user_cow_name = GetDmUserCowName(snapshot);
- if (dm.GetState(user_cow_name) == DmDeviceState::INVALID) {
+ if (dm_.GetState(user_cow_name) == DmDeviceState::INVALID) {
continue;
}
@@ -1504,13 +1495,20 @@
DmTable table;
table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);
- if (!dm.LoadTableAndActivate(user_cow_name, table)) {
+ if (!dm_.LoadTableAndActivate(user_cow_name, table)) {
LOG(ERROR) << "Unable to swap tables for " << misc_name;
continue;
}
+ std::string source_device_name;
+ if (snapshot_status.old_partition_size() > 0) {
+ source_device_name = GetSourceDeviceName(snapshot);
+ } else {
+ source_device_name = GetBaseDeviceName(snapshot);
+ }
+
std::string source_device;
- if (!dm.GetDmDevicePathByName(GetSourceDeviceName(snapshot), &source_device)) {
+ if (!dm_.GetDmDevicePathByName(source_device_name, &source_device)) {
LOG(ERROR) << "Could not get device path for " << GetSourceDeviceName(snapshot);
continue;
}
@@ -1518,7 +1516,7 @@
std::string cow_image_name = GetMappedCowDeviceName(snapshot, snapshot_status);
std::string cow_image_device;
- if (!dm.GetDmDevicePathByName(cow_image_name, &cow_image_device)) {
+ if (!dm_.GetDmDevicePathByName(cow_image_name, &cow_image_device)) {
LOG(ERROR) << "Could not get device path for " << cow_image_name;
continue;
}
@@ -1693,8 +1691,7 @@
// snapshot, but it's on the wrong slot. We can't unmap an active
// partition. If this is not really a snapshot, skip the unmap
// step.
- auto& dm = DeviceMapper::Instance();
- if (dm.GetState(name) == DmDeviceState::INVALID || !IsSnapshotDevice(name)) {
+ if (dm_.GetState(name) == DmDeviceState::INVALID || !IsSnapshotDevice(name)) {
LOG(ERROR) << "Detected snapshot " << name << " on " << current_slot << " slot"
<< " for source partition; removing without unmap.";
should_unmap = false;
@@ -2031,14 +2028,13 @@
// Create the base device for the snapshot, or if there is no snapshot, the
// device itself. This device consists of the real blocks in the super
// partition that this logical partition occupies.
- auto& dm = DeviceMapper::Instance();
std::string base_path;
if (!CreateLogicalPartition(params, &base_path)) {
LOG(ERROR) << "Could not create logical partition " << params.GetPartitionName()
<< " as device " << params.GetDeviceName();
return false;
}
- created_devices.EmplaceBack<AutoUnmapDevice>(&dm, params.GetDeviceName());
+ created_devices.EmplaceBack<AutoUnmapDevice>(&dm_, params.GetDeviceName());
if (paths) {
paths->target_device = base_path;
@@ -2052,7 +2048,7 @@
// We don't have ueventd in first-stage init, so use device major:minor
// strings instead.
std::string base_device;
- if (!dm.GetDeviceString(params.GetDeviceName(), &base_device)) {
+ if (!dm_.GetDeviceString(params.GetDeviceName(), &base_device)) {
LOG(ERROR) << "Could not determine major/minor for: " << params.GetDeviceName();
return false;
}
@@ -2095,7 +2091,7 @@
}
auto source_device = GetSourceDeviceName(params.GetPartitionName());
- created_devices.EmplaceBack<AutoUnmapDevice>(&dm, source_device);
+ created_devices.EmplaceBack<AutoUnmapDevice>(&dm_, source_device);
} else {
source_device_path = base_path;
}
@@ -2122,7 +2118,7 @@
<< params.GetPartitionName();
return false;
}
- created_devices.EmplaceBack<AutoUnmapDevice>(&dm, name);
+ created_devices.EmplaceBack<AutoUnmapDevice>(&dm_, name);
remaining_time = GetRemainingTime(params.timeout_ms, begin);
if (remaining_time.count() < 0) return false;
@@ -2188,8 +2184,6 @@
std::string cow_image_name = GetCowImageDeviceName(partition_name);
*cow_name = GetCowName(partition_name);
- auto& dm = DeviceMapper::Instance();
-
// Map COW image if necessary.
if (snapshot_status.cow_file_size() > 0) {
if (!EnsureImageManager()) return false;
@@ -2240,11 +2234,11 @@
// We have created the DmTable now. Map it.
std::string cow_path;
- if (!dm.CreateDevice(*cow_name, table, &cow_path, remaining_time)) {
+ if (!dm_.CreateDevice(*cow_name, table, &cow_path, remaining_time)) {
LOG(ERROR) << "Could not create COW device: " << *cow_name;
return false;
}
- created_devices->EmplaceBack<AutoUnmapDevice>(&dm, *cow_name);
+ created_devices->EmplaceBack<AutoUnmapDevice>(&dm_, *cow_name);
LOG(INFO) << "Mapped COW device for " << params.GetPartitionName() << " at " << cow_path;
return true;
}
@@ -2271,10 +2265,8 @@
}
bool SnapshotManager::UnmapDmUserDevice(const std::string& snapshot_name) {
- auto& dm = DeviceMapper::Instance();
-
auto dm_user_name = GetDmUserCowName(snapshot_name);
- if (dm.GetState(dm_user_name) == DmDeviceState::INVALID) {
+ if (dm_.GetState(dm_user_name) == DmDeviceState::INVALID) {
return true;
}
@@ -3494,7 +3486,6 @@
return false;
}
- auto& dm = DeviceMapper::Instance();
for (const auto& snapshot : snapshots) {
SnapshotStatus status;
if (!ReadSnapshotStatus(lock, snapshot, &status)) {
@@ -3505,7 +3496,7 @@
}
std::vector<DeviceMapper::TargetInfo> targets;
- if (!dm.GetTableStatus(snapshot, &targets)) {
+ if (!dm_.GetTableStatus(snapshot, &targets)) {
LOG(ERROR) << "Could not read snapshot device table: " << snapshot;
return false;
}
@@ -3598,11 +3589,9 @@
// isn't running yet.
bool SnapshotManager::GetMappedImageDevicePath(const std::string& device_name,
std::string* device_path) {
- auto& dm = DeviceMapper::Instance();
-
// Try getting the device string if it is a device mapper device.
- if (dm.GetState(device_name) != DmDeviceState::INVALID) {
- return dm.GetDmDevicePathByName(device_name, device_path);
+ if (dm_.GetState(device_name) != DmDeviceState::INVALID) {
+ return dm_.GetDmDevicePathByName(device_name, device_path);
}
// Otherwise, get path from IImageManager.
@@ -3611,10 +3600,9 @@
bool SnapshotManager::GetMappedImageDeviceStringOrPath(const std::string& device_name,
std::string* device_string_or_mapped_path) {
- auto& dm = DeviceMapper::Instance();
// Try getting the device string if it is a device mapper device.
- if (dm.GetState(device_name) != DmDeviceState::INVALID) {
- return dm.GetDeviceString(device_name, device_string_or_mapped_path);
+ if (dm_.GetState(device_name) != DmDeviceState::INVALID) {
+ return dm_.GetDeviceString(device_name, device_string_or_mapped_path);
}
// Otherwise, get path from IImageManager.
@@ -3730,10 +3718,9 @@
bool SnapshotManager::DeleteDeviceIfExists(const std::string& name,
const std::chrono::milliseconds& timeout_ms) {
- auto& dm = DeviceMapper::Instance();
auto start = std::chrono::steady_clock::now();
while (true) {
- if (dm.DeleteDeviceIfExists(name)) {
+ if (dm_.DeleteDeviceIfExists(name)) {
return true;
}
auto now = std::chrono::steady_clock::now();
@@ -3746,7 +3733,7 @@
// Try to diagnose why this failed. First get the actual device path.
std::string full_path;
- if (!dm.GetDmDevicePathByName(name, &full_path)) {
+ if (!dm_.GetDmDevicePathByName(name, &full_path)) {
LOG(ERROR) << "Unable to diagnose DM_DEV_REMOVE failure.";
return false;
}
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index 3ed27c8..c1a5af7 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -101,7 +101,8 @@
: env_(env),
data_(&data),
partition_opener_(std::move(partition_opener)),
- metadata_dir_(metadata_dir) {}
+ metadata_dir_(metadata_dir),
+ dm_(android::dm::DeviceMapper::Instance()) {}
// Following APIs are mocked.
std::string GetMetadataDir() const override { return metadata_dir_; }
@@ -125,6 +126,7 @@
}
bool IsRecovery() const override { return data_->is_recovery(); }
bool IsFirstStageInit() const override { return false; }
+ android::dm::IDeviceMapper& GetDeviceMapper() override { return dm_; }
std::unique_ptr<IImageManager> OpenImageManager() const {
return env_->CheckCreateFakeImageManager();
}
@@ -137,6 +139,7 @@
std::unique_ptr<TestPartitionOpener> partition_opener_;
std::string metadata_dir_;
bool switched_slot_ = false;
+ android::dm::DeviceMapper& dm_;
bool CurrentSlotIsA() const { return data_->slot_suffix_is_a() != switched_slot_; }
};
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 057e5b19a..43c7fe2 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -2118,14 +2118,24 @@
// After reboot, init does first stage mount.
auto init = NewManagerForFirstStageMount("_b");
ASSERT_NE(init, nullptr);
+
+ ASSERT_TRUE(init->EnsureSnapuserdConnected());
+ init->set_use_first_stage_snapuserd(true);
+
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
// Check that the target partitions have the same content.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
+ std::vector<std::string> partitions = {"sys_b", "vnd_b", "prd_b", "dlkm_b"};
+ for (const auto& name : partitions) {
ASSERT_TRUE(IsPartitionUnchanged(name));
}
+ ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));
+ for (const auto& name : partitions) {
+ ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete(name + "-user-cow-init"));
+ }
+
// Initiate the merge and wait for it to be completed.
ASSERT_TRUE(init->InitiateMerge());
ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
diff --git a/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
index f4aef44..bff0a50 100644
--- a/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
@@ -108,6 +108,7 @@
void MergeInterruptFixed(int duration);
void MergeInterruptRandomly(int max_duration);
void ReadDmUserBlockWithoutDaemon();
+ void ReadLastBlock();
std::string snapshot_dev() const { return snapshot_dev_->path(); }
@@ -256,6 +257,73 @@
}
}
+void CowSnapuserdTest::ReadLastBlock() {
+ unique_fd rnd_fd;
+ total_base_size_ = BLOCK_SZ * 2;
+
+ base_fd_ = CreateTempFile("base_device", total_base_size_);
+ ASSERT_GE(base_fd_, 0);
+
+ rnd_fd.reset(open("/dev/random", O_RDONLY));
+ ASSERT_TRUE(rnd_fd > 0);
+
+ std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(BLOCK_SZ);
+
+ for (size_t j = 0; j < ((total_base_size_) / BLOCK_SZ); j++) {
+ ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), BLOCK_SZ, 0), true);
+ ASSERT_EQ(android::base::WriteFully(base_fd_, random_buffer.get(), BLOCK_SZ), true);
+ }
+
+ ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0);
+
+ base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
+ ASSERT_TRUE(base_loop_->valid());
+
+ std::string path = android::base::GetExecutableDirectory();
+ cow_system_ = std::make_unique<TemporaryFile>(path);
+
+ std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(total_base_size_);
+ loff_t offset = 0;
+
+ // Fill random data
+ for (size_t j = 0; j < (total_base_size_ / BLOCK_SZ); j++) {
+ ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, BLOCK_SZ, 0),
+ true);
+
+ offset += BLOCK_SZ;
+ }
+
+ CowOptions options;
+ options.compression = "gz";
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+
+ ASSERT_TRUE(writer.AddRawBlocks(0, random_buffer_1_.get(), BLOCK_SZ));
+ ASSERT_TRUE(writer.AddRawBlocks(1, (char*)random_buffer_1_.get() + BLOCK_SZ, BLOCK_SZ));
+
+ ASSERT_TRUE(writer.Finalize());
+
+ SetDeviceControlName();
+
+ StartSnapuserdDaemon();
+ InitCowDevice();
+
+ CreateDmUserDevice();
+ InitDaemon();
+
+ CreateSnapshotDevice();
+
+ unique_fd snapshot_fd(open(snapshot_dev_->path().c_str(), O_RDONLY));
+ ASSERT_TRUE(snapshot_fd > 0);
+
+ std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(BLOCK_SZ);
+
+ offset = 7680;
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), 512, offset), true);
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)random_buffer_1_.get() + offset, 512), 0);
+}
+
void CowSnapuserdTest::CreateBaseDevice() {
unique_fd rnd_fd;
@@ -1143,6 +1211,12 @@
harness.Shutdown();
}
+TEST(Snapuserd_Test, Snapshot_END_IO_TEST) {
+ CowSnapuserdTest harness;
+ harness.ReadLastBlock();
+ harness.Shutdown();
+}
+
TEST(Snapuserd_Test, Snapshot_COPY_Overlap_TEST_1) {
CowSnapuserdTest harness;
ASSERT_TRUE(harness.SetupCopyOverlap_1());
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
index b868eed..3bb7a0a 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
@@ -194,10 +194,12 @@
std::vector<uint64_t>& blocks) {
int num_ops = *pending_ops;
int nr_consecutive = 0;
+ CHECK_NE(source_offset, nullptr);
if (!RAIterDone() && num_ops) {
// Get the first block with offset
const CowOperation* cow_op = GetRAOpIter();
+ CHECK_NE(cow_op, nullptr);
*source_offset = cow_op->source;
if (cow_op->type == kCowCopyOp) {
*source_offset *= BLOCK_SZ;
@@ -216,11 +218,12 @@
*/
while (!RAIterDone() && num_ops) {
const CowOperation* op = GetRAOpIter();
+ CHECK_NE(op, nullptr);
uint64_t next_offset = op->source;
- if (cow_op->type == kCowCopyOp) {
+ if (op->type == kCowCopyOp) {
next_offset *= BLOCK_SZ;
}
- if (next_offset != (*source_offset - nr_consecutive * BLOCK_SZ)) {
+ if (next_offset + nr_consecutive * BLOCK_SZ != *source_offset) {
break;
}
nr_consecutive += 1;
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
index cdf9fe7..5d184ad 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
@@ -350,16 +350,36 @@
it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
Snapuserd::compare);
- if (!(it != chunk_vec.end())) {
- SNAP_LOG(ERROR) << "ReadData: Sector " << sector << " not found in chunk_vec";
- return -1;
+ bool read_end_of_device = false;
+ if (it == chunk_vec.end()) {
+ // |-------|-------|-------|
+ // 0 1 2 3
+ //
+ // Block 0 - op 1
+ // Block 1 - op 2
+ // Block 2 - op 3
+ //
+ // chunk_vec will have block 0, 1, 2 which maps to relavant COW ops.
+ //
+ // Each block is 4k bytes. Thus, the last block will span 8 sectors
+ // ranging till block 3 (However, block 3 won't be in chunk_vec as
+ // it doesn't have any mapping to COW ops. Now, if we get an I/O request for a sector
+ // spanning between block 2 and block 3, we need to step back
+ // and get hold of the last element.
+ //
+ // Additionally, dm-snapshot makes sure that I/O request beyond block 3
+ // will not be routed to the daemon. Hence, it is safe to assume that
+ // if a sector is not available in the chunk_vec, the I/O falls in the
+ // end of region.
+ it = std::prev(chunk_vec.end());
+ read_end_of_device = true;
}
// We didn't find the required sector; hence find the previous sector
// as lower_bound will gives us the value greater than
// the requested sector
if (it->first != sector) {
- if (it != chunk_vec.begin()) {
+ if (it != chunk_vec.begin() && !read_end_of_device) {
--it;
}
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 671de9d..e97afed 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -57,14 +57,14 @@
// Automatically unmap a device upon deletion.
struct AutoUnmapDevice : AutoDevice {
// On destruct, delete |name| from device mapper.
- AutoUnmapDevice(android::dm::DeviceMapper* dm, const std::string& name)
+ AutoUnmapDevice(android::dm::IDeviceMapper* dm, const std::string& name)
: AutoDevice(name), dm_(dm) {}
AutoUnmapDevice(AutoUnmapDevice&& other) = default;
~AutoUnmapDevice();
private:
DISALLOW_COPY_AND_ASSIGN(AutoUnmapDevice);
- android::dm::DeviceMapper* dm_ = nullptr;
+ android::dm::IDeviceMapper* dm_ = nullptr;
};
// Automatically unmap an image upon deletion.
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index 50eee19..9a47f6b 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -46,14 +46,6 @@
HealthdDraw::HealthdDraw(animation* anim)
: kSplitScreen(get_split_screen()), kSplitOffset(get_split_offset()) {
- int ret = gr_init();
-
- if (ret < 0) {
- LOGE("gr_init failed\n");
- graphics_available = false;
- return;
- }
-
graphics_available = true;
sys_font = gr_sys_font();
if (sys_font == nullptr) {
@@ -235,3 +227,11 @@
LOGW("Charging, level unknown\n");
}
}
+
+std::unique_ptr<HealthdDraw> HealthdDraw::Create(animation *anim) {
+ if (gr_init() < 0) {
+ LOGE("gr_init failed\n");
+ return nullptr;
+ }
+ return std::unique_ptr<HealthdDraw>(new HealthdDraw(anim));
+}
diff --git a/healthd/healthd_draw.h b/healthd/healthd_draw.h
index 7c847bd..0b48ce8 100644
--- a/healthd/healthd_draw.h
+++ b/healthd/healthd_draw.h
@@ -26,8 +26,6 @@
class HealthdDraw {
public:
- // Configures font using given animation.
- HealthdDraw(animation* anim);
virtual ~HealthdDraw();
// Redraws screen.
@@ -36,6 +34,8 @@
// Blanks screen if true, unblanks if false.
virtual void blank_screen(bool blank);
+ static std::unique_ptr<HealthdDraw> Create(animation *anim);
+
protected:
virtual void clear_screen();
@@ -76,6 +76,10 @@
// true if minui init'ed OK, false if minui init failed
bool graphics_available;
+
+ private:
+ // Configures font using given animation.
+ HealthdDraw(animation* anim);
};
#endif // HEALTHD_DRAW_H
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index e95efc0..3ea90b0 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -218,9 +218,7 @@
char* ptr;
size_t len;
- LOGW("\n");
LOGW("*************** LAST KMSG ***************\n");
- LOGW("\n");
const char* kmsg[] = {
// clang-format off
"/sys/fs/pstore/console-ramoops-0",
@@ -263,9 +261,7 @@
}
out:
- LOGW("\n");
LOGW("************* END LAST KMSG *************\n");
- LOGW("\n");
}
static int request_suspend(bool enable) {
@@ -325,7 +321,8 @@
}
}
- healthd_draw_.reset(new HealthdDraw(&batt_anim_));
+ healthd_draw_ = HealthdDraw::Create(&batt_anim_);
+ if (healthd_draw_ == nullptr) return;
if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
healthd_draw_->blank_screen(true);
diff --git a/init/Android.bp b/init/Android.bp
index 5d09687..a0fe017 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -89,7 +89,19 @@
"host_init_verifier.cpp",
]
-cc_defaults {
+soong_config_module_type {
+ name: "libinit_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "ANDROID",
+ bool_variables: [
+ "PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT",
+ ],
+ properties: [
+ "cflags",
+ ],
+}
+
+libinit_cc_defaults {
name: "init_defaults",
sanitize: {
misc_undefined: ["signed-integer-overflow"],
@@ -109,6 +121,7 @@
"-DDUMP_ON_UMOUNT_FAILURE=0",
"-DSHUTDOWN_ZERO_TIMEOUT=0",
"-DINIT_FULL_SOURCES",
+ "-DINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT=0",
],
product_variables: {
debuggable: {
@@ -137,6 +150,14 @@
cppflags: ["-DUSER_MODE_LINUX"],
},
},
+ soong_config_variables: {
+ PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT: {
+ cflags: [
+ "-UINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT",
+ "-DINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT=1",
+ ],
+ },
+ },
static_libs: [
"libavb",
"libc++fs",
diff --git a/init/README.md b/init/README.md
index f447ab2..58a8d6b 100644
--- a/init/README.md
+++ b/init/README.md
@@ -693,7 +693,7 @@
fstab.${ro.hardware} or fstab.${ro.hardware.platform} will be scanned for
under /odm/etc, /vendor/etc, or / at runtime, in that order.
-`verity_update_state <mount-point>`
+`verity_update_state`
> Internal implementation detail used to update dm-verity state and
set the partition._mount-point_.verified properties used by adb remount
because fs\_mgr can't set them directly itself.
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 78e5b60..c7b7b0c 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -330,14 +330,21 @@
// If "/force_debuggable" is present, the second-stage init will use a userdebug
// sepolicy and load adb_debug.prop to allow adb root, if the device is unlocked.
if (access("/force_debuggable", F_OK) == 0) {
+ constexpr const char adb_debug_prop_src[] = "/adb_debug.prop";
+ constexpr const char userdebug_plat_sepolicy_cil_src[] = "/userdebug_plat_sepolicy.cil";
std::error_code ec; // to invoke the overloaded copy_file() that won't throw.
- if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) ||
- !fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) {
- LOG(ERROR) << "Failed to setup debug ramdisk";
- } else {
- // setenv for second-stage init to read above kDebugRamdisk* files.
- setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
+ if (access(adb_debug_prop_src, F_OK) == 0 &&
+ !fs::copy_file(adb_debug_prop_src, kDebugRamdiskProp, ec)) {
+ LOG(WARNING) << "Can't copy " << adb_debug_prop_src << " to " << kDebugRamdiskProp
+ << ": " << ec.message();
}
+ if (access(userdebug_plat_sepolicy_cil_src, F_OK) == 0 &&
+ !fs::copy_file(userdebug_plat_sepolicy_cil_src, kDebugRamdiskSEPolicy, ec)) {
+ LOG(WARNING) << "Can't copy " << userdebug_plat_sepolicy_cil_src << " to "
+ << kDebugRamdiskSEPolicy << ": " << ec.message();
+ }
+ // setenv for second-stage init to read above kDebugRamdisk* files.
+ setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
}
if (ForceNormalBoot(cmdline, bootconfig)) {
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 42d3023..29c0ff3 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -295,6 +295,25 @@
return access(plat_policy_cil_file, R_OK) != -1;
}
+std::optional<const char*> GetUserdebugPlatformPolicyFile() {
+ // See if we need to load userdebug_plat_sepolicy.cil instead of plat_sepolicy.cil.
+ const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
+ if (force_debuggable_env && "true"s == force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
+ const std::vector<const char*> debug_policy_candidates = {
+#if INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT == 1
+ "/system_ext/etc/selinux/userdebug_plat_sepolicy.cil",
+#endif
+ kDebugRamdiskSEPolicy,
+ };
+ for (const char* debug_policy : debug_policy_candidates) {
+ if (access(debug_policy, F_OK) == 0) {
+ return debug_policy;
+ }
+ }
+ }
+ return std::nullopt;
+}
+
struct PolicyFile {
unique_fd fd;
std::string path;
@@ -310,13 +329,10 @@
// secilc is invoked to compile the above three policy files into a single monolithic policy
// file. This file is then loaded into the kernel.
- // See if we need to load userdebug_plat_sepolicy.cil instead of plat_sepolicy.cil.
- const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
- bool use_userdebug_policy =
- ((force_debuggable_env && "true"s == force_debuggable_env) &&
- AvbHandle::IsDeviceUnlocked() && access(kDebugRamdiskSEPolicy, F_OK) == 0);
+ const auto userdebug_plat_sepolicy = GetUserdebugPlatformPolicyFile();
+ const bool use_userdebug_policy = userdebug_plat_sepolicy.has_value();
if (use_userdebug_policy) {
- LOG(WARNING) << "Using userdebug system sepolicy";
+ LOG(INFO) << "Using userdebug system sepolicy " << *userdebug_plat_sepolicy;
}
// Load precompiled policy from vendor image, if a matching policy is found there. The policy
@@ -413,7 +429,7 @@
// clang-format off
std::vector<const char*> compile_args {
"/system/bin/secilc",
- use_userdebug_policy ? kDebugRamdiskSEPolicy: plat_policy_cil_file,
+ use_userdebug_policy ? *userdebug_plat_sepolicy : plat_policy_cil_file,
"-m", "-M", "true", "-G", "-N",
"-c", version_as_string.c_str(),
plat_mapping_file.c_str(),
diff --git a/libprocessgroup/cgrouprc/include/android/cgrouprc.h b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
index 100d60e..e704a36 100644
--- a/libprocessgroup/cgrouprc/include/android/cgrouprc.h
+++ b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
@@ -16,6 +16,7 @@
#pragma once
+#include <sys/cdefs.h>
#include <stdint.h>
__BEGIN_DECLS
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 14e3e35..292425a 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -20,6 +20,16 @@
namespace android {
+namespace {
+
+constexpr uint64_t WAKE_EVENT_FD_SEQ = 1;
+
+epoll_event createEpollEvent(uint32_t events, uint64_t seq) {
+ return {.events = events, .data = {.u64 = seq}};
+}
+
+} // namespace
+
// --- WeakMessageHandler ---
WeakMessageHandler::WeakMessageHandler(const wp<MessageHandler>& handler) :
@@ -64,7 +74,7 @@
mSendingMessage(false),
mPolling(false),
mEpollRebuildRequired(false),
- mNextRequestSeq(0),
+ mNextRequestSeq(WAKE_EVENT_FD_SEQ + 1),
mResponseIndex(0),
mNextMessageUptime(LLONG_MAX) {
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
@@ -137,22 +147,17 @@
mEpollFd.reset();
}
- // Allocate the new epoll instance and register the wake pipe.
+ // Allocate the new epoll instance and register the WakeEventFd.
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
- struct epoll_event eventItem;
- memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
- eventItem.events = EPOLLIN;
- eventItem.data.fd = mWakeEventFd.get();
- int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
+ epoll_event wakeEvent = createEpollEvent(EPOLLIN, WAKE_EVENT_FD_SEQ);
+ int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &wakeEvent);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
strerror(errno));
- for (size_t i = 0; i < mRequests.size(); i++) {
- const Request& request = mRequests.valueAt(i);
- struct epoll_event eventItem;
- request.initEventItem(&eventItem);
+ for (const auto& [seq, request] : mRequests) {
+ epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
if (epollResult < 0) {
@@ -276,26 +281,28 @@
#endif
for (int i = 0; i < eventCount; i++) {
- int fd = eventItems[i].data.fd;
+ const SequenceNumber seq = eventItems[i].data.u64;
uint32_t epollEvents = eventItems[i].events;
- if (fd == mWakeEventFd.get()) {
+ if (seq == WAKE_EVENT_FD_SEQ) {
if (epollEvents & EPOLLIN) {
awoken();
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
- ssize_t requestIndex = mRequests.indexOfKey(fd);
- if (requestIndex >= 0) {
+ const auto& request_it = mRequests.find(seq);
+ if (request_it != mRequests.end()) {
+ const auto& request = request_it->second;
int events = 0;
if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
- pushResponse(events, mRequests.valueAt(requestIndex));
+ mResponses.push({.seq = seq, .events = events, .request = request});
} else {
- ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
- "no longer registered.", epollEvents, fd);
+ ALOGW("Ignoring unexpected epoll events 0x%x for sequence number %" PRIu64
+ " that is no longer registered.",
+ epollEvents, seq);
}
}
}
@@ -354,7 +361,8 @@
// we need to be a little careful when removing the file descriptor afterwards.
int callbackResult = response.request.callback->handleEvent(fd, events, data);
if (callbackResult == 0) {
- removeFd(fd, response.request.seq);
+ AutoMutex _l(mLock);
+ removeSequenceNumberLocked(response.seq);
}
// Clear the callback reference in the response structure promptly because we
@@ -416,13 +424,6 @@
TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
}
-void Looper::pushResponse(int events, const Request& request) {
- Response response;
- response.events = events;
- response.request = request;
- mResponses.push(response);
-}
-
int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : nullptr, data);
}
@@ -449,27 +450,27 @@
{ // acquire lock
AutoMutex _l(mLock);
+ // There is a sequence number reserved for the WakeEventFd.
+ if (mNextRequestSeq == WAKE_EVENT_FD_SEQ) mNextRequestSeq++;
+ const SequenceNumber seq = mNextRequestSeq++;
Request request;
request.fd = fd;
request.ident = ident;
request.events = events;
- request.seq = mNextRequestSeq++;
request.callback = callback;
request.data = data;
- if (mNextRequestSeq == -1) mNextRequestSeq = 0; // reserve sequence number -1
- struct epoll_event eventItem;
- request.initEventItem(&eventItem);
-
- ssize_t requestIndex = mRequests.indexOfKey(fd);
- if (requestIndex < 0) {
+ epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
+ auto seq_it = mSequenceNumberByFd.find(fd);
+ if (seq_it == mSequenceNumberByFd.end()) {
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d: %s", fd, strerror(errno));
return -1;
}
- mRequests.add(fd, request);
+ mRequests.emplace(seq, request);
+ mSequenceNumberByFd.emplace(fd, seq);
} else {
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_MOD, fd, &eventItem);
if (epollResult < 0) {
@@ -486,7 +487,7 @@
// set from scratch because it may contain an old file handle that we are
// now unable to remove since its file descriptor is no longer valid.
// No such problem would have occurred if we were using the poll system
- // call instead, but that approach carries others disadvantages.
+ // call instead, but that approach carries other disadvantages.
#if DEBUG_CALLBACKS
ALOGD("%p ~ addFd - EPOLL_CTL_MOD failed due to file descriptor "
"being recycled, falling back on EPOLL_CTL_ADD: %s",
@@ -504,71 +505,69 @@
return -1;
}
}
- mRequests.replaceValueAt(requestIndex, request);
+ const SequenceNumber oldSeq = seq_it->second;
+ mRequests.erase(oldSeq);
+ mRequests.emplace(seq, request);
+ seq_it->second = seq;
}
} // release lock
return 1;
}
int Looper::removeFd(int fd) {
- return removeFd(fd, -1);
+ AutoMutex _l(mLock);
+ const auto& it = mSequenceNumberByFd.find(fd);
+ if (it == mSequenceNumberByFd.end()) {
+ return 0;
+ }
+ return removeSequenceNumberLocked(it->second);
}
-int Looper::removeFd(int fd, int seq) {
+int Looper::removeSequenceNumberLocked(SequenceNumber seq) {
#if DEBUG_CALLBACKS
- ALOGD("%p ~ removeFd - fd=%d, seq=%d", this, fd, seq);
+ ALOGD("%p ~ removeFd - fd=%d, seq=%u", this, fd, seq);
#endif
- { // acquire lock
- AutoMutex _l(mLock);
- ssize_t requestIndex = mRequests.indexOfKey(fd);
- if (requestIndex < 0) {
- return 0;
- }
+ const auto& request_it = mRequests.find(seq);
+ if (request_it == mRequests.end()) {
+ return 0;
+ }
+ const int fd = request_it->second.fd;
- // Check the sequence number if one was given.
- if (seq != -1 && mRequests.valueAt(requestIndex).seq != seq) {
+ // Always remove the FD from the request map even if an error occurs while
+ // updating the epoll set so that we avoid accidentally leaking callbacks.
+ mRequests.erase(request_it);
+ mSequenceNumberByFd.erase(fd);
+
+ int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_DEL, fd, nullptr);
+ if (epollResult < 0) {
+ if (errno == EBADF || errno == ENOENT) {
+ // Tolerate EBADF or ENOENT because it means that the file descriptor was closed
+ // before its callback was unregistered. This error may occur naturally when a
+ // callback has the side-effect of closing the file descriptor before returning and
+ // unregistering itself.
+ //
+ // Unfortunately due to kernel limitations we need to rebuild the epoll
+ // set from scratch because it may contain an old file handle that we are
+ // now unable to remove since its file descriptor is no longer valid.
+ // No such problem would have occurred if we were using the poll system
+ // call instead, but that approach carries other disadvantages.
#if DEBUG_CALLBACKS
- ALOGD("%p ~ removeFd - sequence number mismatch, oldSeq=%d",
- this, mRequests.valueAt(requestIndex).seq);
+ ALOGD("%p ~ removeFd - EPOLL_CTL_DEL failed due to file descriptor "
+ "being closed: %s",
+ this, strerror(errno));
#endif
- return 0;
+ scheduleEpollRebuildLocked();
+ } else {
+ // Some other error occurred. This is really weird because it means
+ // our list of callbacks got out of sync with the epoll set somehow.
+ // We defensively rebuild the epoll set to avoid getting spurious
+ // notifications with nowhere to go.
+ ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(errno));
+ scheduleEpollRebuildLocked();
+ return -1;
}
-
- // Always remove the FD from the request map even if an error occurs while
- // updating the epoll set so that we avoid accidentally leaking callbacks.
- mRequests.removeItemsAt(requestIndex);
-
- int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_DEL, fd, nullptr);
- if (epollResult < 0) {
- if (seq != -1 && (errno == EBADF || errno == ENOENT)) {
- // Tolerate EBADF or ENOENT when the sequence number is known because it
- // means that the file descriptor was closed before its callback was
- // unregistered. This error may occur naturally when a callback has the
- // side-effect of closing the file descriptor before returning and
- // unregistering itself.
- //
- // Unfortunately due to kernel limitations we need to rebuild the epoll
- // set from scratch because it may contain an old file handle that we are
- // now unable to remove since its file descriptor is no longer valid.
- // No such problem would have occurred if we were using the poll system
- // call instead, but that approach carries others disadvantages.
-#if DEBUG_CALLBACKS
- ALOGD("%p ~ removeFd - EPOLL_CTL_DEL failed due to file descriptor "
- "being closed: %s", this, strerror(errno));
-#endif
- scheduleEpollRebuildLocked();
- } else {
- // Some other error occurred. This is really weird because it means
- // our list of callbacks got out of sync with the epoll set somehow.
- // We defensively rebuild the epoll set to avoid getting spurious
- // notifications with nowhere to go.
- ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(errno));
- scheduleEpollRebuildLocked();
- return -1;
- }
- }
- } // release lock
+ }
return 1;
}
@@ -656,14 +655,11 @@
return mPolling;
}
-void Looper::Request::initEventItem(struct epoll_event* eventItem) const {
- int epollEvents = 0;
+uint32_t Looper::Request::getEpollEvents() const {
+ uint32_t epollEvents = 0;
if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;
-
- memset(eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
- eventItem->events = epollEvents;
- eventItem->data.fd = fd;
+ return epollEvents;
}
MessageHandler::~MessageHandler() { }
diff --git a/libutils/Looper_test.cpp b/libutils/Looper_test.cpp
index 34f424b..c859f9c 100644
--- a/libutils/Looper_test.cpp
+++ b/libutils/Looper_test.cpp
@@ -8,6 +8,9 @@
#include <utils/Looper.h>
#include <utils/StopWatch.h>
#include <utils/Timers.h>
+#include <thread>
+#include <unordered_map>
+#include <utility>
#include "Looper_test_pipe.h"
#include <utils/threads.h>
@@ -710,4 +713,123 @@
<< "no more messages to handle";
}
+class LooperEventCallback : public LooperCallback {
+ public:
+ using Callback = std::function<int(int fd, int events)>;
+ explicit LooperEventCallback(Callback callback) : mCallback(std::move(callback)) {}
+ int handleEvent(int fd, int events, void* /*data*/) override { return mCallback(fd, events); }
+
+ private:
+ Callback mCallback;
+};
+
+// A utility class that allows for pipes to be added and removed from the looper, and polls the
+// looper from a different thread.
+class ThreadedLooperUtil {
+ public:
+ explicit ThreadedLooperUtil(const sp<Looper>& looper) : mLooper(looper), mRunning(true) {
+ mThread = std::thread([this]() {
+ while (mRunning) {
+ static constexpr std::chrono::milliseconds POLL_TIMEOUT(500);
+ mLooper->pollOnce(POLL_TIMEOUT.count());
+ }
+ });
+ }
+
+ ~ThreadedLooperUtil() {
+ mRunning = false;
+ mThread.join();
+ }
+
+ // Create a new pipe, and return the write end of the pipe and the id used to track the pipe.
+ // The read end of the pipe is added to the looper.
+ std::pair<int /*id*/, base::unique_fd> createPipe() {
+ int pipeFd[2];
+ if (pipe(pipeFd)) {
+ ADD_FAILURE() << "pipe() failed.";
+ return {};
+ }
+ const int readFd = pipeFd[0];
+ const int writeFd = pipeFd[1];
+
+ int id;
+ { // acquire lock
+ std::scoped_lock l(mLock);
+
+ id = mNextId++;
+ mFds.emplace(id, readFd);
+
+ auto removeCallback = [this, id, readFd](int fd, int events) {
+ EXPECT_EQ(readFd, fd) << "Received callback for incorrect fd.";
+ if ((events & Looper::EVENT_HANGUP) == 0) {
+ return 1; // Not a hangup, keep the callback.
+ }
+ removePipe(id);
+ return 0; // Remove the callback.
+ };
+
+ mLooper->addFd(readFd, 0, Looper::EVENT_INPUT,
+ new LooperEventCallback(std::move(removeCallback)), nullptr);
+ } // release lock
+
+ return {id, base::unique_fd(writeFd)};
+ }
+
+ // Remove the pipe with the given id.
+ void removePipe(int id) {
+ std::scoped_lock l(mLock);
+ if (mFds.find(id) == mFds.end()) {
+ return;
+ }
+ mLooper->removeFd(mFds[id].get());
+ mFds.erase(id);
+ }
+
+ // Check if the pipe with the given id exists and has not been removed.
+ bool hasPipe(int id) {
+ std::scoped_lock l(mLock);
+ return mFds.find(id) != mFds.end();
+ }
+
+ private:
+ sp<Looper> mLooper;
+ std::atomic<bool> mRunning;
+ std::thread mThread;
+
+ std::mutex mLock;
+ std::unordered_map<int, base::unique_fd> mFds GUARDED_BY(mLock);
+ int mNextId GUARDED_BY(mLock) = 0;
+};
+
+TEST_F(LooperTest, MultiThreaded_NoUnexpectedFdRemoval) {
+ ThreadedLooperUtil util(mLooper);
+
+ // Iterate repeatedly to try to recreate a flaky instance.
+ for (int i = 0; i < 1000; i++) {
+ auto [firstPipeId, firstPipeFd] = util.createPipe();
+ const int firstFdNumber = firstPipeFd.get();
+
+ // Close the first pipe's fd, causing a fd hangup.
+ firstPipeFd.reset();
+
+ // Request to remove the pipe from this test thread. This causes a race for pipe removal
+ // between the hangup in the looper's thread and this remove request from the test thread.
+ util.removePipe(firstPipeId);
+
+ // Create the second pipe. Since the fds for the first pipe are closed, this pipe should
+ // have the same fd numbers as the first pipe because the lowest unused fd number is used.
+ const auto [secondPipeId, fd] = util.createPipe();
+ EXPECT_EQ(firstFdNumber, fd.get())
+ << "The first and second fds must match for the purposes of this test.";
+
+ // Wait for unexpected hangup to occur.
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+
+ ASSERT_TRUE(util.hasPipe(secondPipeId)) << "The second pipe was removed unexpectedly.";
+
+ util.removePipe(secondPipeId);
+ }
+ SUCCEED() << "No unexpectedly removed fds.";
+}
+
} // namespace android
diff --git a/libutils/include/utils/Looper.h b/libutils/include/utils/Looper.h
index 466fbb7..b387d68 100644
--- a/libutils/include/utils/Looper.h
+++ b/libutils/include/utils/Looper.h
@@ -17,15 +17,16 @@
#ifndef UTILS_LOOPER_H
#define UTILS_LOOPER_H
-#include <utils/threads.h>
#include <utils/RefBase.h>
-#include <utils/KeyedVector.h>
#include <utils/Timers.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
#include <sys/epoll.h>
#include <android-base/unique_fd.h>
+#include <unordered_map>
#include <utility>
namespace android {
@@ -421,18 +422,20 @@
static sp<Looper> getForThread();
private:
- struct Request {
- int fd;
- int ident;
- int events;
- int seq;
- sp<LooperCallback> callback;
- void* data;
+ using SequenceNumber = uint64_t;
- void initEventItem(struct epoll_event* eventItem) const;
- };
+ struct Request {
+ int fd;
+ int ident;
+ int events;
+ sp<LooperCallback> callback;
+ void* data;
+
+ uint32_t getEpollEvents() const;
+ };
struct Response {
+ SequenceNumber seq;
int events;
Request request;
};
@@ -463,9 +466,14 @@
android::base::unique_fd mEpollFd; // guarded by mLock but only modified on the looper thread
bool mEpollRebuildRequired; // guarded by mLock
- // Locked list of file descriptor monitoring requests.
- KeyedVector<int, Request> mRequests; // guarded by mLock
- int mNextRequestSeq;
+ // Locked maps of fds and sequence numbers monitoring requests.
+ // Both maps must be kept in sync at all times.
+ std::unordered_map<SequenceNumber, Request> mRequests; // guarded by mLock
+ std::unordered_map<int /*fd*/, SequenceNumber> mSequenceNumberByFd; // guarded by mLock
+
+ // The sequence number to use for the next fd that is added to the looper.
+ // The sequence number 0 is reserved for the WakeEventFd.
+ SequenceNumber mNextRequestSeq; // guarded by mLock
// This state is only used privately by pollOnce and does not require a lock since
// it runs on a single thread.
@@ -474,9 +482,8 @@
nsecs_t mNextMessageUptime; // set to LLONG_MAX when none
int pollInner(int timeoutMillis);
- int removeFd(int fd, int seq);
+ int removeSequenceNumberLocked(SequenceNumber seq); // requires mLock
void awoken();
- void pushResponse(int events, const Request& request);
void rebuildEpollLocked();
void scheduleEpollRebuildLocked();
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index 6861456..87646f9 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -18,7 +18,11 @@
"-Werror",
],
stl: "none",
- system_shared_libs: [],
- header_libs: ["libc_headers"],
+ target: {
+ bionic: {
+ system_shared_libs: [],
+ header_libs: ["libc_headers"],
+ },
+ },
export_include_dirs: ["include"],
}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 9371617..08ef6e3 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -776,11 +776,13 @@
# Create directories to push tests to for each linker namespace.
# Create the subdirectories in case the first test is run as root
# so it doesn't end up owned by root.
- mkdir /data/local/tests 0700 shell shell
- mkdir /data/local/tests/product 0700 shell shell
- mkdir /data/local/tests/system 0700 shell shell
- mkdir /data/local/tests/unrestricted 0700 shell shell
- mkdir /data/local/tests/vendor 0700 shell shell
+ # Set directories to be executable by any process so that debuggerd,
+ # aka crash_dump, can read any executables/shared libraries.
+ mkdir /data/local/tests 0701 shell shell
+ mkdir /data/local/tests/product 0701 shell shell
+ mkdir /data/local/tests/system 0701 shell shell
+ mkdir /data/local/tests/unrestricted 0701 shell shell
+ mkdir /data/local/tests/vendor 0701 shell shell
# create dalvik-cache, so as to enforce our permissions
mkdir /data/dalvik-cache 0771 root root encryption=Require
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index e4d9b39..c72af40 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -245,6 +245,8 @@
tipc_fd = tipc_connect(dev_name, APPLOADER_PORT);
if (tipc_fd < 0) {
LOG(ERROR) << "Failed to connect to Trusty app loader: " << strerror(-tipc_fd);
+ // print this to stderr too to avoid silently exiting when run as non-root
+ fprintf(stderr, "Failed to connect to Trusty app loader: %s\n", strerror(-tipc_fd));
rc = tipc_fd;
goto err_tipc_connect;
}
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index 29c6f93..eb0acb5 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -45,36 +45,40 @@
static const char *main_ctrl_name = "com.android.ipc-unittest.ctrl";
static const char* receiver_name = "com.android.trusty.memref.receiver";
-static const char *_sopts = "hsvD:t:r:m:b:";
+static const char* _sopts = "hsvDS:t:r:m:b:";
+/* clang-format off */
static const struct option _lopts[] = {
- {"help", no_argument, 0, 'h'},
- {"silent", no_argument, 0, 's'},
- {"variable",no_argument, 0, 'v'},
- {"dev", required_argument, 0, 'D'},
- {"repeat", required_argument, 0, 'r'},
- {"burst", required_argument, 0, 'b'},
- {"msgsize", required_argument, 0, 'm'},
- {0, 0, 0, 0}
+ {"help", no_argument, 0, 'h'},
+ {"silent", no_argument, 0, 's'},
+ {"variable",no_argument, 0, 'v'},
+ {"dev", required_argument, 0, 'D'},
+ {"srv", required_argument, 0, 'S'},
+ {"repeat", required_argument, 0, 'r'},
+ {"burst", required_argument, 0, 'b'},
+ {"msgsize", required_argument, 0, 'm'},
+ {0, 0, 0, 0}
};
+/* clang-format on */
-static const char *usage =
-"Usage: %s [options]\n"
-"\n"
-"options:\n"
-" -h, --help prints this message and exit\n"
-" -D, --dev name device name\n"
-" -t, --test name test to run\n"
-" -r, --repeat cnt repeat count\n"
-" -m, --msgsize size max message size\n"
-" -v, --variable variable message size\n"
-" -s, --silent silent\n"
-"\n"
-;
+static const char* usage =
+ "Usage: %s [options]\n"
+ "\n"
+ "options:\n"
+ " -h, --help prints this message and exit\n"
+ " -D, --dev name device name\n"
+ " -S, --srv name service name\n"
+ " -t, --test name test to run\n"
+ " -r, --repeat cnt repeat count\n"
+ " -b, --burst cnt burst count\n"
+ " -m, --msgsize size max message size\n"
+ " -v, --variable variable message size\n"
+ " -s, --silent silent\n"
+ "\n";
static const char* usage_long =
"\n"
"The following tests are available:\n"
- " connect - connect to datasink service\n"
+ " connect - connect to specified service, defaults to echo+datasink\n"
" connect_foo - connect to non existing service\n"
" burst_write - send messages to datasink service\n"
" echo - send/receive messages to echo service\n"
@@ -96,798 +100,774 @@
static uint opt_msgburst = 32;
static bool opt_variable = false;
static bool opt_silent = false;
+static char* srv_name = NULL;
static void print_usage_and_exit(const char *prog, int code, bool verbose)
{
- fprintf (stderr, usage, prog);
- if (verbose)
- fprintf (stderr, "%s", usage_long);
- exit(code);
+ fprintf(stderr, usage, prog);
+ if (verbose) fprintf(stderr, "%s", usage_long);
+ exit(code);
}
static void parse_options(int argc, char **argv)
{
- int c;
- int oidx = 0;
+ int c;
+ int oidx = 0;
- while (1)
- {
- c = getopt_long (argc, argv, _sopts, _lopts, &oidx);
- if (c == -1)
- break; /* done */
+ while (1) {
+ c = getopt_long(argc, argv, _sopts, _lopts, &oidx);
+ if (c == -1) break; /* done */
- switch (c) {
+ switch (c) {
+ case 'D':
+ dev_name = strdup(optarg);
+ break;
- case 'D':
- dev_name = strdup(optarg);
- break;
+ case 'S':
+ srv_name = strdup(optarg);
+ break;
- case 't':
- test_name = strdup(optarg);
- break;
+ case 't':
+ test_name = strdup(optarg);
+ break;
- case 'v':
- opt_variable = true;
- break;
+ case 'v':
+ opt_variable = true;
+ break;
- case 'r':
- opt_repeat = atoi(optarg);
- break;
+ case 'r':
+ opt_repeat = atoi(optarg);
+ break;
- case 'm':
- opt_msgsize = atoi(optarg);
- break;
+ case 'm':
+ opt_msgsize = atoi(optarg);
+ break;
- case 'b':
- opt_msgburst = atoi(optarg);
- break;
+ case 'b':
+ opt_msgburst = atoi(optarg);
+ break;
- case 's':
- opt_silent = true;
- break;
+ case 's':
+ opt_silent = true;
+ break;
- case 'h':
- print_usage_and_exit(argv[0], EXIT_SUCCESS, true);
- break;
+ case 'h':
+ print_usage_and_exit(argv[0], EXIT_SUCCESS, true);
+ break;
- default:
- print_usage_and_exit(argv[0], EXIT_FAILURE, false);
- }
- }
+ default:
+ print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+ }
+ }
}
static int connect_test(uint repeat)
{
- uint i;
- int echo_fd;
- int dsink_fd;
+ uint i;
+ int echo_fd;
+ int dsink_fd;
+ int custom_fd;
- if (!opt_silent) {
- printf("%s: repeat = %u\n", __func__, repeat);
- }
+ if (!opt_silent) {
+ printf("%s: repeat = %u\n", __func__, repeat);
+ }
- for (i = 0; i < repeat; i++) {
- echo_fd = tipc_connect(dev_name, echo_name);
- if (echo_fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "echo");
- }
- dsink_fd = tipc_connect(dev_name, datasink_name);
- if (dsink_fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "datasink");
- }
+ for (i = 0; i < repeat; i++) {
+ if (srv_name) {
+ custom_fd = tipc_connect(dev_name, srv_name);
+ if (custom_fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", srv_name);
+ }
+ if (custom_fd >= 0) {
+ tipc_close(custom_fd);
+ }
+ } else {
+ echo_fd = tipc_connect(dev_name, echo_name);
+ if (echo_fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "echo");
+ }
+ dsink_fd = tipc_connect(dev_name, datasink_name);
+ if (dsink_fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "datasink");
+ }
- if (echo_fd >= 0) {
- tipc_close(echo_fd);
- }
- if (dsink_fd >= 0) {
- tipc_close(dsink_fd);
- }
- }
+ if (echo_fd >= 0) {
+ tipc_close(echo_fd);
+ }
+ if (dsink_fd >= 0) {
+ tipc_close(dsink_fd);
+ }
+ }
+ }
- if (!opt_silent) {
- printf("%s: done\n", __func__);
- }
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- return 0;
+ return 0;
}
static int connect_foo(uint repeat)
{
- uint i;
- int fd;
+ uint i;
+ int fd;
- if (!opt_silent) {
- printf("%s: repeat = %u\n", __func__, repeat);
- }
+ if (!opt_silent) {
+ printf("%s: repeat = %u\n", __func__, repeat);
+ }
- for (i = 0; i < repeat; i++) {
- fd = tipc_connect(dev_name, "foo");
- if (fd >= 0) {
- fprintf(stderr, "succeeded to connect to '%s' service\n",
- "foo");
- tipc_close(fd);
- }
- }
+ for (i = 0; i < repeat; i++) {
+ fd = tipc_connect(dev_name, "foo");
+ if (fd >= 0) {
+ fprintf(stderr, "succeeded to connect to '%s' service\n", "foo");
+ tipc_close(fd);
+ }
+ }
- if (!opt_silent) {
- printf("%s: done\n", __func__);
- }
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- return 0;
+ return 0;
}
static int closer1_test(uint repeat)
{
- uint i;
- int fd;
+ uint i;
+ int fd;
- if (!opt_silent) {
- printf("%s: repeat = %u\n", __func__, repeat);
- }
+ if (!opt_silent) {
+ printf("%s: repeat = %u\n", __func__, repeat);
+ }
- for (i = 0; i < repeat; i++) {
- fd = tipc_connect(dev_name, closer1_name);
- if (fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "closer1");
- continue;
- }
- if (!opt_silent) {
- printf("%s: connected\n", __func__);
- }
- tipc_close(fd);
- }
+ for (i = 0; i < repeat; i++) {
+ fd = tipc_connect(dev_name, closer1_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "closer1");
+ continue;
+ }
+ if (!opt_silent) {
+ printf("%s: connected\n", __func__);
+ }
+ tipc_close(fd);
+ }
- if (!opt_silent) {
- printf("%s: done\n", __func__);
- }
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- return 0;
+ return 0;
}
static int closer2_test(uint repeat)
{
- uint i;
- int fd;
+ uint i;
+ int fd;
- if (!opt_silent) {
- printf("%s: repeat = %u\n", __func__, repeat);
- }
+ if (!opt_silent) {
+ printf("%s: repeat = %u\n", __func__, repeat);
+ }
- for (i = 0; i < repeat; i++) {
- fd = tipc_connect(dev_name, closer2_name);
- if (fd < 0) {
- if (!opt_silent) {
- printf("failed to connect to '%s' service\n", "closer2");
- }
- } else {
- /* this should always fail */
- fprintf(stderr, "connected to '%s' service\n", "closer2");
- tipc_close(fd);
- }
- }
+ for (i = 0; i < repeat; i++) {
+ fd = tipc_connect(dev_name, closer2_name);
+ if (fd < 0) {
+ if (!opt_silent) {
+ printf("failed to connect to '%s' service\n", "closer2");
+ }
+ } else {
+ /* this should always fail */
+ fprintf(stderr, "connected to '%s' service\n", "closer2");
+ tipc_close(fd);
+ }
+ }
- if (!opt_silent) {
- printf("%s: done\n", __func__);
- }
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- return 0;
+ return 0;
}
static int closer3_test(uint repeat)
{
- uint i, j;
- ssize_t rc;
- int fd[4];
- char buf[64];
+ uint i, j;
+ ssize_t rc;
+ int fd[4];
+ char buf[64];
- if (!opt_silent) {
- printf("%s: repeat = %u\n", __func__, repeat);
- }
+ if (!opt_silent) {
+ printf("%s: repeat = %u\n", __func__, repeat);
+ }
- for (i = 0; i < repeat; i++) {
+ for (i = 0; i < repeat; i++) {
+ /* open 4 connections to closer3 service */
+ for (j = 0; j < 4; j++) {
+ fd[j] = tipc_connect(dev_name, closer3_name);
+ if (fd[j] < 0) {
+ fprintf(stderr, "fd[%d]: failed to connect to '%s' service\n", j, "closer3");
+ } else {
+ if (!opt_silent) {
+ printf("%s: fd[%d]=%d: connected\n", __func__, j, fd[j]);
+ }
+ memset(buf, i + j, sizeof(buf));
+ rc = write(fd[j], buf, sizeof(buf));
+ if (rc != sizeof(buf)) {
+ if (!opt_silent) {
+ printf("%s: fd[%d]=%d: write returned = %zd\n", __func__, j, fd[j], rc);
+ }
+ perror("closer3_test: write");
+ }
+ }
+ }
- /* open 4 connections to closer3 service */
- for (j = 0; j < 4; j++) {
- fd[j] = tipc_connect(dev_name, closer3_name);
- if (fd[j] < 0) {
- fprintf(stderr, "fd[%d]: failed to connect to '%s' service\n", j, "closer3");
- } else {
- if (!opt_silent) {
- printf("%s: fd[%d]=%d: connected\n", __func__, j, fd[j]);
- }
- memset(buf, i + j, sizeof(buf));
- rc = write(fd[j], buf, sizeof(buf));
- if (rc != sizeof(buf)) {
- if (!opt_silent) {
- printf("%s: fd[%d]=%d: write returned = %zd\n",
- __func__, j, fd[j], rc);
- }
- perror("closer3_test: write");
- }
- }
- }
+ /* sleep a bit */
+ sleep(1);
- /* sleep a bit */
- sleep(1);
+ /* It is expected that they will be closed by remote */
+ for (j = 0; j < 4; j++) {
+ if (fd[j] < 0) continue;
+ rc = write(fd[j], buf, sizeof(buf));
+ if (rc != sizeof(buf)) {
+ if (!opt_silent) {
+ printf("%s: fd[%d]=%d: write returned = %zd\n", __func__, j, fd[j], rc);
+ }
+ perror("closer3_test: write");
+ }
+ }
- /* It is expected that they will be closed by remote */
- for (j = 0; j < 4; j++) {
- if (fd[j] < 0)
- continue;
- rc = write(fd[j], buf, sizeof(buf));
- if (rc != sizeof(buf)) {
- if (!opt_silent) {
- printf("%s: fd[%d]=%d: write returned = %zd\n",
- __func__, j, fd[j], rc);
- }
- perror("closer3_test: write");
- }
- }
+ /* then they have to be closed by remote */
+ for (j = 0; j < 4; j++) {
+ if (fd[j] >= 0) {
+ tipc_close(fd[j]);
+ }
+ }
+ }
- /* then they have to be closed by remote */
- for (j = 0; j < 4; j++) {
- if (fd[j] >= 0) {
- tipc_close(fd[j]);
- }
- }
- }
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- if (!opt_silent) {
- printf("%s: done\n", __func__);
- }
-
- return 0;
+ return 0;
}
static int echo_test(uint repeat, uint msgsz, bool var)
{
- uint i;
- ssize_t rc;
- size_t msg_len;
- int echo_fd =-1;
- char tx_buf[msgsz];
- char rx_buf[msgsz];
+ uint i;
+ ssize_t rc;
+ size_t msg_len;
+ int echo_fd = -1;
+ char tx_buf[msgsz];
+ char rx_buf[msgsz];
- if (!opt_silent) {
- printf("%s: repeat %u: msgsz %u: variable %s\n",
- __func__, repeat, msgsz, var ? "true" : "false");
- }
+ if (!opt_silent) {
+ printf("%s: repeat %u: msgsz %u: variable %s\n", __func__, repeat, msgsz,
+ var ? "true" : "false");
+ }
- echo_fd = tipc_connect(dev_name, echo_name);
- if (echo_fd < 0) {
- fprintf(stderr, "Failed to connect to service\n");
- return echo_fd;
- }
+ echo_fd = tipc_connect(dev_name, echo_name);
+ if (echo_fd < 0) {
+ fprintf(stderr, "Failed to connect to service\n");
+ return echo_fd;
+ }
- for (i = 0; i < repeat; i++) {
+ for (i = 0; i < repeat; i++) {
+ msg_len = msgsz;
+ if (opt_variable && msgsz) {
+ msg_len = rand() % msgsz;
+ }
- msg_len = msgsz;
- if (opt_variable && msgsz) {
- msg_len = rand() % msgsz;
- }
+ memset(tx_buf, i + 1, msg_len);
- memset(tx_buf, i + 1, msg_len);
+ rc = write(echo_fd, tx_buf, msg_len);
+ if ((size_t)rc != msg_len) {
+ perror("echo_test: write");
+ break;
+ }
- rc = write(echo_fd, tx_buf, msg_len);
- if ((size_t)rc != msg_len) {
- perror("echo_test: write");
- break;
- }
+ rc = read(echo_fd, rx_buf, msg_len);
+ if (rc < 0) {
+ perror("echo_test: read");
+ break;
+ }
- rc = read(echo_fd, rx_buf, msg_len);
- if (rc < 0) {
- perror("echo_test: read");
- break;
- }
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr, "data truncated (%zu vs. %zu)\n", rc, msg_len);
+ continue;
+ }
- if ((size_t)rc != msg_len) {
- fprintf(stderr, "data truncated (%zu vs. %zu)\n",
- rc, msg_len);
- continue;
- }
+ if (memcmp(tx_buf, rx_buf, (size_t)rc)) {
+ fprintf(stderr, "data mismatch\n");
+ continue;
+ }
+ }
- if (memcmp(tx_buf, rx_buf, (size_t) rc)) {
- fprintf(stderr, "data mismatch\n");
- continue;
- }
- }
+ tipc_close(echo_fd);
- tipc_close(echo_fd);
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- if (!opt_silent) {
- printf("%s: done\n",__func__);
- }
-
- return 0;
+ return 0;
}
static int burst_write_test(uint repeat, uint msgburst, uint msgsz, bool var)
{
- int fd;
- uint i, j;
- ssize_t rc;
- size_t msg_len;
- char tx_buf[msgsz];
+ int fd;
+ uint i, j;
+ ssize_t rc;
+ size_t msg_len;
+ char tx_buf[msgsz];
- if (!opt_silent) {
- printf("%s: repeat %u: burst %u: msgsz %u: variable %s\n",
- __func__, repeat, msgburst, msgsz,
- var ? "true" : "false");
- }
+ if (!opt_silent) {
+ printf("%s: repeat %u: burst %u: msgsz %u: variable %s\n", __func__, repeat, msgburst,
+ msgsz, var ? "true" : "false");
+ }
- for (i = 0; i < repeat; i++) {
+ for (i = 0; i < repeat; i++) {
+ fd = tipc_connect(dev_name, datasink_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "datasink");
+ break;
+ }
- fd = tipc_connect(dev_name, datasink_name);
- if (fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "datasink");
- break;
- }
+ for (j = 0; j < msgburst; j++) {
+ msg_len = msgsz;
+ if (var && msgsz) {
+ msg_len = rand() % msgsz;
+ }
- for (j = 0; j < msgburst; j++) {
- msg_len = msgsz;
- if (var && msgsz) {
- msg_len = rand() % msgsz;
- }
+ memset(tx_buf, i + 1, msg_len);
+ rc = write(fd, tx_buf, msg_len);
+ if ((size_t)rc != msg_len) {
+ perror("burst_test: write");
+ break;
+ }
+ }
- memset(tx_buf, i + 1, msg_len);
- rc = write(fd, tx_buf, msg_len);
- if ((size_t)rc != msg_len) {
- perror("burst_test: write");
- break;
- }
- }
+ tipc_close(fd);
+ }
- tipc_close(fd);
- }
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- if (!opt_silent) {
- printf("%s: done\n",__func__);
- }
-
- return 0;
+ return 0;
}
static int _wait_for_msg(int fd, uint msgsz, int timeout)
{
- int rc;
- fd_set rfds;
- uint msgcnt = 0;
- char rx_buf[msgsz];
- struct timeval tv;
+ int rc;
+ fd_set rfds;
+ uint msgcnt = 0;
+ char rx_buf[msgsz];
+ struct timeval tv;
- if (!opt_silent) {
- printf("waiting (%d) for msg\n", timeout);
- }
+ if (!opt_silent) {
+ printf("waiting (%d) for msg\n", timeout);
+ }
- FD_ZERO(&rfds);
- FD_SET(fd, &rfds);
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
- tv.tv_sec = timeout;
- tv.tv_usec = 0;
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
- for(;;) {
- rc = select(fd+1, &rfds, NULL, NULL, &tv);
+ for (;;) {
+ rc = select(fd + 1, &rfds, NULL, NULL, &tv);
- if (rc == 0) {
- if (!opt_silent) {
- printf("select timedout\n");
- }
- break;
- }
+ if (rc == 0) {
+ if (!opt_silent) {
+ printf("select timedout\n");
+ }
+ break;
+ }
- if (rc == -1) {
- perror("select_test: select");
- return rc;
- }
+ if (rc == -1) {
+ perror("select_test: select");
+ return rc;
+ }
- rc = read(fd, rx_buf, sizeof(rx_buf));
- if (rc < 0) {
- perror("select_test: read");
- return rc;
- } else {
- if (rc > 0) {
- msgcnt++;
- }
- }
- }
+ rc = read(fd, rx_buf, sizeof(rx_buf));
+ if (rc < 0) {
+ perror("select_test: read");
+ return rc;
+ } else {
+ if (rc > 0) {
+ msgcnt++;
+ }
+ }
+ }
- if (!opt_silent) {
- printf("got %u messages\n", msgcnt);
- }
+ if (!opt_silent) {
+ printf("got %u messages\n", msgcnt);
+ }
- return 0;
+ return 0;
}
static int select_test(uint repeat, uint msgburst, uint msgsz)
{
- int fd;
- uint i, j;
- ssize_t rc;
- char tx_buf[msgsz];
+ int fd;
+ uint i, j;
+ ssize_t rc;
+ char tx_buf[msgsz];
- if (!opt_silent) {
- printf("%s: repeat %u\n", __func__, repeat);
- }
+ if (!opt_silent) {
+ printf("%s: repeat %u\n", __func__, repeat);
+ }
- fd = tipc_connect(dev_name, echo_name);
- if (fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "echo");
- return fd;
- }
+ fd = tipc_connect(dev_name, echo_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "echo");
+ return fd;
+ }
- for (i = 0; i < repeat; i++) {
+ for (i = 0; i < repeat; i++) {
+ _wait_for_msg(fd, msgsz, 1);
- _wait_for_msg(fd, msgsz, 1);
+ if (!opt_silent) {
+ printf("sending burst: %u msg\n", msgburst);
+ }
- if (!opt_silent) {
- printf("sending burst: %u msg\n", msgburst);
- }
+ for (j = 0; j < msgburst; j++) {
+ memset(tx_buf, i + j, msgsz);
+ rc = write(fd, tx_buf, msgsz);
+ if ((size_t)rc != msgsz) {
+ perror("burst_test: write");
+ break;
+ }
+ }
+ }
- for (j = 0; j < msgburst; j++) {
- memset(tx_buf, i + j, msgsz);
- rc = write(fd, tx_buf, msgsz);
- if ((size_t)rc != msgsz) {
- perror("burst_test: write");
- break;
- }
- }
- }
+ tipc_close(fd);
- tipc_close(fd);
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- if (!opt_silent) {
- printf("%s: done\n",__func__);
- }
-
- return 0;
+ return 0;
}
static int blocked_read_test(uint repeat)
{
- int fd;
- uint i;
- ssize_t rc;
- char rx_buf[512];
+ int fd;
+ uint i;
+ ssize_t rc;
+ char rx_buf[512];
- if (!opt_silent) {
- printf("%s: repeat %u\n", __func__, repeat);
- }
+ if (!opt_silent) {
+ printf("%s: repeat %u\n", __func__, repeat);
+ }
- fd = tipc_connect(dev_name, echo_name);
- if (fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "echo");
- return fd;
- }
+ fd = tipc_connect(dev_name, echo_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "echo");
+ return fd;
+ }
- for (i = 0; i < repeat; i++) {
- rc = read(fd, rx_buf, sizeof(rx_buf));
- if (rc < 0) {
- perror("select_test: read");
- break;
- } else {
- if (!opt_silent) {
- printf("got %zd bytes\n", rc);
- }
- }
- }
+ for (i = 0; i < repeat; i++) {
+ rc = read(fd, rx_buf, sizeof(rx_buf));
+ if (rc < 0) {
+ perror("select_test: read");
+ break;
+ } else {
+ if (!opt_silent) {
+ printf("got %zd bytes\n", rc);
+ }
+ }
+ }
- tipc_close(fd);
+ tipc_close(fd);
- if (!opt_silent) {
- printf("%s: done\n",__func__);
- }
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- return 0;
+ return 0;
}
static int ta2ta_ipc_test(void)
{
- enum test_message_header {
- TEST_PASSED = 0,
- TEST_FAILED = 1,
- TEST_MESSAGE = 2,
- };
+ enum test_message_header {
+ TEST_PASSED = 0,
+ TEST_FAILED = 1,
+ TEST_MESSAGE = 2,
+ };
- int fd;
- int ret;
- unsigned char rx_buf[256];
+ int fd;
+ int ret;
+ unsigned char rx_buf[256];
- if (!opt_silent) {
- printf("%s:\n", __func__);
- }
+ if (!opt_silent) {
+ printf("%s:\n", __func__);
+ }
- fd = tipc_connect(dev_name, main_ctrl_name);
- if (fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "main_ctrl");
- return fd;
- }
+ fd = tipc_connect(dev_name, main_ctrl_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "main_ctrl");
+ return fd;
+ }
- /* Wait for tests to complete and read status */
- while (true) {
- ret = read(fd, rx_buf, sizeof(rx_buf));
- if (ret <= 0 || ret >= (int)sizeof(rx_buf)) {
- fprintf(stderr, "%s: Read failed: %d\n", __func__, ret);
- tipc_close(fd);
- return -1;
- }
+ /* Wait for tests to complete and read status */
+ while (true) {
+ ret = read(fd, rx_buf, sizeof(rx_buf));
+ if (ret <= 0 || ret >= (int)sizeof(rx_buf)) {
+ fprintf(stderr, "%s: Read failed: %d\n", __func__, ret);
+ tipc_close(fd);
+ return -1;
+ }
- if (rx_buf[0] == TEST_PASSED) {
- break;
- } else if (rx_buf[0] == TEST_FAILED) {
- break;
- } else if (rx_buf[0] == TEST_MESSAGE) {
- write(STDOUT_FILENO, rx_buf + 1, ret - 1);
- } else {
- fprintf(stderr, "%s: Bad message header: %d\n",
- __func__, rx_buf[0]);
- break;
- }
- }
+ if (rx_buf[0] == TEST_PASSED) {
+ break;
+ } else if (rx_buf[0] == TEST_FAILED) {
+ break;
+ } else if (rx_buf[0] == TEST_MESSAGE) {
+ write(STDOUT_FILENO, rx_buf + 1, ret - 1);
+ } else {
+ fprintf(stderr, "%s: Bad message header: %d\n", __func__, rx_buf[0]);
+ break;
+ }
+ }
- tipc_close(fd);
+ tipc_close(fd);
- return rx_buf[0] == TEST_PASSED ? 0 : -1;
+ return rx_buf[0] == TEST_PASSED ? 0 : -1;
}
typedef struct uuid
{
- uint32_t time_low;
- uint16_t time_mid;
- uint16_t time_hi_and_version;
- uint8_t clock_seq_and_node[8];
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_hi_and_version;
+ uint8_t clock_seq_and_node[8];
} uuid_t;
static void print_uuid(const char *dev, uuid_t *uuid)
{
- printf("%s:", dev);
- printf("uuid: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
- uuid->time_low,
- uuid->time_mid,
- uuid->time_hi_and_version,
- uuid->clock_seq_and_node[0],
- uuid->clock_seq_and_node[1],
- uuid->clock_seq_and_node[2],
- uuid->clock_seq_and_node[3],
- uuid->clock_seq_and_node[4],
- uuid->clock_seq_and_node[5],
- uuid->clock_seq_and_node[6],
- uuid->clock_seq_and_node[7]
- );
+ printf("%s:", dev);
+ printf("uuid: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", uuid->time_low,
+ uuid->time_mid, uuid->time_hi_and_version, uuid->clock_seq_and_node[0],
+ uuid->clock_seq_and_node[1], uuid->clock_seq_and_node[2], uuid->clock_seq_and_node[3],
+ uuid->clock_seq_and_node[4], uuid->clock_seq_and_node[5], uuid->clock_seq_and_node[6],
+ uuid->clock_seq_and_node[7]);
}
static int dev_uuid_test(void)
{
- int fd;
- ssize_t rc;
- uuid_t uuid;
+ int fd;
+ ssize_t rc;
+ uuid_t uuid;
- fd = tipc_connect(dev_name, uuid_name);
- if (fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "uuid");
- return fd;
- }
+ fd = tipc_connect(dev_name, uuid_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "uuid");
+ return fd;
+ }
- /* wait for test to complete */
- rc = read(fd, &uuid, sizeof(uuid));
- if (rc < 0) {
- perror("dev_uuid_test: read");
- } else if (rc != sizeof(uuid)) {
- fprintf(stderr, "unexpected uuid size (%d vs. %d)\n",
- (int)rc, (int)sizeof(uuid));
- } else {
- print_uuid(dev_name, &uuid);
- }
+ /* wait for test to complete */
+ rc = read(fd, &uuid, sizeof(uuid));
+ if (rc < 0) {
+ perror("dev_uuid_test: read");
+ } else if (rc != sizeof(uuid)) {
+ fprintf(stderr, "unexpected uuid size (%d vs. %d)\n", (int)rc, (int)sizeof(uuid));
+ } else {
+ print_uuid(dev_name, &uuid);
+ }
- tipc_close(fd);
+ tipc_close(fd);
- return 0;
+ return 0;
}
static int ta_access_test(void)
{
- int fd;
+ int fd;
- if (!opt_silent) {
- printf("%s:\n", __func__);
- }
+ if (!opt_silent) {
+ printf("%s:\n", __func__);
+ }
- fd = tipc_connect(dev_name, ta_only_name);
- if (fd >= 0) {
- fprintf(stderr, "Succeed to connect to '%s' service\n",
- "ta_only");
- tipc_close(fd);
- }
+ fd = tipc_connect(dev_name, ta_only_name);
+ if (fd >= 0) {
+ fprintf(stderr, "Succeed to connect to '%s' service\n", "ta_only");
+ tipc_close(fd);
+ }
- fd = tipc_connect(dev_name, ns_only_name);
- if (fd < 0) {
- fprintf(stderr, "Failed to connect to '%s' service\n",
- "ns_only");
- return fd;
- }
- tipc_close(fd);
+ fd = tipc_connect(dev_name, ns_only_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to '%s' service\n", "ns_only");
+ return fd;
+ }
+ tipc_close(fd);
- if (!opt_silent) {
- printf("%s: done\n",__func__);
- }
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- return 0;
+ return 0;
}
static int writev_test(uint repeat, uint msgsz, bool var)
{
- uint i;
- ssize_t rc;
- size_t msg_len;
- int echo_fd = -1;
- char tx0_buf[msgsz];
- char tx1_buf[msgsz];
- char rx_buf [msgsz];
- struct iovec iovs[2]= {{tx0_buf, 0}, {tx1_buf, 0}};
+ uint i;
+ ssize_t rc;
+ size_t msg_len;
+ int echo_fd = -1;
+ char tx0_buf[msgsz];
+ char tx1_buf[msgsz];
+ char rx_buf[msgsz];
+ struct iovec iovs[2] = {{tx0_buf, 0}, {tx1_buf, 0}};
- if (!opt_silent) {
- printf("%s: repeat %u: msgsz %u: variable %s\n",
- __func__, repeat, msgsz, var ? "true" : "false");
- }
+ if (!opt_silent) {
+ printf("%s: repeat %u: msgsz %u: variable %s\n", __func__, repeat, msgsz,
+ var ? "true" : "false");
+ }
- echo_fd = tipc_connect(dev_name, echo_name);
- if (echo_fd < 0) {
- fprintf(stderr, "Failed to connect to service\n");
- return echo_fd;
- }
+ echo_fd = tipc_connect(dev_name, echo_name);
+ if (echo_fd < 0) {
+ fprintf(stderr, "Failed to connect to service\n");
+ return echo_fd;
+ }
- for (i = 0; i < repeat; i++) {
+ for (i = 0; i < repeat; i++) {
+ msg_len = msgsz;
+ if (opt_variable && msgsz) {
+ msg_len = rand() % msgsz;
+ }
- msg_len = msgsz;
- if (opt_variable && msgsz) {
- msg_len = rand() % msgsz;
- }
+ iovs[0].iov_len = msg_len / 3;
+ iovs[1].iov_len = msg_len - iovs[0].iov_len;
- iovs[0].iov_len = msg_len / 3;
- iovs[1].iov_len = msg_len - iovs[0].iov_len;
+ memset(tx0_buf, i + 1, iovs[0].iov_len);
+ memset(tx1_buf, i + 2, iovs[1].iov_len);
+ memset(rx_buf, i + 3, sizeof(rx_buf));
- memset(tx0_buf, i + 1, iovs[0].iov_len);
- memset(tx1_buf, i + 2, iovs[1].iov_len);
- memset(rx_buf, i + 3, sizeof(rx_buf));
+ rc = writev(echo_fd, iovs, 2);
+ if (rc < 0) {
+ perror("writev_test: writev");
+ break;
+ }
- rc = writev(echo_fd, iovs, 2);
- if (rc < 0) {
- perror("writev_test: writev");
- break;
- }
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr, "%s: %s: data size mismatch (%zd vs. %zd)\n", __func__, "writev",
+ (size_t)rc, msg_len);
+ break;
+ }
- if ((size_t)rc != msg_len) {
- fprintf(stderr,
- "%s: %s: data size mismatch (%zd vs. %zd)\n",
- __func__, "writev", (size_t)rc, msg_len);
- break;
- }
+ rc = read(echo_fd, rx_buf, sizeof(rx_buf));
+ if (rc < 0) {
+ perror("writev_test: read");
+ break;
+ }
- rc = read(echo_fd, rx_buf, sizeof(rx_buf));
- if (rc < 0) {
- perror("writev_test: read");
- break;
- }
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr, "%s: %s: data size mismatch (%zd vs. %zd)\n", __func__, "read",
+ (size_t)rc, msg_len);
+ break;
+ }
- if ((size_t)rc != msg_len) {
- fprintf(stderr,
- "%s: %s: data size mismatch (%zd vs. %zd)\n",
- __func__, "read", (size_t)rc, msg_len);
- break;
- }
+ if (memcmp(tx0_buf, rx_buf, iovs[0].iov_len)) {
+ fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
+ break;
+ }
- if (memcmp(tx0_buf, rx_buf, iovs[0].iov_len)) {
- fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
- break;
- }
+ if (memcmp(tx1_buf, rx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
+ fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
+ break;
+ }
+ }
- if (memcmp(tx1_buf, rx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
- fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
- break;
- }
- }
+ tipc_close(echo_fd);
- tipc_close(echo_fd);
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- if (!opt_silent) {
- printf("%s: done\n",__func__);
- }
-
- return 0;
+ return 0;
}
static int readv_test(uint repeat, uint msgsz, bool var)
{
- uint i;
- ssize_t rc;
- size_t msg_len;
- int echo_fd = -1;
- char tx_buf [msgsz];
- char rx0_buf[msgsz];
- char rx1_buf[msgsz];
- struct iovec iovs[2]= {{rx0_buf, 0}, {rx1_buf, 0}};
+ uint i;
+ ssize_t rc;
+ size_t msg_len;
+ int echo_fd = -1;
+ char tx_buf[msgsz];
+ char rx0_buf[msgsz];
+ char rx1_buf[msgsz];
+ struct iovec iovs[2] = {{rx0_buf, 0}, {rx1_buf, 0}};
- if (!opt_silent) {
- printf("%s: repeat %u: msgsz %u: variable %s\n",
- __func__, repeat, msgsz, var ? "true" : "false");
- }
+ if (!opt_silent) {
+ printf("%s: repeat %u: msgsz %u: variable %s\n", __func__, repeat, msgsz,
+ var ? "true" : "false");
+ }
- echo_fd = tipc_connect(dev_name, echo_name);
- if (echo_fd < 0) {
- fprintf(stderr, "Failed to connect to service\n");
- return echo_fd;
- }
+ echo_fd = tipc_connect(dev_name, echo_name);
+ if (echo_fd < 0) {
+ fprintf(stderr, "Failed to connect to service\n");
+ return echo_fd;
+ }
- for (i = 0; i < repeat; i++) {
+ for (i = 0; i < repeat; i++) {
+ msg_len = msgsz;
+ if (opt_variable && msgsz) {
+ msg_len = rand() % msgsz;
+ }
- msg_len = msgsz;
- if (opt_variable && msgsz) {
- msg_len = rand() % msgsz;
- }
+ iovs[0].iov_len = msg_len / 3;
+ iovs[1].iov_len = msg_len - iovs[0].iov_len;
- iovs[0].iov_len = msg_len / 3;
- iovs[1].iov_len = msg_len - iovs[0].iov_len;
+ memset(tx_buf, i + 1, sizeof(tx_buf));
+ memset(rx0_buf, i + 2, iovs[0].iov_len);
+ memset(rx1_buf, i + 3, iovs[1].iov_len);
- memset(tx_buf, i + 1, sizeof(tx_buf));
- memset(rx0_buf, i + 2, iovs[0].iov_len);
- memset(rx1_buf, i + 3, iovs[1].iov_len);
+ rc = write(echo_fd, tx_buf, msg_len);
+ if (rc < 0) {
+ perror("readv_test: write");
+ break;
+ }
- rc = write(echo_fd, tx_buf, msg_len);
- if (rc < 0) {
- perror("readv_test: write");
- break;
- }
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr, "%s: %s: data size mismatch (%zd vs. %zd)\n", __func__, "write",
+ (size_t)rc, msg_len);
+ break;
+ }
- if ((size_t)rc != msg_len) {
- fprintf(stderr,
- "%s: %s: data size mismatch (%zd vs. %zd)\n",
- __func__, "write", (size_t)rc, msg_len);
- break;
- }
+ rc = readv(echo_fd, iovs, 2);
+ if (rc < 0) {
+ perror("readv_test: readv");
+ break;
+ }
- rc = readv(echo_fd, iovs, 2);
- if (rc < 0) {
- perror("readv_test: readv");
- break;
- }
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr, "%s: %s: data size mismatch (%zd vs. %zd)\n", __func__, "write",
+ (size_t)rc, msg_len);
+ break;
+ }
- if ((size_t)rc != msg_len) {
- fprintf(stderr,
- "%s: %s: data size mismatch (%zd vs. %zd)\n",
- __func__, "write", (size_t)rc, msg_len);
- break;
- }
+ if (memcmp(rx0_buf, tx_buf, iovs[0].iov_len)) {
+ fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
+ break;
+ }
- if (memcmp(rx0_buf, tx_buf, iovs[0].iov_len)) {
- fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
- break;
- }
+ if (memcmp(rx1_buf, tx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
+ fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
+ break;
+ }
+ }
- if (memcmp(rx1_buf, tx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
- fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
- break;
- }
- }
+ tipc_close(echo_fd);
- tipc_close(echo_fd);
+ if (!opt_silent) {
+ printf("%s: done\n", __func__);
+ }
- if (!opt_silent) {
- printf("%s: done\n",__func__);
- }
-
- return 0;
+ return 0;
}
static int send_fd_test(void) {
@@ -964,51 +944,51 @@
int main(int argc, char **argv)
{
- int rc = 0;
+ int rc = 0;
- if (argc <= 1) {
- print_usage_and_exit(argv[0], EXIT_FAILURE, false);
- }
+ if (argc <= 1) {
+ print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+ }
- parse_options(argc, argv);
+ parse_options(argc, argv);
- if (!dev_name) {
- dev_name = TIPC_DEFAULT_DEVNAME;
- }
+ if (!dev_name) {
+ dev_name = TIPC_DEFAULT_DEVNAME;
+ }
- if (!test_name) {
- fprintf(stderr, "need a Test to run\n");
- print_usage_and_exit(argv[0], EXIT_FAILURE, true);
- }
+ if (!test_name) {
+ fprintf(stderr, "need a Test to run\n");
+ print_usage_and_exit(argv[0], EXIT_FAILURE, true);
+ }
- if (strcmp(test_name, "connect") == 0) {
- rc = connect_test(opt_repeat);
- } else if (strcmp(test_name, "connect_foo") == 0) {
- rc = connect_foo(opt_repeat);
- } else if (strcmp(test_name, "burst_write") == 0) {
- rc = burst_write_test(opt_repeat, opt_msgburst, opt_msgsize, opt_variable);
- } else if (strcmp(test_name, "select") == 0) {
- rc = select_test(opt_repeat, opt_msgburst, opt_msgsize);
- } else if (strcmp(test_name, "blocked_read") == 0) {
- rc = blocked_read_test(opt_repeat);
- } else if (strcmp(test_name, "closer1") == 0) {
- rc = closer1_test(opt_repeat);
- } else if (strcmp(test_name, "closer2") == 0) {
- rc = closer2_test(opt_repeat);
- } else if (strcmp(test_name, "closer3") == 0) {
- rc = closer3_test(opt_repeat);
- } else if (strcmp(test_name, "echo") == 0) {
- rc = echo_test(opt_repeat, opt_msgsize, opt_variable);
- } else if(strcmp(test_name, "ta2ta-ipc") == 0) {
- rc = ta2ta_ipc_test();
- } else if (strcmp(test_name, "dev-uuid") == 0) {
- rc = dev_uuid_test();
- } else if (strcmp(test_name, "ta-access") == 0) {
- rc = ta_access_test();
- } else if (strcmp(test_name, "writev") == 0) {
- rc = writev_test(opt_repeat, opt_msgsize, opt_variable);
- } else if (strcmp(test_name, "readv") == 0) {
- rc = readv_test(opt_repeat, opt_msgsize, opt_variable);
+ if (strcmp(test_name, "connect") == 0) {
+ rc = connect_test(opt_repeat);
+ } else if (strcmp(test_name, "connect_foo") == 0) {
+ rc = connect_foo(opt_repeat);
+ } else if (strcmp(test_name, "burst_write") == 0) {
+ rc = burst_write_test(opt_repeat, opt_msgburst, opt_msgsize, opt_variable);
+ } else if (strcmp(test_name, "select") == 0) {
+ rc = select_test(opt_repeat, opt_msgburst, opt_msgsize);
+ } else if (strcmp(test_name, "blocked_read") == 0) {
+ rc = blocked_read_test(opt_repeat);
+ } else if (strcmp(test_name, "closer1") == 0) {
+ rc = closer1_test(opt_repeat);
+ } else if (strcmp(test_name, "closer2") == 0) {
+ rc = closer2_test(opt_repeat);
+ } else if (strcmp(test_name, "closer3") == 0) {
+ rc = closer3_test(opt_repeat);
+ } else if (strcmp(test_name, "echo") == 0) {
+ rc = echo_test(opt_repeat, opt_msgsize, opt_variable);
+ } else if (strcmp(test_name, "ta2ta-ipc") == 0) {
+ rc = ta2ta_ipc_test();
+ } else if (strcmp(test_name, "dev-uuid") == 0) {
+ rc = dev_uuid_test();
+ } else if (strcmp(test_name, "ta-access") == 0) {
+ rc = ta_access_test();
+ } else if (strcmp(test_name, "writev") == 0) {
+ rc = writev_test(opt_repeat, opt_msgsize, opt_variable);
+ } else if (strcmp(test_name, "readv") == 0) {
+ rc = readv_test(opt_repeat, opt_msgsize, opt_variable);
} else if (strcmp(test_name, "send-fd") == 0) {
rc = send_fd_test();
} else {
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index b59fb67..8b8aee2 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -16,7 +16,10 @@
#include <errno.h>
#include <fcntl.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_proto.h>
#include <scsi/sg.h>
+#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -104,21 +107,62 @@
static const char* UFS_WAKE_LOCK_NAME = "ufs_seq_wakelock";
-#ifdef RPMB_DEBUG
-
-static void print_buf(const char* prefix, const uint8_t* buf, size_t size) {
+/**
+ * log_buf - Log a byte buffer to the android log.
+ * @priority: One of ANDROID_LOG_* priority levels from android_LogPriority in
+ * android/log.h
+ * @prefix: A null-terminated string that identifies this buffer. Must be less
+ * than 128 bytes.
+ * @buf: Buffer to dump.
+ * @size: Length of @buf in bytes.
+ */
+#define LOG_BUF_SIZE 256
+static int log_buf(int priority, const char* prefix, const uint8_t* buf, size_t size) {
+ int rc;
size_t i;
+ char line[LOG_BUF_SIZE] = {0};
+ char* cur = line;
- printf("%s @%p [%zu]", prefix, buf, size);
- for (i = 0; i < size; i++) {
- if (i && i % 32 == 0) printf("\n%*s", (int)strlen(prefix), "");
- printf(" %02x", buf[i]);
+ rc = snprintf(line, LOG_BUF_SIZE, "%s @%p [%zu]", prefix, buf, size);
+ if (rc < 0 || rc >= LOG_BUF_SIZE) {
+ goto err;
}
- printf("\n");
- fflush(stdout);
-}
+ cur += rc;
+ for (i = 0; i < size; i++) {
+ if (i % 32 == 0) {
+ /*
+ * Flush the line out to the log after we have printed 32 bytes
+ * (also flushes the header line on the first iteration and sets up
+ * for printing the buffer itself)
+ */
+ LOG_PRI(priority, LOG_TAG, "%s", line);
+ memset(line, 0, LOG_BUF_SIZE);
+ cur = line;
+ /* Shift output over by the length of the prefix */
+ rc = snprintf(line, LOG_BUF_SIZE, "%*s", (int)strlen(prefix), "");
+ if (rc < 0 || rc >= LOG_BUF_SIZE) {
+ goto err;
+ }
+ cur += rc;
+ }
+ rc = snprintf(cur, LOG_BUF_SIZE - (cur - line), "%02x ", buf[i]);
+ if (rc < 0 || rc >= LOG_BUF_SIZE - (cur - line)) {
+ goto err;
+ }
+ cur += rc;
+ }
+ LOG_PRI(priority, LOG_TAG, "%s", line);
-#endif
+ return 0;
+
+err:
+ if (rc < 0) {
+ return rc;
+ } else {
+ ALOGE("log_buf prefix was too long");
+ return -1;
+ }
+}
static void set_sg_io_hdr(sg_io_hdr_t* io_hdrp, int dxfer_direction, unsigned char cmd_len,
unsigned char mx_sb_len, unsigned int dxfer_len, void* dxferp,
@@ -135,6 +179,118 @@
io_hdrp->timeout = TIMEOUT;
}
+/**
+ * unexpected_scsi_sense - Check for unexpected codes in the sense buffer.
+ * @sense_buf: buffer containing sense data
+ * @len: length of @sense_buf
+ *
+ * Return: %true if the sense data is not valid or contains an unexpected sense
+ * code, %false otherwise.
+ */
+static bool unexpected_scsi_sense(const uint8_t* sense_buf, size_t len) {
+ uint8_t response_code = 0;
+ uint8_t sense_key = 0;
+ uint8_t additional_sense_code = 0;
+ uint8_t additional_sense_code_qualifier = 0;
+ uint8_t additional_length = 0;
+
+ if (!sense_buf || len == 0) {
+ ALOGE("Invalid SCSI sense buffer, length: %zu\n", len);
+ return true;
+ }
+
+ response_code = 0x7f & sense_buf[0];
+
+ if (response_code < 0x70 || response_code > 0x73) {
+ ALOGE("Invalid SCSI sense response code: %hhu\n", response_code);
+ return true;
+ }
+
+ if (response_code >= 0x72) {
+ /* descriptor format, SPC-6 4.4.2 */
+ if (len > 1) {
+ sense_key = 0xf & sense_buf[1];
+ }
+ if (len > 2) {
+ additional_sense_code = sense_buf[2];
+ }
+ if (len > 3) {
+ additional_sense_code_qualifier = sense_buf[3];
+ }
+ if (len > 7) {
+ additional_length = sense_buf[7];
+ }
+ } else {
+ /* fixed format, SPC-6 4.4.3 */
+ if (len > 2) {
+ sense_key = 0xf & sense_buf[2];
+ }
+ if (len > 7) {
+ additional_length = sense_buf[7];
+ }
+ if (len > 12) {
+ additional_sense_code = sense_buf[12];
+ }
+ if (len > 13) {
+ additional_sense_code_qualifier = sense_buf[13];
+ }
+ }
+
+ switch (sense_key) {
+ case NO_SENSE:
+ case 0x0f: /* COMPLETED, not present in kernel headers */
+ ALOGD("SCSI success with sense data: key=%hhu, asc=%hhu, ascq=%hhu\n", sense_key,
+ additional_sense_code, additional_sense_code_qualifier);
+ return false;
+ }
+
+ ALOGE("Unexpected SCSI sense data: key=%hhu, asc=%hhu, ascq=%hhu\n", sense_key,
+ additional_sense_code, additional_sense_code_qualifier);
+ log_buf(ANDROID_LOG_ERROR, "sense buffer: ", sense_buf, len);
+ return true;
+}
+
+static void check_sg_io_hdr(const sg_io_hdr_t* io_hdrp) {
+ if (io_hdrp->status == 0 && io_hdrp->host_status == 0 && io_hdrp->driver_status == 0) {
+ return;
+ }
+
+ if (io_hdrp->status & 0x01) {
+ ALOGE("SG_IO received unknown status, LSB is set: %hhu", io_hdrp->status);
+ }
+
+ if (io_hdrp->masked_status != GOOD && io_hdrp->sb_len_wr > 0) {
+ bool sense_error = unexpected_scsi_sense(io_hdrp->sbp, io_hdrp->sb_len_wr);
+ if (sense_error) {
+ ALOGE("Unexpected SCSI sense. masked_status: %hhu, host_status: %hu, driver_status: "
+ "%hu\n",
+ io_hdrp->masked_status, io_hdrp->host_status, io_hdrp->driver_status);
+ return;
+ }
+ }
+
+ switch (io_hdrp->masked_status) {
+ case GOOD:
+ break;
+ case CHECK_CONDITION:
+ /* handled by check_sg_sense above */
+ break;
+ default:
+ ALOGE("SG_IO failed with masked_status: %hhu, host_status: %hu, driver_status: %hu\n",
+ io_hdrp->masked_status, io_hdrp->host_status, io_hdrp->driver_status);
+ return;
+ }
+
+ if (io_hdrp->host_status != 0) {
+ ALOGE("SG_IO failed with host_status: %hu, driver_status: %hu\n", io_hdrp->host_status,
+ io_hdrp->driver_status);
+ }
+
+ if (io_hdrp->resid != 0) {
+ ALOGE("SG_IO resid was non-zero: %d\n", io_hdrp->resid);
+ }
+}
+
static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req) {
struct {
struct mmc_ioc_multi_cmd multi;
@@ -153,7 +309,7 @@
mmc_ioc_cmd_set_data((*cmd), write_buf);
#ifdef RPMB_DEBUG
ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
- print_buf("request: ", write_buf, req->reliable_write_size);
+ log_buf(ANDROID_LOG_INFO, "request: ", write_buf, req->reliable_write_size);
#endif
write_buf += req->reliable_write_size;
mmc.multi.num_of_cmds++;
@@ -169,7 +325,7 @@
mmc_ioc_cmd_set_data((*cmd), write_buf);
#ifdef RPMB_DEBUG
ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
- print_buf("request: ", write_buf, req->write_size);
+ log_buf(ANDROID_LOG_INFO, "request: ", write_buf, req->write_size);
#endif
write_buf += req->write_size;
mmc.multi.num_of_cmds++;
@@ -225,6 +381,7 @@
ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
goto err_op;
}
+ check_sg_io_hdr(&io_hdr);
write_buf += req->reliable_write_size;
}
@@ -239,6 +396,7 @@
ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
goto err_op;
}
+ check_sg_io_hdr(&io_hdr);
write_buf += req->write_size;
}
@@ -252,6 +410,7 @@
if (rc < 0) {
ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
}
+ check_sg_io_hdr(&io_hdr);
}
err_op:
@@ -353,7 +512,7 @@
goto err_response;
}
#ifdef RPMB_DEBUG
- if (req->read_size) print_buf("response: ", read_buf, req->read_size);
+ if (req->read_size) log_buf(ANDROID_LOG_INFO, "response: ", read_buf, req->read_size);
#endif
if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index 6cd381f..21ea7ae 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -23,7 +23,7 @@
# HAL loading of gatekeeper.trusty.
PRODUCT_PACKAGES += \
- android.hardware.keymaster@4.0-service.trusty \
+ android.hardware.security.keymint-service.trusty \
android.hardware.gatekeeper@1.0-service.trusty \
trusty_apploader