Merge "Add wchan data to ANR dump"
diff --git a/adb/Android.bp b/adb/Android.bp
index 6558b1b..47dafff 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -221,6 +221,7 @@
target: {
windows: {
enabled: true,
+ ldflags: ["-municode"],
shared_libs: ["AdbWinApi"],
},
},
diff --git a/adb/sysdeps_test.cpp b/adb/sysdeps_test.cpp
index 79cebe6..0f4b39c 100644
--- a/adb/sysdeps_test.cpp
+++ b/adb/sysdeps_test.cpp
@@ -25,6 +25,21 @@
#include "sysdeps.h"
#include "sysdeps/chrono.h"
+#if defined(_WIN32)
+#include <windows.h>
+static bool IsWine() {
+ HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
+ if (!ntdll) {
+ return false;
+ }
+ return GetProcAddress(ntdll, "wine_get_version") != nullptr;
+}
+#else
+static bool IsWine() {
+ return false;
+}
+#endif
+
TEST(sysdeps_socketpair, smoke) {
int fds[2];
ASSERT_EQ(0, adb_socketpair(fds)) << strerror(errno);
@@ -182,8 +197,10 @@
EXPECT_EQ(1, adb_poll(&pfd, 1, 100));
- // Linux returns POLLIN | POLLHUP, Windows returns just POLLHUP.
- EXPECT_EQ(POLLHUP, pfd.revents & POLLHUP);
+ if (!IsWine()) {
+ // Linux returns POLLIN | POLLHUP, Windows returns just POLLHUP.
+ EXPECT_EQ(POLLHUP, pfd.revents & POLLHUP);
+ }
}
TEST_F(sysdeps_poll, fd_count) {
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index f86cd03..6372b3d 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -610,15 +610,6 @@
static int _fh_socket_close(FH f) {
if (f->fh_socket != INVALID_SOCKET) {
- /* gently tell any peer that we're closing the socket */
- if (shutdown(f->fh_socket, SD_BOTH) == SOCKET_ERROR) {
- // If the socket is not connected, this returns an error. We want to
- // minimize logging spam, so don't log these errors for now.
-#if 0
- D("socket shutdown failed: %s",
- android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
-#endif
- }
if (closesocket(f->fh_socket) == SOCKET_ERROR) {
// Don't set errno here, since adb_close will ignore it.
const DWORD err = WSAGetLastError();
diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h
index 6153b77..030ef35e 100644
--- a/base/include/android-base/expected.h
+++ b/base/include/android-base/expected.h
@@ -256,7 +256,8 @@
expected& operator=(const expected& rhs) = default;
// Note for SFNAIE above applies to here as well
- expected& operator=(expected&& rhs) = default;
+ expected& operator=(expected&& rhs) noexcept(
+ std::is_nothrow_move_assignable_v<T>&& std::is_nothrow_move_assignable_v<E>) = default;
template <class U = T _ENABLE_IF(
!std::is_void_v<T> &&
@@ -542,7 +543,7 @@
expected& operator=(const expected& rhs) = default;
// Note for SFNAIE above applies to here as well
- expected& operator=(expected&& rhs) = default;
+ expected& operator=(expected&& rhs) noexcept(std::is_nothrow_move_assignable_v<E>) = default;
template<class G = E>
expected& operator=(const unexpected<G>& rhs) {
@@ -633,7 +634,7 @@
public:
// constructors
constexpr unexpected(const unexpected&) = default;
- constexpr unexpected(unexpected&&) = default;
+ constexpr unexpected(unexpected&&) noexcept(std::is_nothrow_move_constructible_v<E>) = default;
template <class Err = E _ENABLE_IF(
std::is_constructible_v<E, Err> &&
@@ -709,7 +710,8 @@
// assignment
constexpr unexpected& operator=(const unexpected&) = default;
- constexpr unexpected& operator=(unexpected&&) = default;
+ constexpr unexpected& operator=(unexpected&&) noexcept(std::is_nothrow_move_assignable_v<E>) =
+ default;
template<class Err = E>
constexpr unexpected& operator=(const unexpected<Err>& rhs) {
val_ = rhs.value();
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 3a02cff..a39245b 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -258,9 +258,9 @@
// A wrapper type that can be implicitly constructed from either int or unique_fd.
struct borrowed_fd {
- /* implicit */ borrowed_fd(int fd) : fd_(fd) {}
+ /* implicit */ borrowed_fd(int fd) : fd_(fd) {} // NOLINT
template <typename T>
- /* implicit */ borrowed_fd(const unique_fd_impl<T>& ufd) : fd_(ufd.get()) {}
+ /* implicit */ borrowed_fd(const unique_fd_impl<T>& ufd) : fd_(ufd.get()) {} // NOLINT
int get() const { return fd_; }
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 6936cc2..cd9fda3 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -140,13 +140,13 @@
{"mba_err", 13},
{"Watchdog", 14},
{"Panic", 15},
- {"power_key", 16}, // Mediatek
- {"power_on", 17},
+ {"power_key", 16}, // aliasReasons to cold,powerkey (Mediatek)
+ {"power_on", 17}, // aliasReasons to cold,powerkey
{"Reboot", 18},
{"rtc", 19},
{"edl", 20},
{"oem_pon1", 21},
- {"oem_powerkey", 22},
+ {"oem_powerkey", 22}, // aliasReasons to cold,powerkey
{"oem_unknown_reset", 23},
{"srto: HWWDT reset SC", 24},
{"srto: HWWDT reset platform", 25},
@@ -201,15 +201,15 @@
{"hard,hw_reset", 72},
{"shutdown,suspend", 73}, // Suspend to RAM
{"shutdown,hibernate", 74}, // Suspend to DISK
- {"power_on_key", 75},
- {"reboot_by_key", 76},
- {"wdt_by_pass_pwk", 77}, // Mediatek
- {"reboot_longkey", 78},
- {"powerkey", 79},
- {"usb", 80}, // Mediatek
- {"wdt", 81}, // Mediatek
- {"tool_by_pass_pwk", 82}, // Mediatek
- {"2sec_reboot", 83}, // Mediatek
+ {"power_on_key", 75}, // aliasReasons to cold,powerkey
+ {"reboot_by_key", 76}, // translated to reboot,by_key
+ {"wdt_by_pass_pwk", 77}, // Mediatek
+ {"reboot_longkey", 78}, // translated to reboot,longkey
+ {"powerkey", 79}, // aliasReasons to cold,powerkey
+ {"usb", 80}, // aliasReasons to cold,charger (Mediatek)
+ {"wdt", 81}, // Mediatek
+ {"tool_by_pass_pwk", 82}, // aliasReasons to reboot,tool (Mediatek)
+ {"2sec_reboot", 83}, // aliasReasons to cold,rtc,2sec (Mediatek)
{"reboot,by_key", 84},
{"reboot,longkey", 85},
{"reboot,2sec", 86}, // Deprecate in two years, replaced with cold,rtc,2sec
@@ -219,28 +219,28 @@
{"reboot,rescueparty", 90},
{"charge", 91},
{"oem_tz_crash", 92},
- {"uvlo", 93}, // aliasReasons converts to reboot,undervoltage
+ {"uvlo", 93}, // aliasReasons to reboot,undervoltage
{"oem_ps_hold", 94},
{"abnormal_reset", 95},
{"oemerr_unknown", 96},
{"reboot_fastboot_mode", 97},
{"watchdog_apps_bite", 98},
{"xpu_err", 99},
- {"power_on_usb", 100},
+ {"power_on_usb", 100}, // aliasReasons to cold,charger
{"watchdog_rpm", 101},
{"watchdog_nonsec", 102},
{"watchdog_apps_bark", 103},
{"reboot_dmverity_corrupted", 104},
- {"reboot_smpl", 105}, // aliasReasons converts to reboot,powerloss
+ {"reboot_smpl", 105}, // aliasReasons to reboot,powerloss
{"watchdog_sdi_apps_reset", 106},
- {"smpl", 107}, // aliasReasons converts to reboot,powerloss
+ {"smpl", 107}, // aliasReasons to reboot,powerloss
{"oem_modem_failed_to_powerup", 108},
{"reboot_normal", 109},
{"oem_lpass_cfg", 110},
{"oem_xpu_ns_error", 111},
- {"power_key_press", 112},
+ {"power_key_press", 112}, // aliasReasons to cold,powerkey
{"hardware_reset", 113},
- {"reboot_by_powerkey", 114},
+ {"reboot_by_powerkey", 114}, // aliasReasons to cold,powerkey (is this correct?)
{"reboot_verity", 115},
{"oem_rpm_undef_error", 116},
{"oem_crash_on_the_lk", 117},
@@ -250,7 +250,7 @@
{"factory_cable", 121},
{"oem_ar6320_failed_to_powerup", 122},
{"watchdog_rpm_bite", 123},
- {"power_on_cable", 124},
+ {"power_on_cable", 124}, // aliasReasons to cold,charger
{"reboot_unknown", 125},
{"wireless_charger", 126},
{"0x776655ff", 127},
@@ -839,12 +839,12 @@
// following table smaller.
static const std::vector<std::pair<const std::string, const std::string>> aliasReasons = {
{"watchdog", "wdog"},
- {"cold,powerkey", "powerkey|power_key|PowerKey"},
{"kernel_panic", "panic"},
{"shutdown,thermal", "thermal"},
{"warm,s3_wakeup", "s3_wakeup"},
{"hard,hw_reset", "hw_reset"},
- {"cold,charger", "usb"},
+ {"cold,charger", "usb|power_on_cable"},
+ {"cold,powerkey", "powerkey|power_key|PowerKey|power_on"},
{"cold,rtc", "rtc"},
{"cold,rtc,2sec", "2sec_reboot"},
{"!warm", "wdt_by_pass_pwk"}, // change flavour of blunt
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 1f0e420..fbc8b97 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -1099,3 +1099,30 @@
// This should be good enough, though...
ASSERT_LT(diff, 10) << "too many new tombstones; is something crashing in the background?";
}
+
+static __attribute__((__noinline__)) void overflow_stack(void* p) {
+ void* buf[1];
+ buf[0] = p;
+ static volatile void* global = buf;
+ if (global) {
+ global = buf;
+ overflow_stack(&buf);
+ }
+}
+
+TEST_F(CrasherTest, stack_overflow) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() { overflow_stack(nullptr); });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(Cause: stack pointer[^\n]*stack overflow.\n)");
+}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index d246722..da2ba58 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -86,7 +86,39 @@
_LOG(log, logtype::HEADER, "Timestamp: %s\n", buf);
}
-static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Maps* maps) {
+static std::string get_stack_overflow_cause(uint64_t fault_addr, uint64_t sp,
+ unwindstack::Maps* maps) {
+ static constexpr uint64_t kMaxDifferenceBytes = 256;
+ uint64_t difference;
+ if (sp >= fault_addr) {
+ difference = sp - fault_addr;
+ } else {
+ difference = fault_addr - sp;
+ }
+ if (difference <= kMaxDifferenceBytes) {
+ // The faulting address is close to the current sp, check if the sp
+ // indicates a stack overflow.
+ // On arm, the sp does not get updated when the instruction faults.
+ // In this case, the sp will still be in a valid map, which is the
+ // last case below.
+ // On aarch64, the sp does get updated when the instruction faults.
+ // In this case, the sp will be in either an invalid map if triggered
+ // on the main thread, or in a guard map if in another thread, which
+ // will be the first case or second case from below.
+ unwindstack::MapInfo* map_info = maps->Find(sp);
+ if (map_info == nullptr) {
+ return "stack pointer is in a non-existent map; likely due to stack overflow.";
+ } else if ((map_info->flags & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
+ return "stack pointer is not in a rw map; likely due to stack overflow.";
+ } else if ((sp - map_info->start) <= kMaxDifferenceBytes) {
+ return "stack pointer is close to top of stack; likely stack overflow.";
+ }
+ }
+ return "";
+}
+
+static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Maps* maps,
+ unwindstack::Regs* regs) {
std::string cause;
if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {
if (si->si_addr < reinterpret_cast<void*>(4096)) {
@@ -101,11 +133,16 @@
cause = "call to kuser_memory_barrier";
} else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) {
cause = "call to kuser_cmpxchg64";
+ } else {
+ cause = get_stack_overflow_cause(reinterpret_cast<uint64_t>(si->si_addr), regs->sp(), maps);
}
} else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
- unwindstack::MapInfo* map_info = maps->Find(reinterpret_cast<uint64_t>(si->si_addr));
+ uint64_t fault_addr = reinterpret_cast<uint64_t>(si->si_addr);
+ unwindstack::MapInfo* map_info = maps->Find(fault_addr);
if (map_info != nullptr && map_info->flags == PROT_EXEC) {
cause = "execute-only (no-read) memory access error; likely due to data in .text.";
+ } else {
+ cause = get_stack_overflow_cause(fault_addr, regs->sp(), maps);
}
} else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
@@ -447,7 +484,7 @@
if (thread_info.siginfo) {
dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get());
- dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps());
+ dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(), thread_info.registers.get());
}
if (primary_thread) {
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index e429d9f..4cdea71 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -27,6 +27,7 @@
"dm_target.cpp",
"dm.cpp",
"loop_control.cpp",
+ "utility.cpp",
],
static_libs: [
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index d56a4b1..0ad8d9d 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -29,6 +29,8 @@
#include <android-base/strings.h>
#include <uuid/uuid.h>
+#include "utility.h"
+
namespace android {
namespace dm {
@@ -94,20 +96,6 @@
return true;
}
-bool WaitForCondition(const std::function<bool()>& condition,
- const std::chrono::milliseconds& timeout_ms) {
- auto start_time = std::chrono::steady_clock::now();
- while (true) {
- if (condition()) return true;
-
- std::this_thread::sleep_for(20ms);
-
- auto now = std::chrono::steady_clock::now();
- auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
- if (time_elapsed > timeout_ms) return false;
- }
-}
-
static std::string GenerateUuid() {
uuid_t uuid_bytes;
uuid_generate(uuid_bytes);
@@ -138,16 +126,7 @@
if (timeout_ms <= std::chrono::milliseconds::zero()) {
return true;
}
-
- auto condition = [&]() -> bool {
- // If the file exists but returns EPERM or something, we consider the
- // condition met.
- if (access(unique_path.c_str(), F_OK) != 0) {
- if (errno == ENOENT) return false;
- }
- return true;
- };
- if (!WaitForCondition(condition, timeout_ms)) {
+ if (!WaitForFile(unique_path, timeout_ms)) {
LOG(ERROR) << "Timed out waiting for device path: " << unique_path;
DeleteDevice(name);
return false;
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 7a834e2..b28a8f2 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -103,9 +103,9 @@
ASSERT_TRUE(android::base::WriteFully(tmp1, message1, sizeof(message1)));
ASSERT_TRUE(android::base::WriteFully(tmp2, message2, sizeof(message2)));
- LoopDevice loop_a(tmp1);
+ LoopDevice loop_a(tmp1, 10s);
ASSERT_TRUE(loop_a.valid());
- LoopDevice loop_b(tmp2);
+ LoopDevice loop_b(tmp2, 10s);
ASSERT_TRUE(loop_b.valid());
// Define a 2-sector device, with each sector mapping to the first sector
@@ -255,9 +255,9 @@
cow_fd_ = CreateTempFile("cow_device", kCowDeviceSize);
ASSERT_GE(cow_fd_, 0);
- base_loop_ = std::make_unique<LoopDevice>(base_fd_);
+ base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
ASSERT_TRUE(base_loop_->valid());
- cow_loop_ = std::make_unique<LoopDevice>(cow_fd_);
+ cow_loop_ = std::make_unique<LoopDevice>(cow_fd_, 10s);
ASSERT_TRUE(cow_loop_->valid());
DmTable origin_table;
diff --git a/fs_mgr/libdm/include/libdm/loop_control.h b/fs_mgr/libdm/include/libdm/loop_control.h
index 6b4c2d8..eeed6b5 100644
--- a/fs_mgr/libdm/include/libdm/loop_control.h
+++ b/fs_mgr/libdm/include/libdm/loop_control.h
@@ -17,6 +17,7 @@
#ifndef _LIBDM_LOOP_CONTROL_H_
#define _LIBDM_LOOP_CONTROL_H_
+#include <chrono>
#include <string>
#include <android-base/unique_fd.h>
@@ -29,8 +30,15 @@
LoopControl();
// Attaches the file specified by 'file_fd' to the loop device specified
- // by 'loopdev'
- bool Attach(int file_fd, std::string* loopdev) const;
+ // by 'loopdev'. It is possible that in between allocating and attaching
+ // a loop device, another process attaches to the chosen loop device. If
+ // this happens, Attach() will retry for up to |timeout_ms|. The timeout
+ // should not be zero.
+ //
+ // The caller does not have to call WaitForFile(); it is implicitly called.
+ // The given |timeout_ms| covers both potential sources of timeout.
+ bool Attach(int file_fd, const std::chrono::milliseconds& timeout_ms,
+ std::string* loopdev) const;
// Detach the loop device given by 'loopdev' from the attached backing file.
bool Detach(const std::string& loopdev) const;
@@ -56,13 +64,13 @@
public:
// Create a loop device for the given file descriptor. It is closed when
// LoopDevice is destroyed only if auto_close is true.
- LoopDevice(int fd, bool auto_close = false);
+ LoopDevice(int fd, const std::chrono::milliseconds& timeout_ms, bool auto_close = false);
// Create a loop device for the given file path. It will be opened for
// reading and writing and closed when the loop device is detached.
- explicit LoopDevice(const std::string& path);
+ LoopDevice(const std::string& path, const std::chrono::milliseconds& timeout_ms);
~LoopDevice();
- bool valid() const { return fd_ != -1 && !device_.empty(); }
+ bool valid() const { return valid_; }
const std::string& device() const { return device_; }
LoopDevice(const LoopDevice&) = delete;
@@ -71,12 +79,13 @@
LoopDevice(LoopDevice&&) = default;
private:
- void Init();
+ void Init(const std::chrono::milliseconds& timeout_ms);
android::base::unique_fd fd_;
bool owns_fd_;
std::string device_;
LoopControl control_;
+ bool valid_ = false;
};
} // namespace dm
diff --git a/fs_mgr/libdm/loop_control.cpp b/fs_mgr/libdm/loop_control.cpp
index 16bf4b0..edc9a45 100644
--- a/fs_mgr/libdm/loop_control.cpp
+++ b/fs_mgr/libdm/loop_control.cpp
@@ -27,6 +27,8 @@
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
+#include "utility.h"
+
namespace android {
namespace dm {
@@ -37,21 +39,40 @@
}
}
-bool LoopControl::Attach(int file_fd, std::string* loopdev) const {
- if (!FindFreeLoopDevice(loopdev)) {
- LOG(ERROR) << "Failed to attach, no free loop devices";
- return false;
- }
+bool LoopControl::Attach(int file_fd, const std::chrono::milliseconds& timeout_ms,
+ std::string* loopdev) const {
+ auto start_time = std::chrono::steady_clock::now();
+ auto condition = [&]() -> WaitResult {
+ if (!FindFreeLoopDevice(loopdev)) {
+ LOG(ERROR) << "Failed to attach, no free loop devices";
+ return WaitResult::Fail;
+ }
- android::base::unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loopdev->c_str(), O_RDWR | O_CLOEXEC)));
- if (loop_fd < 0) {
- PLOG(ERROR) << "Failed to open: " << *loopdev;
- return false;
- }
+ auto now = std::chrono::steady_clock::now();
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (!WaitForFile(*loopdev, timeout_ms - time_elapsed)) {
+ LOG(ERROR) << "Timed out waiting for path: " << *loopdev;
+ return WaitResult::Fail;
+ }
- int rc = ioctl(loop_fd, LOOP_SET_FD, file_fd);
- if (rc < 0) {
- PLOG(ERROR) << "Failed LOOP_SET_FD";
+ android::base::unique_fd loop_fd(
+ TEMP_FAILURE_RETRY(open(loopdev->c_str(), O_RDWR | O_CLOEXEC)));
+ if (loop_fd < 0) {
+ PLOG(ERROR) << "Failed to open: " << *loopdev;
+ return WaitResult::Fail;
+ }
+
+ if (int rc = ioctl(loop_fd, LOOP_SET_FD, file_fd); rc == 0) {
+ return WaitResult::Done;
+ }
+ if (errno != EBUSY) {
+ PLOG(ERROR) << "Failed LOOP_SET_FD";
+ return WaitResult::Fail;
+ }
+ return WaitResult::Wait;
+ };
+ if (!WaitForCondition(condition, timeout_ms)) {
+ LOG(ERROR) << "Timed out trying to acquire a loop device";
return false;
}
return true;
@@ -112,17 +133,19 @@
return true;
}
-LoopDevice::LoopDevice(int fd, bool auto_close) : fd_(fd), owns_fd_(auto_close) {
- Init();
+LoopDevice::LoopDevice(int fd, const std::chrono::milliseconds& timeout_ms, bool auto_close)
+ : fd_(fd), owns_fd_(auto_close) {
+ Init(timeout_ms);
}
-LoopDevice::LoopDevice(const std::string& path) : fd_(-1), owns_fd_(true) {
+LoopDevice::LoopDevice(const std::string& path, const std::chrono::milliseconds& timeout_ms)
+ : fd_(-1), owns_fd_(true) {
fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC));
if (fd_ < -1) {
PLOG(ERROR) << "open failed for " << path;
return;
}
- Init();
+ Init(timeout_ms);
}
LoopDevice::~LoopDevice() {
@@ -134,8 +157,8 @@
}
}
-void LoopDevice::Init() {
- control_.Attach(fd_, &device_);
+void LoopDevice::Init(const std::chrono::milliseconds& timeout_ms) {
+ valid_ = control_.Attach(fd_, timeout_ms, &device_);
}
} // namespace dm
diff --git a/fs_mgr/libdm/loop_control_test.cpp b/fs_mgr/libdm/loop_control_test.cpp
index 08bdc00..0749f26 100644
--- a/fs_mgr/libdm/loop_control_test.cpp
+++ b/fs_mgr/libdm/loop_control_test.cpp
@@ -53,7 +53,7 @@
unique_fd fd = TempFile();
ASSERT_GE(fd, 0);
- LoopDevice loop(fd);
+ LoopDevice loop(fd, 10s);
ASSERT_TRUE(loop.valid());
char buffer[6];
diff --git a/fs_mgr/libdm/utility.cpp b/fs_mgr/libdm/utility.cpp
new file mode 100644
index 0000000..eccf2fb
--- /dev/null
+++ b/fs_mgr/libdm/utility.cpp
@@ -0,0 +1,56 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "utility.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <thread>
+
+using namespace std::literals;
+
+namespace android {
+namespace dm {
+
+bool WaitForCondition(const std::function<WaitResult()>& condition,
+ const std::chrono::milliseconds& timeout_ms) {
+ auto start_time = std::chrono::steady_clock::now();
+ while (true) {
+ auto result = condition();
+ if (result == WaitResult::Done) return true;
+ if (result == WaitResult::Fail) return false;
+
+ std::this_thread::sleep_for(20ms);
+
+ auto now = std::chrono::steady_clock::now();
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (time_elapsed > timeout_ms) return false;
+ }
+}
+
+bool WaitForFile(const std::string& path, const std::chrono::milliseconds& timeout_ms) {
+ auto condition = [&]() -> WaitResult {
+ // If the file exists but returns EPERM or something, we consider the
+ // condition met.
+ if (access(path.c_str(), F_OK) != 0) {
+ if (errno == ENOENT) return WaitResult::Wait;
+ }
+ return WaitResult::Done;
+ };
+ return WaitForCondition(condition, timeout_ms);
+}
+
+} // namespace dm
+} // namespace android
diff --git a/fs_mgr/libdm/utility.h b/fs_mgr/libdm/utility.h
new file mode 100644
index 0000000..f1dce9e
--- /dev/null
+++ b/fs_mgr/libdm/utility.h
@@ -0,0 +1,30 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <chrono>
+#include <functional>
+
+namespace android {
+namespace dm {
+
+enum class WaitResult { Wait, Done, Fail };
+
+bool WaitForFile(const std::string& path, const std::chrono::milliseconds& timeout_ms);
+bool WaitForCondition(const std::function<WaitResult()>& condition,
+ const std::chrono::milliseconds& timeout_ms);
+
+} // namespace dm
+} // namespace android
diff --git a/fs_mgr/libfiemap_writer/.clang-format b/fs_mgr/libfiemap_writer/.clang-format
deleted file mode 120000
index 8b770a1..0000000
--- a/fs_mgr/libfiemap_writer/.clang-format
+++ /dev/null
@@ -1 +0,0 @@
-../../.clang-format-4
\ No newline at end of file
diff --git a/fs_mgr/libfiemap_writer/Android.bp b/fs_mgr/libfiemap_writer/Android.bp
deleted file mode 100644
index ed209aa..0000000
--- a/fs_mgr/libfiemap_writer/Android.bp
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-// Copyright (C) 2018 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.
-//
-
-cc_library_static {
- name: "libfiemap_writer",
- defaults: ["fs_mgr_defaults"],
- recovery_available: true,
- export_include_dirs: ["include"],
- cflags: [
- "-D_FILE_OFFSET_BITS=64",
- ],
-
- srcs: [
- "fiemap_writer.cpp",
- "split_fiemap_writer.cpp",
- "utility.cpp",
- ],
-
- static_libs: [
- "libdm",
- "libext4_utils",
- ],
-
- header_libs: [
- "libbase_headers",
- "liblog_headers",
- ],
-}
-
-cc_test {
- name: "fiemap_writer_test",
- cflags: [
- "-D_FILE_OFFSET_BITS=64",
- ],
- static_libs: [
- "libbase",
- "libdm",
- "libfiemap_writer",
- "liblog",
- ],
-
- data: [
- "testdata/unaligned_file",
- "testdata/file_4k",
- "testdata/file_32k",
- ],
-
- srcs: [
- "fiemap_writer_test.cpp",
- ],
-}
diff --git a/fs_mgr/libfiemap_writer/Android.mk b/fs_mgr/libfiemap_writer/Android.mk
deleted file mode 100644
index 3c07b8e..0000000
--- a/fs_mgr/libfiemap_writer/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := VtsFiemapWriterTest
--include test/vts/tools/build/Android.host_config.mk
diff --git a/fs_mgr/libfiemap_writer/AndroidTest.xml b/fs_mgr/libfiemap_writer/AndroidTest.xml
deleted file mode 100644
index 08cff0e..0000000
--- a/fs_mgr/libfiemap_writer/AndroidTest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 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.
--->
-<configuration description="Config for VTS VtsFiemapWriterTest">
- <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
- <option name="abort-on-push-failure" value="false"/>
- <option name="push-group" value="HostDrivenTest.push"/>
- </target_preparer>
- <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
- <option name="test-module-name" value="VtsFiemapWriterTest"/>
- <option name="binary-test-source" value="_32bit::DATA/nativetest/fiemap_writer_test/fiemap_writer_test" />
- <option name="binary-test-source" value="_64bit::DATA/nativetest64/fiemap_writer_test/fiemap_writer_test" />
- <option name="binary-test-type" value="gtest"/>
- <option name="test-timeout" value="1m"/>
- </test>
-</configuration>
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
deleted file mode 100644
index 0a3ba6c..0000000
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ /dev/null
@@ -1,745 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <libfiemap_writer/fiemap_writer.h>
-
-#include <dirent.h>
-#include <fcntl.h>
-#include <linux/fs.h>
-#include <stdio.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/sysmacros.h>
-#include <sys/types.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-#include <limits>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <libdm/dm.h>
-
-namespace android {
-namespace fiemap_writer {
-
-using namespace android::dm;
-
-// We are expecting no more than 512 extents in a fiemap of the file we create.
-// If we find more, then it is treated as error for now.
-static constexpr const uint32_t kMaxExtents = 512;
-
-// TODO: Fallback to using fibmap if FIEMAP_EXTENT_MERGED is set.
-static constexpr const uint32_t kUnsupportedExtentFlags =
- FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_DELALLOC |
- FIEMAP_EXTENT_NOT_ALIGNED | FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_DATA_TAIL |
- FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED | FIEMAP_EXTENT_MERGED;
-
-// Large file support must be enabled.
-static_assert(sizeof(off_t) == sizeof(uint64_t));
-
-static inline void cleanup(const std::string& file_path, bool created) {
- if (created) {
- unlink(file_path.c_str());
- }
-}
-
-static bool BlockDeviceToName(uint32_t major, uint32_t minor, std::string* bdev_name) {
- // The symlinks in /sys/dev/block point to the block device node under /sys/device/..
- // The directory name in the target corresponds to the name of the block device. We use
- // that to extract the block device name.
- // e.g for block device name 'ram0', there exists a symlink named '1:0' in /sys/dev/block as
- // follows.
- // 1:0 -> ../../devices/virtual/block/ram0
- std::string sysfs_path = ::android::base::StringPrintf("/sys/dev/block/%u:%u", major, minor);
- std::string sysfs_bdev;
-
- if (!::android::base::Readlink(sysfs_path, &sysfs_bdev)) {
- PLOG(ERROR) << "Failed to read link at: " << sysfs_path;
- return false;
- }
-
- *bdev_name = ::android::base::Basename(sysfs_bdev);
- // Paranoid sanity check to make sure we just didn't get the
- // input in return as-is.
- if (sysfs_bdev == *bdev_name) {
- LOG(ERROR) << "Malformed symlink for block device: " << sysfs_bdev;
- return false;
- }
-
- return true;
-}
-
-static bool ValidateDmTarget(const DeviceMapper::TargetInfo& target) {
- const auto& entry = target.spec;
- if (entry.sector_start != 0) {
- LOG(INFO) << "Stopping at target with non-zero starting sector";
- return false;
- }
-
- auto target_type = DeviceMapper::GetTargetType(entry);
- if (target_type == "bow" || target_type == "default-key" || target_type == "crypt") {
- return true;
- }
- if (target_type == "linear") {
- auto pieces = android::base::Split(target.data, " ");
- if (pieces[1] != "0") {
- LOG(INFO) << "Stopping at complex linear target with non-zero starting sector: "
- << pieces[1];
- return false;
- }
- return true;
- }
-
- LOG(INFO) << "Stopping at complex target type " << target_type;
- return false;
-}
-
-static bool DeviceMapperStackPop(const std::string& bdev, std::string* bdev_raw) {
- *bdev_raw = bdev;
-
- if (!::android::base::StartsWith(bdev, "dm-")) {
- // We are at the bottom of the device mapper stack.
- return true;
- }
-
- // Get the device name.
- auto dm_name_file = "/sys/block/" + bdev + "/dm/name";
- std::string dm_name;
- if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {
- PLOG(ERROR) << "Could not read file: " << dm_name_file;
- return false;
- }
- dm_name = android::base::Trim(dm_name);
-
- auto& dm = DeviceMapper::Instance();
- std::vector<DeviceMapper::TargetInfo> table;
- if (!dm.GetTableInfo(dm_name, &table)) {
- LOG(ERROR) << "Could not read device-mapper table for " << dm_name << " at " << bdev;
- return false;
- }
-
- // The purpose of libfiemap_writer is to provide an extent-based view into
- // a file. This is difficult if devices are not layered in a 1:1 manner;
- // we would have to translate and break up extents based on the actual
- // block mapping. Since this is too complex, we simply stop processing
- // the device-mapper stack if we encounter a complex case.
- //
- // It is up to the caller to decide whether stopping at a virtual block
- // device is allowable. In most cases it is not, because we want either
- // "userdata" or an external volume. It is useful for tests however.
- // Callers can check by comparing the device number to that of userdata,
- // or by checking whether is a device-mapper node.
- if (table.size() > 1) {
- LOG(INFO) << "Stopping at complex table for " << dm_name << " at " << bdev;
- return true;
- }
- if (!ValidateDmTarget(table[0])) {
- return true;
- }
-
- auto dm_leaf_dir = "/sys/block/" + bdev + "/slaves";
- auto d = std::unique_ptr<DIR, decltype(&closedir)>(opendir(dm_leaf_dir.c_str()), closedir);
- if (d == nullptr) {
- PLOG(ERROR) << "Failed to open: " << dm_leaf_dir;
- return false;
- }
-
- struct dirent* de;
- uint32_t num_leaves = 0;
- std::string bdev_next = "";
- while ((de = readdir(d.get())) != nullptr) {
- if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
- continue;
- }
-
- // We set the first name we find here
- if (bdev_next.empty()) {
- bdev_next = de->d_name;
- }
- num_leaves++;
- }
-
- // if we have more than one leaves, we return immediately. We can't continue to create the
- // file since we don't know how to write it out using fiemap, so it will be readable via the
- // underlying block devices later. The reader will also have to construct the same device mapper
- // target in order read the file out.
- if (num_leaves > 1) {
- LOG(ERROR) << "Found " << num_leaves << " leaf block devices under device mapper device "
- << bdev;
- return false;
- }
-
- // recursively call with the block device we found in order to pop the device mapper stack.
- return DeviceMapperStackPop(bdev_next, bdev_raw);
-}
-
-bool FiemapWriter::GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
- bool* uses_dm) {
- struct stat sb;
- if (stat(file_path.c_str(), &sb)) {
- PLOG(ERROR) << "Failed to get stat for: " << file_path;
- return false;
- }
-
- std::string bdev;
- if (!BlockDeviceToName(major(sb.st_dev), minor(sb.st_dev), &bdev)) {
- LOG(ERROR) << "Failed to get block device name for " << major(sb.st_dev) << ":"
- << minor(sb.st_dev);
- return false;
- }
-
- std::string bdev_raw;
- if (!DeviceMapperStackPop(bdev, &bdev_raw)) {
- LOG(ERROR) << "Failed to get the bottom of the device mapper stack for device: " << bdev;
- return false;
- }
-
- if (uses_dm) {
- *uses_dm = (bdev_raw != bdev);
- }
-
- LOG(DEBUG) << "Popped device (" << bdev_raw << ") from device mapper stack starting with ("
- << bdev << ")";
-
- *bdev_path = ::android::base::StringPrintf("/dev/block/%s", bdev_raw.c_str());
-
- // Make sure we are talking to a block device before calling it a success.
- if (stat(bdev_path->c_str(), &sb)) {
- PLOG(ERROR) << "Failed to get stat for block device: " << *bdev_path;
- return false;
- }
-
- if ((sb.st_mode & S_IFMT) != S_IFBLK) {
- PLOG(ERROR) << "File: " << *bdev_path << " is not a block device";
- return false;
- }
-
- return true;
-}
-
-static bool GetBlockDeviceSize(int bdev_fd, const std::string& bdev_path, uint64_t* bdev_size) {
- uint64_t size_in_bytes = 0;
- if (ioctl(bdev_fd, BLKGETSIZE64, &size_in_bytes)) {
- PLOG(ERROR) << "Failed to get total size for: " << bdev_path;
- return false;
- }
-
- *bdev_size = size_in_bytes;
-
- return true;
-}
-
-static uint64_t GetFileSize(const std::string& file_path) {
- struct stat sb;
- if (stat(file_path.c_str(), &sb)) {
- PLOG(ERROR) << "Failed to get size for file: " << file_path;
- return 0;
- }
-
- return sb.st_size;
-}
-
-static bool PerformFileChecks(const std::string& file_path, uint64_t file_size, uint64_t* blocksz,
- uint32_t* fs_type) {
- struct statfs64 sfs;
- if (statfs64(file_path.c_str(), &sfs)) {
- PLOG(ERROR) << "Failed to read file system status at: " << file_path;
- return false;
- }
-
- if (!sfs.f_bsize) {
- LOG(ERROR) << "Unsupported block size: " << sfs.f_bsize;
- return false;
- }
-
- // Check if the filesystem is of supported types.
- // Only ext4, f2fs, and vfat are tested and supported.
- switch (sfs.f_type) {
- case EXT4_SUPER_MAGIC:
- case F2FS_SUPER_MAGIC:
- case MSDOS_SUPER_MAGIC:
- break;
- default:
- LOG(ERROR) << "Unsupported file system type: 0x" << std::hex << sfs.f_type;
- return false;
- }
-
- uint64_t available_bytes = sfs.f_bsize * sfs.f_bavail;
- if (available_bytes <= file_size) {
- LOG(ERROR) << "Not enough free space in file system to create file of size : " << file_size;
- return false;
- }
-
- *blocksz = sfs.f_bsize;
- *fs_type = sfs.f_type;
- return true;
-}
-
-static bool FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,
- const std::string& file_path,
- const std::function<bool(uint64_t, uint64_t)>& on_progress) {
- // Even though this is much faster than writing zeroes, it is still slow
- // enough that we need to fire the progress callback periodically. To
- // easily achieve this, we seek in chunks. We use 1000 chunks since
- // normally we only fire the callback on 1/1000th increments.
- uint64_t bytes_per_chunk = std::max(file_size / 1000, block_size);
-
- // Seek just to the end of each chunk and write a single byte, causing
- // the filesystem to allocate blocks.
- off_t cursor = 0;
- off_t end = static_cast<off_t>(file_size);
- while (cursor < end) {
- cursor = std::min(static_cast<off_t>(cursor + bytes_per_chunk), end);
- auto rv = TEMP_FAILURE_RETRY(lseek(file_fd, cursor - 1, SEEK_SET));
- if (rv < 0) {
- PLOG(ERROR) << "Failed to lseek " << file_path;
- return false;
- }
- if (rv != cursor - 1) {
- LOG(ERROR) << "Seek returned wrong offset " << rv << " for file " << file_path;
- return false;
- }
- char buffer[] = {0};
- if (!android::base::WriteFully(file_fd, buffer, 1)) {
- PLOG(ERROR) << "Write failed: " << file_path;
- return false;
- }
- if (on_progress && !on_progress(cursor, file_size)) {
- return false;
- }
- }
- return true;
-}
-
-static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
- uint64_t file_size, unsigned int fs_type,
- std::function<bool(uint64_t, uint64_t)> on_progress) {
- // Reserve space for the file on the file system and write it out to make sure the extents
- // don't come back unwritten. Return from this function with the kernel file offset set to 0.
- // If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
- // aren't moved around.
- switch (fs_type) {
- case EXT4_SUPER_MAGIC:
- case F2FS_SUPER_MAGIC:
- if (fallocate(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
- PLOG(ERROR) << "Failed to allocate space for file: " << file_path
- << " size: " << file_size;
- return false;
- }
- break;
- case MSDOS_SUPER_MAGIC:
- // fallocate() is not supported, and not needed, since VFAT does not support holes.
- // Instead we can perform a much faster allocation.
- return FallocateFallback(file_fd, blocksz, file_size, file_path, on_progress);
- default:
- LOG(ERROR) << "Missing fallocate() support for file system " << fs_type;
- return false;
- }
-
- // write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
- // blocks are actually written to by the file system and thus getting rid of the holes in the
- // file.
- auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksz), free);
- if (buffer == nullptr) {
- LOG(ERROR) << "failed to allocate memory for writing file";
- return false;
- }
-
- off64_t offset = lseek64(file_fd, 0, SEEK_SET);
- if (offset < 0) {
- PLOG(ERROR) << "Failed to seek at the beginning of : " << file_path;
- return false;
- }
-
- int permille = -1;
- while (offset < file_size) {
- if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
- PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
- << " in file " << file_path;
- return false;
- }
-
- offset += blocksz;
-
- // Don't invoke the callback every iteration - wait until a significant
- // chunk (here, 1/1000th) of the data has been processed.
- int new_permille = (static_cast<uint64_t>(offset) * 1000) / file_size;
- if (new_permille != permille && static_cast<uint64_t>(offset) != file_size) {
- if (on_progress && !on_progress(offset, file_size)) {
- return false;
- }
- permille = new_permille;
- }
- }
-
- if (lseek64(file_fd, 0, SEEK_SET) < 0) {
- PLOG(ERROR) << "Failed to reset offset at the beginning of : " << file_path;
- return false;
- }
-
- // flush all writes here ..
- if (fsync(file_fd)) {
- PLOG(ERROR) << "Failed to synchronize written file:" << file_path;
- return false;
- }
-
- // Send one last progress notification.
- if (on_progress && !on_progress(file_size, file_size)) {
- return false;
- }
- return true;
-}
-
-static bool PinFile(int file_fd, const std::string& file_path, uint32_t fs_type) {
- if (fs_type != F2FS_SUPER_MAGIC) {
- // No pinning necessary for ext4/msdos. The blocks, once allocated, are
- // expected to be fixed.
- return true;
- }
-
-// F2FS-specific ioctl
-// It requires the below kernel commit merged in v4.16-rc1.
-// 1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
-// In android-4.4,
-// 56ee1e817908 ("f2fs: updates on v4.16-rc1")
-// In android-4.9,
-// 2f17e34672a8 ("f2fs: updates on v4.16-rc1")
-// In android-4.14,
-// ce767d9a55bc ("f2fs: updates on v4.16-rc1")
-#ifndef F2FS_IOC_SET_PIN_FILE
-#ifndef F2FS_IOCTL_MAGIC
-#define F2FS_IOCTL_MAGIC 0xf5
-#endif
-#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
-#endif
-
- uint32_t pin_status = 1;
- int error = ioctl(file_fd, F2FS_IOC_SET_PIN_FILE, &pin_status);
- if (error < 0) {
- if ((errno == ENOTTY) || (errno == ENOTSUP)) {
- PLOG(ERROR) << "Failed to pin file, not supported by kernel: " << file_path;
- } else {
- PLOG(ERROR) << "Failed to pin file: " << file_path;
- }
- return false;
- }
-
- return true;
-}
-
-static bool IsFilePinned(int file_fd, const std::string& file_path, uint32_t fs_type) {
- if (fs_type != F2FS_SUPER_MAGIC) {
- // No pinning necessary for ext4 or vfat. The blocks, once allocated,
- // are expected to be fixed.
- return true;
- }
-
-// F2FS-specific ioctl
-// It requires the below kernel commit merged in v4.16-rc1.
-// 1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
-// In android-4.4,
-// 56ee1e817908 ("f2fs: updates on v4.16-rc1")
-// In android-4.9,
-// 2f17e34672a8 ("f2fs: updates on v4.16-rc1")
-// In android-4.14,
-// ce767d9a55bc ("f2fs: updates on v4.16-rc1")
-#ifndef F2FS_IOC_GET_PIN_FILE
-#ifndef F2FS_IOCTL_MAGIC
-#define F2FS_IOCTL_MAGIC 0xf5
-#endif
-#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
-#endif
-
- // f2fs: export FS_NOCOW_FL flag to user
- uint32_t flags;
- int error = ioctl(file_fd, FS_IOC_GETFLAGS, &flags);
- if (error < 0) {
- if ((errno == ENOTTY) || (errno == ENOTSUP)) {
- PLOG(ERROR) << "Failed to get flags, not supported by kernel: " << file_path;
- } else {
- PLOG(ERROR) << "Failed to get flags: " << file_path;
- }
- return false;
- }
- if (!(flags & FS_NOCOW_FL)) {
- LOG(ERROR) << "It is not pinned: " << file_path;
- return false;
- }
-
- // F2FS_IOC_GET_PIN_FILE returns the number of blocks moved.
- uint32_t moved_blocks_nr;
- error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);
- if (error < 0) {
- if ((errno == ENOTTY) || (errno == ENOTSUP)) {
- PLOG(ERROR) << "Failed to get file pin status, not supported by kernel: " << file_path;
- } else {
- PLOG(ERROR) << "Failed to get file pin status: " << file_path;
- }
- return false;
- }
-
- if (moved_blocks_nr) {
- LOG(ERROR) << moved_blocks_nr << " blocks moved in file " << file_path;
- }
- return moved_blocks_nr == 0;
-}
-
-bool FiemapWriter::HasPinnedExtents(const std::string& file_path) {
- android::base::unique_fd fd(open(file_path.c_str(), O_NOFOLLOW | O_CLOEXEC | O_RDONLY));
- if (fd < 0) {
- PLOG(ERROR) << "open: " << file_path;
- return false;
- }
-
- struct statfs64 sfs;
- if (fstatfs64(fd, &sfs)) {
- PLOG(ERROR) << "fstatfs64: " << file_path;
- return false;
- }
- return IsFilePinned(fd, file_path, sfs.f_type);
-}
-
-static bool ReadFiemap(int file_fd, const std::string& file_path,
- std::vector<struct fiemap_extent>* extents) {
- uint64_t fiemap_size =
- sizeof(struct fiemap_extent) + kMaxExtents * sizeof(struct fiemap_extent);
- auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, fiemap_size), free);
- if (buffer == nullptr) {
- LOG(ERROR) << "Failed to allocate memory for fiemap";
- return false;
- }
-
- struct fiemap* fiemap = reinterpret_cast<struct fiemap*>(buffer.get());
- fiemap->fm_start = 0;
- fiemap->fm_length = UINT64_MAX;
- // make sure file is synced to disk before we read the fiemap
- fiemap->fm_flags = FIEMAP_FLAG_SYNC;
- fiemap->fm_extent_count = kMaxExtents;
-
- if (ioctl(file_fd, FS_IOC_FIEMAP, fiemap)) {
- PLOG(ERROR) << "Failed to get FIEMAP from the kernel for file: " << file_path;
- return false;
- }
-
- if (fiemap->fm_mapped_extents == 0) {
- LOG(ERROR) << "File " << file_path << " has zero extents";
- return false;
- }
-
- // Iterate through each extent read and make sure its valid before adding it to the vector
- bool last_extent_seen = false;
- struct fiemap_extent* extent = &fiemap->fm_extents[0];
- for (uint32_t i = 0; i < fiemap->fm_mapped_extents; i++, extent++) {
- // LogExtent(i + 1, *extent);
- if (extent->fe_flags & kUnsupportedExtentFlags) {
- LOG(ERROR) << "Extent " << i + 1 << " of file " << file_path
- << " has unsupported flags";
- extents->clear();
- return false;
- }
-
- if (extent->fe_flags & FIEMAP_EXTENT_LAST) {
- last_extent_seen = true;
- if (i != (fiemap->fm_mapped_extents - 1)) {
- LOG(WARNING) << "Extents are being received out-of-order";
- }
- }
- extents->emplace_back(std::move(*extent));
- }
-
- if (!last_extent_seen) {
- // The file is possibly too fragmented.
- if (fiemap->fm_mapped_extents == kMaxExtents) {
- LOG(ERROR) << "File is too fragmented, needs more than " << kMaxExtents << " extents.";
- }
- extents->clear();
- }
-
- return last_extent_seen;
-}
-
-static bool ReadFibmap(int file_fd, const std::string& file_path,
- std::vector<struct fiemap_extent>* extents) {
- struct stat s;
- if (fstat(file_fd, &s)) {
- PLOG(ERROR) << "Failed to stat " << file_path;
- return false;
- }
-
- unsigned int blksize;
- if (ioctl(file_fd, FIGETBSZ, &blksize) < 0) {
- PLOG(ERROR) << "Failed to get FIGETBSZ for " << file_path;
- return false;
- }
- if (!blksize) {
- LOG(ERROR) << "Invalid filesystem block size: " << blksize;
- return false;
- }
-
- uint64_t num_blocks = (s.st_size + blksize - 1) / blksize;
- if (num_blocks > std::numeric_limits<uint32_t>::max()) {
- LOG(ERROR) << "Too many blocks for FIBMAP (" << num_blocks << ")";
- return false;
- }
-
- for (uint32_t last_block, block_number = 0; block_number < num_blocks; block_number++) {
- uint32_t block = block_number;
- if (ioctl(file_fd, FIBMAP, &block)) {
- PLOG(ERROR) << "Failed to get FIBMAP for file " << file_path;
- return false;
- }
- if (!block) {
- LOG(ERROR) << "Logical block " << block_number << " is a hole, which is not supported";
- return false;
- }
-
- if (!extents->empty() && block == last_block + 1) {
- extents->back().fe_length += blksize;
- } else {
- extents->push_back(fiemap_extent{.fe_logical = block_number,
- .fe_physical = static_cast<uint64_t>(block) * blksize,
- .fe_length = static_cast<uint64_t>(blksize),
- .fe_flags = 0});
- }
- last_block = block;
- }
- return true;
-}
-
-FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create,
- std::function<bool(uint64_t, uint64_t)> progress) {
- // if 'create' is false, open an existing file and do not truncate.
- int open_flags = O_RDWR | O_CLOEXEC;
- if (create) {
- if (access(file_path.c_str(), F_OK) == 0) {
- LOG(WARNING) << "File " << file_path << " already exists, truncating";
- }
- open_flags |= O_CREAT | O_TRUNC;
- }
- ::android::base::unique_fd file_fd(
- TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags, S_IRUSR | S_IWUSR)));
- if (file_fd < 0) {
- PLOG(ERROR) << "Failed to create file at: " << file_path;
- return nullptr;
- }
-
- std::string abs_path;
- if (!::android::base::Realpath(file_path, &abs_path)) {
- PLOG(ERROR) << "Invalid file path: " << file_path;
- cleanup(file_path, create);
- return nullptr;
- }
-
- std::string bdev_path;
- if (!GetBlockDeviceForFile(abs_path, &bdev_path)) {
- LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
- cleanup(abs_path, create);
- return nullptr;
- }
-
- ::android::base::unique_fd bdev_fd(
- TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDONLY | O_CLOEXEC)));
- if (bdev_fd < 0) {
- PLOG(ERROR) << "Failed to open block device: " << bdev_path;
- cleanup(file_path, create);
- return nullptr;
- }
-
- uint64_t bdevsz;
- if (!GetBlockDeviceSize(bdev_fd, bdev_path, &bdevsz)) {
- LOG(ERROR) << "Failed to get block device size for : " << bdev_path;
- cleanup(file_path, create);
- return nullptr;
- }
-
- if (!create) {
- file_size = GetFileSize(abs_path);
- if (file_size == 0) {
- LOG(ERROR) << "Invalid file size of zero bytes for file: " << abs_path;
- return nullptr;
- }
- }
-
- uint64_t blocksz;
- uint32_t fs_type;
- if (!PerformFileChecks(abs_path, file_size, &blocksz, &fs_type)) {
- LOG(ERROR) << "Failed to validate file or file system for file:" << abs_path;
- cleanup(abs_path, create);
- return nullptr;
- }
-
- // Align up to the nearest block size.
- if (file_size % blocksz) {
- file_size += blocksz - (file_size % blocksz);
- }
-
- if (create) {
- if (!AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress))) {
- LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size
- << " bytes";
- cleanup(abs_path, create);
- return nullptr;
- }
- }
-
- // f2fs may move the file blocks around.
- if (!PinFile(file_fd, abs_path, fs_type)) {
- cleanup(abs_path, create);
- LOG(ERROR) << "Failed to pin the file in storage";
- return nullptr;
- }
-
- // now allocate the FiemapWriter and start setting it up
- FiemapUniquePtr fmap(new FiemapWriter());
- switch (fs_type) {
- case EXT4_SUPER_MAGIC:
- case F2FS_SUPER_MAGIC:
- if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
- LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
- cleanup(abs_path, create);
- return nullptr;
- }
- break;
- case MSDOS_SUPER_MAGIC:
- if (!ReadFibmap(file_fd, abs_path, &fmap->extents_)) {
- LOG(ERROR) << "Failed to read fibmap of file: " << abs_path;
- cleanup(abs_path, create);
- return nullptr;
- }
- break;
- }
-
- fmap->file_path_ = abs_path;
- fmap->bdev_path_ = bdev_path;
- fmap->file_size_ = file_size;
- fmap->bdev_size_ = bdevsz;
- fmap->fs_type_ = fs_type;
- fmap->block_size_ = blocksz;
-
- LOG(VERBOSE) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
- << bdev_path;
- return fmap;
-}
-
-} // namespace fiemap_writer
-} // namespace android
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
deleted file mode 100644
index dda7dfd..0000000
--- a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
+++ /dev/null
@@ -1,541 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
-#include <libdm/loop_control.h>
-#include <libfiemap_writer/fiemap_writer.h>
-#include <libfiemap_writer/split_fiemap_writer.h>
-
-#include "utility.h"
-
-namespace android {
-namespace fiemap_writer {
-
-using namespace std;
-using namespace std::string_literals;
-using namespace android::fiemap_writer;
-using unique_fd = android::base::unique_fd;
-using LoopDevice = android::dm::LoopDevice;
-
-std::string gTestDir;
-uint64_t testfile_size = 536870912; // default of 512MiB
-size_t gBlockSize = 0;
-
-class FiemapWriterTest : public ::testing::Test {
- protected:
- void SetUp() override {
- const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
- testfile = gTestDir + "/"s + tinfo->name();
- }
-
- void TearDown() override { unlink(testfile.c_str()); }
-
- // name of the file we use for testing
- std::string testfile;
-};
-
-class SplitFiemapTest : public ::testing::Test {
- protected:
- void SetUp() override {
- const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
- testfile = gTestDir + "/"s + tinfo->name();
- }
-
- void TearDown() override {
- std::string message;
- if (!SplitFiemap::RemoveSplitFiles(testfile, &message)) {
- cerr << "Could not remove all split files: " << message;
- }
- }
-
- // name of the file we use for testing
- std::string testfile;
-};
-
-TEST_F(FiemapWriterTest, CreateImpossiblyLargeFile) {
- // Try creating a file of size ~100TB but aligned to
- // 512 byte to make sure block alignment tests don't
- // fail.
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1099511627997184);
- EXPECT_EQ(fptr, nullptr);
- EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
- EXPECT_EQ(errno, ENOENT);
-}
-
-TEST_F(FiemapWriterTest, CreateUnalignedFile) {
- // Try creating a file of size 4097 bytes which is guaranteed
- // to be unaligned to all known block sizes.
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize + 1);
- ASSERT_NE(fptr, nullptr);
- ASSERT_EQ(fptr->size(), gBlockSize * 2);
-}
-
-TEST_F(FiemapWriterTest, CheckFilePath) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
- ASSERT_NE(fptr, nullptr);
- EXPECT_EQ(fptr->size(), gBlockSize);
- EXPECT_EQ(fptr->file_path(), testfile);
- EXPECT_EQ(access(testfile.c_str(), F_OK), 0);
-}
-
-TEST_F(FiemapWriterTest, CheckFileSize) {
- // Create a large-ish file and test that the expected size matches.
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1024 * 1024 * 16);
- ASSERT_NE(fptr, nullptr);
-
- struct stat s;
- ASSERT_EQ(stat(testfile.c_str(), &s), 0);
- EXPECT_EQ(static_cast<uint64_t>(s.st_size), fptr->size());
-}
-
-TEST_F(FiemapWriterTest, CheckProgress) {
- std::vector<uint64_t> expected;
- size_t invocations = 0;
- auto callback = [&](uint64_t done, uint64_t total) -> bool {
- if (invocations >= expected.size()) {
- return false;
- }
- EXPECT_EQ(done, expected[invocations]);
- EXPECT_EQ(total, gBlockSize);
- invocations++;
- return true;
- };
-
- expected.push_back(gBlockSize);
-
- auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
- EXPECT_NE(ptr, nullptr);
- EXPECT_EQ(invocations, expected.size());
-}
-
-TEST_F(FiemapWriterTest, CheckPinning) {
- auto ptr = FiemapWriter::Open(testfile, 4096);
- ASSERT_NE(ptr, nullptr);
- EXPECT_TRUE(FiemapWriter::HasPinnedExtents(testfile));
-}
-
-TEST_F(FiemapWriterTest, CheckBlockDevicePath) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
- EXPECT_EQ(fptr->size(), gBlockSize);
- EXPECT_EQ(fptr->bdev_path().find("/dev/block/"), size_t(0));
- EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
-}
-
-TEST_F(FiemapWriterTest, CheckFileCreated) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 32768);
- ASSERT_NE(fptr, nullptr);
- unique_fd fd(open(testfile.c_str(), O_RDONLY));
- EXPECT_GT(fd, -1);
-}
-
-TEST_F(FiemapWriterTest, CheckFileSizeActual) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
- ASSERT_NE(fptr, nullptr);
-
- struct stat sb;
- ASSERT_EQ(stat(testfile.c_str(), &sb), 0);
- EXPECT_GE(sb.st_size, testfile_size);
-}
-
-TEST_F(FiemapWriterTest, CheckFileExtents) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
- ASSERT_NE(fptr, nullptr);
- EXPECT_GT(fptr->extents().size(), 0);
-}
-
-TEST_F(FiemapWriterTest, ExistingFile) {
- // Create the file.
- { ASSERT_NE(FiemapWriter::Open(testfile, gBlockSize), nullptr); }
- // Test that we can still open it.
- {
- auto ptr = FiemapWriter::Open(testfile, 0, false);
- ASSERT_NE(ptr, nullptr);
- EXPECT_GT(ptr->extents().size(), 0);
- }
-}
-
-TEST_F(FiemapWriterTest, FileDeletedOnError) {
- auto callback = [](uint64_t, uint64_t) -> bool { return false; };
- auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
- EXPECT_EQ(ptr, nullptr);
- EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
- EXPECT_EQ(errno, ENOENT);
-}
-
-TEST_F(FiemapWriterTest, MaxBlockSize) {
- ASSERT_GT(DetermineMaximumFileSize(testfile), 0);
-}
-
-TEST_F(FiemapWriterTest, FibmapBlockAddressing) {
- FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
- ASSERT_NE(fptr, nullptr);
-
- switch (fptr->fs_type()) {
- case F2FS_SUPER_MAGIC:
- case EXT4_SUPER_MAGIC:
- // Skip the test for FIEMAP supported filesystems. This is really
- // because f2fs/ext4 have caches that seem to defeat reading back
- // directly from the block device, and writing directly is too
- // dangerous.
- std::cout << "Skipping test, filesystem does not use FIBMAP\n";
- return;
- }
-
- bool uses_dm;
- std::string bdev_path;
- ASSERT_TRUE(FiemapWriter::GetBlockDeviceForFile(testfile, &bdev_path, &uses_dm));
-
- if (uses_dm) {
- // We could use a device-mapper wrapper here to bypass encryption, but
- // really this test is for FIBMAP correctness on VFAT (where encryption
- // is never used), so we don't bother.
- std::cout << "Skipping test, block device is metadata encrypted\n";
- return;
- }
-
- std::string data(fptr->size(), '\0');
- for (size_t i = 0; i < data.size(); i++) {
- data[i] = 'A' + static_cast<char>(data.size() % 26);
- }
-
- {
- unique_fd fd(open(testfile.c_str(), O_WRONLY | O_CLOEXEC));
- ASSERT_GE(fd, 0);
- ASSERT_TRUE(android::base::WriteFully(fd, data.data(), data.size()));
- ASSERT_EQ(fsync(fd), 0);
- }
-
- ASSERT_FALSE(fptr->extents().empty());
- const auto& first_extent = fptr->extents()[0];
-
- unique_fd bdev(open(fptr->bdev_path().c_str(), O_RDONLY | O_CLOEXEC));
- ASSERT_GE(bdev, 0);
-
- off_t where = first_extent.fe_physical;
- ASSERT_EQ(lseek(bdev, where, SEEK_SET), where);
-
- // Note: this will fail on encrypted folders.
- std::string actual(data.size(), '\0');
- ASSERT_GE(first_extent.fe_length, data.size());
- ASSERT_TRUE(android::base::ReadFully(bdev, actual.data(), actual.size()));
- EXPECT_EQ(memcmp(actual.data(), data.data(), data.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, Create) {
- auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
- ASSERT_NE(ptr, nullptr);
-
- auto extents = ptr->extents();
-
- // Destroy the fiemap, closing file handles. This should not delete them.
- ptr = nullptr;
-
- std::vector<std::string> files;
- ASSERT_TRUE(SplitFiemap::GetSplitFileList(testfile, &files));
- for (const auto& path : files) {
- EXPECT_EQ(access(path.c_str(), F_OK), 0);
- }
-
- ASSERT_GE(extents.size(), files.size());
-}
-
-TEST_F(SplitFiemapTest, Open) {
- {
- auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
- ASSERT_NE(ptr, nullptr);
- }
-
- auto ptr = SplitFiemap::Open(testfile);
- ASSERT_NE(ptr, nullptr);
-
- auto extents = ptr->extents();
- ASSERT_GE(extents.size(), 24);
-}
-
-TEST_F(SplitFiemapTest, DeleteOnFail) {
- auto ptr = SplitFiemap::Create(testfile, 1024 * 1024 * 100, 1);
- ASSERT_EQ(ptr, nullptr);
-
- std::string first_file = testfile + ".0001";
- ASSERT_NE(access(first_file.c_str(), F_OK), 0);
- ASSERT_EQ(errno, ENOENT);
- ASSERT_NE(access(testfile.c_str(), F_OK), 0);
- ASSERT_EQ(errno, ENOENT);
-}
-
-static string ReadSplitFiles(const std::string& base_path, size_t num_files) {
- std::string result;
- for (int i = 0; i < num_files; i++) {
- std::string path = base_path + android::base::StringPrintf(".%04d", i);
- std::string data;
- if (!android::base::ReadFileToString(path, &data)) {
- return {};
- }
- result += data;
- }
- return result;
-}
-
-TEST_F(SplitFiemapTest, WriteWholeFile) {
- static constexpr size_t kChunkSize = 32768;
- static constexpr size_t kSize = kChunkSize * 3;
- auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
- ASSERT_NE(ptr, nullptr);
-
- auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
- for (size_t i = 0; i < kSize / sizeof(int); i++) {
- buffer[i] = i;
- }
- ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
-
- std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
- auto actual = ReadSplitFiles(testfile, 3);
- ASSERT_EQ(expected.size(), actual.size());
- EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, WriteFileInChunks1) {
- static constexpr size_t kChunkSize = 32768;
- static constexpr size_t kSize = kChunkSize * 3;
- auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
- ASSERT_NE(ptr, nullptr);
-
- auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
- for (size_t i = 0; i < kSize / sizeof(int); i++) {
- buffer[i] = i;
- }
-
- // Write in chunks of 1000 (so some writes straddle the boundary of two
- // files).
- size_t bytes_written = 0;
- while (bytes_written < kSize) {
- size_t to_write = std::min(kSize - bytes_written, (size_t)1000);
- char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
- ASSERT_TRUE(ptr->Write(data, to_write));
- bytes_written += to_write;
- }
-
- std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
- auto actual = ReadSplitFiles(testfile, 3);
- ASSERT_EQ(expected.size(), actual.size());
- EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, WriteFileInChunks2) {
- static constexpr size_t kChunkSize = 32768;
- static constexpr size_t kSize = kChunkSize * 3;
- auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
- ASSERT_NE(ptr, nullptr);
-
- auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
- for (size_t i = 0; i < kSize / sizeof(int); i++) {
- buffer[i] = i;
- }
-
- // Write in chunks of 32KiB so every write is exactly at the end of the
- // current file.
- size_t bytes_written = 0;
- while (bytes_written < kSize) {
- size_t to_write = std::min(kSize - bytes_written, kChunkSize);
- char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
- ASSERT_TRUE(ptr->Write(data, to_write));
- bytes_written += to_write;
- }
-
- std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
- auto actual = ReadSplitFiles(testfile, 3);
- ASSERT_EQ(expected.size(), actual.size());
- EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, WritePastEnd) {
- static constexpr size_t kChunkSize = 32768;
- static constexpr size_t kSize = kChunkSize * 3;
- auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
- ASSERT_NE(ptr, nullptr);
-
- auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
- for (size_t i = 0; i < kSize / sizeof(int); i++) {
- buffer[i] = i;
- }
- ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
- ASSERT_FALSE(ptr->Write(buffer.get(), kSize));
-}
-
-class VerifyBlockWritesExt4 : public ::testing::Test {
- // 2GB Filesystem and 4k block size by default
- static constexpr uint64_t block_size = 4096;
- static constexpr uint64_t fs_size = 2147483648;
-
- protected:
- void SetUp() override {
- fs_path = std::string(getenv("TMPDIR")) + "/ext4_2G.img";
- uint64_t count = fs_size / block_size;
- std::string dd_cmd =
- ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
- " count=%" PRIu64 " > /dev/null 2>&1",
- fs_path.c_str(), block_size, count);
- std::string mkfs_cmd =
- ::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path.c_str());
- // create mount point
- mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
- ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
- // create file for the file system
- int ret = system(dd_cmd.c_str());
- ASSERT_EQ(ret, 0);
- // Get and attach a loop device to the filesystem we created
- LoopDevice loop_dev(fs_path);
- ASSERT_TRUE(loop_dev.valid());
- // create file system
- ret = system(mkfs_cmd.c_str());
- ASSERT_EQ(ret, 0);
-
- // mount the file system
- ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "ext4", 0, nullptr), 0);
- }
-
- void TearDown() override {
- umount(mntpoint.c_str());
- rmdir(mntpoint.c_str());
- unlink(fs_path.c_str());
- }
-
- std::string mntpoint;
- std::string fs_path;
-};
-
-class VerifyBlockWritesF2fs : public ::testing::Test {
- // 2GB Filesystem and 4k block size by default
- static constexpr uint64_t block_size = 4096;
- static constexpr uint64_t fs_size = 2147483648;
-
- protected:
- void SetUp() override {
- fs_path = std::string(getenv("TMPDIR")) + "/f2fs_2G.img";
- uint64_t count = fs_size / block_size;
- std::string dd_cmd =
- ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
- " count=%" PRIu64 " > /dev/null 2>&1",
- fs_path.c_str(), block_size, count);
- std::string mkfs_cmd =
- ::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path.c_str());
- // create mount point
- mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
- ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
- // create file for the file system
- int ret = system(dd_cmd.c_str());
- ASSERT_EQ(ret, 0);
- // Get and attach a loop device to the filesystem we created
- LoopDevice loop_dev(fs_path);
- ASSERT_TRUE(loop_dev.valid());
- // create file system
- ret = system(mkfs_cmd.c_str());
- ASSERT_EQ(ret, 0);
-
- // mount the file system
- ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "f2fs", 0, nullptr), 0);
- }
-
- void TearDown() override {
- umount(mntpoint.c_str());
- rmdir(mntpoint.c_str());
- unlink(fs_path.c_str());
- }
-
- std::string mntpoint;
- std::string fs_path;
-};
-
-bool DetermineBlockSize() {
- struct statfs s;
- if (statfs(gTestDir.c_str(), &s)) {
- std::cerr << "Could not call statfs: " << strerror(errno) << "\n";
- return false;
- }
- if (!s.f_bsize) {
- std::cerr << "Invalid block size: " << s.f_bsize << "\n";
- return false;
- }
-
- gBlockSize = s.f_bsize;
- return true;
-}
-
-} // namespace fiemap_writer
-} // namespace android
-
-using namespace android::fiemap_writer;
-
-int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
- if (argc > 1 && argv[1] == "-h"s) {
- cerr << "Usage: [test_dir] [file_size]\n";
- cerr << "\n";
- cerr << "Note: test_dir must be a writable, unencrypted directory.\n";
- exit(EXIT_FAILURE);
- }
- ::android::base::InitLogging(argv, ::android::base::StderrLogger);
-
- std::string root_dir = "/data/local/unencrypted";
- if (access(root_dir.c_str(), F_OK)) {
- root_dir = "/data";
- }
-
- std::string tempdir = root_dir + "/XXXXXX"s;
- if (!mkdtemp(tempdir.data())) {
- cerr << "unable to create tempdir on " << root_dir << "\n";
- exit(EXIT_FAILURE);
- }
- if (!android::base::Realpath(tempdir, &gTestDir)) {
- cerr << "unable to find realpath for " << tempdir;
- exit(EXIT_FAILURE);
- }
-
- if (argc > 2) {
- testfile_size = strtoull(argv[2], NULL, 0);
- if (testfile_size == ULLONG_MAX) {
- testfile_size = 512 * 1024 * 1024;
- }
- }
-
- if (!DetermineBlockSize()) {
- exit(EXIT_FAILURE);
- }
-
- auto result = RUN_ALL_TESTS();
-
- std::string cmd = "rm -rf " + gTestDir;
- system(cmd.c_str());
-
- return result;
-}
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
deleted file mode 100644
index ee79262..0000000
--- a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2018 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 <linux/fiemap.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <functional>
-#include <string>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-
-namespace android {
-namespace fiemap_writer {
-
-class FiemapWriter;
-using FiemapUniquePtr = std::unique_ptr<FiemapWriter>;
-
-class FiemapWriter final {
- public:
- // Factory method for FiemapWriter.
- // The method returns FiemapUniquePtr that contains all the data necessary to be able to write
- // to the given file directly using raw block i/o. The optional progress callback will be
- // invoked, if create is true, while the file is being initialized. It receives the bytes
- // written and the number of total bytes. If the callback returns false, the operation will
- // fail.
- //
- // Note: when create is true, the file size will be aligned up to the nearest file system
- // block.
- static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size,
- bool create = true,
- std::function<bool(uint64_t, uint64_t)> progress = {});
-
- // Check that a file still has the same extents since it was last opened with FiemapWriter,
- // assuming the file was not resized outside of FiemapWriter. Returns false either on error
- // or if the file was not pinned.
- //
- // This will always return true on Ext4. On F2FS, it will return true if either of the
- // following cases are true:
- // - The file was never pinned.
- // - The file is pinned and has not been moved by the GC.
- // Thus, this method should only be called for pinned files (such as those returned by
- // FiemapWriter::Open).
- static bool HasPinnedExtents(const std::string& file_path);
-
- // Returns the underlying block device of a file. This will look past device-mapper layers
- // as long as each layer would not change block mappings (i.e., dm-crypt, dm-bow, and dm-
- // default-key tables are okay; dm-linear is not). If a mapping such as dm-linear is found,
- // it will be returned in place of any physical block device.
- //
- // It is the caller's responsibility to check whether the returned block device is acceptable.
- // Gsid, for example, will only accept /dev/block/by-name/userdata as the bottom device.
- // Callers can check the device name (dm- or loop prefix), inspect sysfs, or compare the major
- // number against a boot device.
- //
- // If device-mapper nodes were encountered, then |uses_dm| will be set to true.
- static bool GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
- bool* uses_dm = nullptr);
-
- ~FiemapWriter() = default;
-
- const std::string& file_path() const { return file_path_; };
- uint64_t size() const { return file_size_; };
- const std::string& bdev_path() const { return bdev_path_; };
- uint64_t block_size() const { return block_size_; };
- const std::vector<struct fiemap_extent>& extents() { return extents_; };
- uint32_t fs_type() const { return fs_type_; }
-
- // Non-copyable & Non-movable
- FiemapWriter(const FiemapWriter&) = delete;
- FiemapWriter& operator=(const FiemapWriter&) = delete;
- FiemapWriter& operator=(FiemapWriter&&) = delete;
- FiemapWriter(FiemapWriter&&) = delete;
-
- private:
- // Name of the file managed by this class.
- std::string file_path_;
- // Block device on which we have created the file.
- std::string bdev_path_;
-
- // Size in bytes of the file this class is writing
- uint64_t file_size_;
-
- // total size in bytes of the block device
- uint64_t bdev_size_;
-
- // Filesystem type where the file is being created.
- // See: <uapi/linux/magic.h> for filesystem magic numbers
- uint32_t fs_type_;
-
- // block size as reported by the kernel of the underlying block device;
- uint64_t block_size_;
-
- // This file's fiemap
- std::vector<struct fiemap_extent> extents_;
-
- FiemapWriter() = default;
-};
-
-} // namespace fiemap_writer
-} // namespace android
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
deleted file mode 100644
index 7b977e1..0000000
--- a/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-#include <functional>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-
-#include "fiemap_writer.h"
-
-namespace android {
-namespace fiemap_writer {
-
-// Wrapper around FiemapWriter that is able to split images across files if
-// necessary.
-class SplitFiemap final {
- public:
- using ProgressCallback = std::function<bool(uint64_t, uint64_t)>;
-
- // Create a new split fiemap file. If |max_piece_size| is 0, the number of
- // pieces will be determined automatically by detecting the filesystem.
- // Otherwise, the file will be split evenly (with the remainder in the
- // final file).
- static std::unique_ptr<SplitFiemap> Create(const std::string& file_path, uint64_t file_size,
- uint64_t max_piece_size,
- ProgressCallback progress = {});
-
- // Open an existing split fiemap file.
- static std::unique_ptr<SplitFiemap> Open(const std::string& file_path);
-
- ~SplitFiemap();
-
- // Return a list of all files created for a split file.
- static bool GetSplitFileList(const std::string& file_path, std::vector<std::string>* list);
-
- // Destroy all components of a split file. If the root file does not exist,
- // this returns true and does not report an error.
- static bool RemoveSplitFiles(const std::string& file_path, std::string* message = nullptr);
-
- // Return whether all components of a split file still have pinned extents.
- bool HasPinnedExtents() const;
-
- // Helper method for writing data that spans files. Note there is no seek
- // method (yet); this starts at 0 and increments the position by |bytes|.
- bool Write(const void* data, uint64_t bytes);
-
- // Flush all writes to all split files.
- bool Flush();
-
- const std::vector<struct fiemap_extent>& extents();
- uint32_t block_size() const;
- uint64_t size() const { return total_size_; }
- const std::string& bdev_path() const;
-
- // Non-copyable & Non-movable
- SplitFiemap(const SplitFiemap&) = delete;
- SplitFiemap& operator=(const SplitFiemap&) = delete;
- SplitFiemap& operator=(SplitFiemap&&) = delete;
- SplitFiemap(SplitFiemap&&) = delete;
-
- private:
- SplitFiemap() = default;
- void AddFile(FiemapUniquePtr&& file);
-
- bool creating_ = false;
- std::string list_file_;
- std::vector<FiemapUniquePtr> files_;
- std::vector<struct fiemap_extent> extents_;
- uint64_t total_size_ = 0;
-
- // Most recently open file and position for Write().
- size_t cursor_index_ = 0;
- uint64_t cursor_file_pos_ = 0;
- android::base::unique_fd cursor_fd_;
-};
-
-} // namespace fiemap_writer
-} // namespace android
diff --git a/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp b/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
deleted file mode 100644
index 16a82d2..0000000
--- a/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <libfiemap_writer/split_fiemap_writer.h>
-
-#include <fcntl.h>
-#include <stdint.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-
-#include "utility.h"
-
-namespace android {
-namespace fiemap_writer {
-
-using android::base::unique_fd;
-
-// We use a four-digit suffix at the end of filenames.
-static const size_t kMaxFilePieces = 500;
-
-std::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, uint64_t file_size,
- uint64_t max_piece_size,
- ProgressCallback progress) {
- if (!file_size) {
- LOG(ERROR) << "Cannot create a fiemap for a 0-length file: " << file_path;
- return nullptr;
- }
-
- if (!max_piece_size) {
- max_piece_size = DetermineMaximumFileSize(file_path);
- if (!max_piece_size) {
- LOG(ERROR) << "Could not determine maximum file size for " << file_path;
- return nullptr;
- }
- }
-
- // Call |progress| only when the total percentage would significantly change.
- int permille = -1;
- uint64_t total_bytes_written = 0;
- auto on_progress = [&](uint64_t written, uint64_t) -> bool {
- uint64_t actual_written = total_bytes_written + written;
- int new_permille = (actual_written * 1000) / file_size;
- if (new_permille != permille && actual_written < file_size) {
- if (progress && !progress(actual_written, file_size)) {
- return false;
- }
- permille = new_permille;
- }
- return true;
- };
-
- std::unique_ptr<SplitFiemap> out(new SplitFiemap());
- out->creating_ = true;
- out->list_file_ = file_path;
-
- // Create the split files.
- uint64_t remaining_bytes = file_size;
- while (remaining_bytes) {
- if (out->files_.size() >= kMaxFilePieces) {
- LOG(ERROR) << "Requested size " << file_size << " created too many split files";
- return nullptr;
- }
- std::string chunk_path =
- android::base::StringPrintf("%s.%04d", file_path.c_str(), (int)out->files_.size());
- uint64_t chunk_size = std::min(max_piece_size, remaining_bytes);
- auto writer = FiemapWriter::Open(chunk_path, chunk_size, true, on_progress);
- if (!writer) {
- return nullptr;
- }
-
- // To make sure the alignment doesn't create too much inconsistency, we
- // account the *actual* size, not the requested size.
- total_bytes_written += writer->size();
- // writer->size() is block size aligned and could be bigger than remaining_bytes
- // If remaining_bytes is bigger, set remaining_bytes to 0 to avoid underflow error.
- remaining_bytes = remaining_bytes > writer->size() ? (remaining_bytes - writer->size()) : 0;
- out->AddFile(std::move(writer));
- }
-
- // Create the split file list.
- unique_fd fd(open(out->list_file_.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0660));
- if (fd < 0) {
- PLOG(ERROR) << "Failed to open " << file_path;
- return nullptr;
- }
-
- for (const auto& writer : out->files_) {
- std::string line = android::base::Basename(writer->file_path()) + "\n";
- if (!android::base::WriteFully(fd, line.data(), line.size())) {
- PLOG(ERROR) << "Write failed " << file_path;
- return nullptr;
- }
- }
-
- // Unset this bit, so we don't unlink on destruction.
- out->creating_ = false;
- return out;
-}
-
-std::unique_ptr<SplitFiemap> SplitFiemap::Open(const std::string& file_path) {
- std::vector<std::string> files;
- if (!GetSplitFileList(file_path, &files)) {
- return nullptr;
- }
-
- std::unique_ptr<SplitFiemap> out(new SplitFiemap());
- out->list_file_ = file_path;
-
- for (const auto& file : files) {
- auto writer = FiemapWriter::Open(file, 0, false);
- if (!writer) {
- // Error was logged in Open().
- return nullptr;
- }
- out->AddFile(std::move(writer));
- }
- return out;
-}
-
-bool SplitFiemap::GetSplitFileList(const std::string& file_path, std::vector<std::string>* list) {
- // This is not the most efficient thing, but it is simple and recovering
- // the fiemap/fibmap is much more expensive.
- std::string contents;
- if (!android::base::ReadFileToString(file_path, &contents, true)) {
- PLOG(ERROR) << "Error reading file: " << file_path;
- return false;
- }
-
- std::vector<std::string> names = android::base::Split(contents, "\n");
- std::string dir = android::base::Dirname(file_path);
- for (const auto& name : names) {
- if (!name.empty()) {
- list->emplace_back(dir + "/" + name);
- }
- }
- return true;
-}
-
-bool SplitFiemap::RemoveSplitFiles(const std::string& file_path, std::string* message) {
- // Early exit if this does not exist, and do not report an error.
- if (access(file_path.c_str(), F_OK) && errno == ENOENT) {
- return true;
- }
-
- bool ok = true;
- std::vector<std::string> files;
- if (GetSplitFileList(file_path, &files)) {
- for (const auto& file : files) {
- ok &= android::base::RemoveFileIfExists(file, message);
- }
- }
- ok &= android::base::RemoveFileIfExists(file_path, message);
- return ok;
-}
-
-bool SplitFiemap::HasPinnedExtents() const {
- for (const auto& file : files_) {
- if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
- return false;
- }
- }
- return true;
-}
-
-const std::vector<struct fiemap_extent>& SplitFiemap::extents() {
- if (extents_.empty()) {
- for (const auto& file : files_) {
- const auto& extents = file->extents();
- extents_.insert(extents_.end(), extents.begin(), extents.end());
- }
- }
- return extents_;
-}
-
-bool SplitFiemap::Write(const void* data, uint64_t bytes) {
- // Open the current file.
- FiemapWriter* file = files_[cursor_index_].get();
-
- const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(data);
- uint64_t bytes_remaining = bytes;
- while (bytes_remaining) {
- // How many bytes can we write into the current file?
- uint64_t file_bytes_left = file->size() - cursor_file_pos_;
- if (!file_bytes_left) {
- if (cursor_index_ == files_.size() - 1) {
- LOG(ERROR) << "write past end of file requested";
- return false;
- }
-
- // No space left in the current file, but we have more files to
- // use, so prep the next one.
- cursor_fd_ = {};
- cursor_file_pos_ = 0;
- file = files_[++cursor_index_].get();
- file_bytes_left = file->size();
- }
-
- // Open the current file if it's not open.
- if (cursor_fd_ < 0) {
- cursor_fd_.reset(open(file->file_path().c_str(), O_CLOEXEC | O_WRONLY));
- if (cursor_fd_ < 0) {
- PLOG(ERROR) << "open failed: " << file->file_path();
- return false;
- }
- CHECK(cursor_file_pos_ == 0);
- }
-
- if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
- LOG(ERROR) << "file is no longer pinned: " << file->file_path();
- return false;
- }
-
- uint64_t bytes_to_write = std::min(file_bytes_left, bytes_remaining);
- if (!android::base::WriteFully(cursor_fd_, data_ptr, bytes_to_write)) {
- PLOG(ERROR) << "write failed: " << file->file_path();
- return false;
- }
- data_ptr += bytes_to_write;
- bytes_remaining -= bytes_to_write;
- cursor_file_pos_ += bytes_to_write;
- }
-
- // If we've reached the end of the current file, close it for sanity.
- if (cursor_file_pos_ == file->size()) {
- cursor_fd_ = {};
- }
- return true;
-}
-
-bool SplitFiemap::Flush() {
- for (const auto& file : files_) {
- unique_fd fd(open(file->file_path().c_str(), O_RDONLY | O_CLOEXEC));
- if (fd < 0) {
- PLOG(ERROR) << "open failed: " << file->file_path();
- return false;
- }
- if (fsync(fd)) {
- PLOG(ERROR) << "fsync failed: " << file->file_path();
- return false;
- }
- }
- return true;
-}
-
-SplitFiemap::~SplitFiemap() {
- if (!creating_) {
- return;
- }
-
- // We failed to finish creating, so unlink everything.
- unlink(list_file_.c_str());
- for (auto&& file : files_) {
- std::string path = file->file_path();
- file = nullptr;
-
- unlink(path.c_str());
- }
-}
-
-void SplitFiemap::AddFile(FiemapUniquePtr&& file) {
- total_size_ += file->size();
- files_.emplace_back(std::move(file));
-}
-
-uint32_t SplitFiemap::block_size() const {
- return files_[0]->block_size();
-}
-
-const std::string& SplitFiemap::bdev_path() const {
- return files_[0]->bdev_path();
-}
-
-} // namespace fiemap_writer
-} // namespace android
diff --git a/fs_mgr/libfiemap_writer/testdata/file_32k b/fs_mgr/libfiemap_writer/testdata/file_32k
deleted file mode 100644
index 12f3be4..0000000
--- a/fs_mgr/libfiemap_writer/testdata/file_32k
+++ /dev/null
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/testdata/file_4k b/fs_mgr/libfiemap_writer/testdata/file_4k
deleted file mode 100644
index 08e7df1..0000000
--- a/fs_mgr/libfiemap_writer/testdata/file_4k
+++ /dev/null
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/testdata/unaligned_file b/fs_mgr/libfiemap_writer/testdata/unaligned_file
deleted file mode 100644
index c107c26..0000000
--- a/fs_mgr/libfiemap_writer/testdata/unaligned_file
+++ /dev/null
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/utility.cpp b/fs_mgr/libfiemap_writer/utility.cpp
deleted file mode 100644
index 192ec16..0000000
--- a/fs_mgr/libfiemap_writer/utility.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "utility.h"
-
-#include <stdint.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <libfiemap_writer/fiemap_writer.h>
-
-namespace android {
-namespace fiemap_writer {
-
-uint64_t DetermineMaximumFileSize(const std::string& file_path) {
- // Create the smallest file possible (one block).
- auto writer = FiemapWriter::Open(file_path, 1);
- if (!writer) {
- return 0;
- }
-
- uint64_t result = 0;
- switch (writer->fs_type()) {
- case EXT4_SUPER_MAGIC:
- // The minimum is 16GiB, so just report that. If we wanted we could parse the
- // superblock and figure out if 64-bit support is enabled.
- result = 17179869184ULL;
- break;
- case F2FS_SUPER_MAGIC:
- // Formula is from https://www.kernel.org/doc/Documentation/filesystems/f2fs.txt
- // 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB.
- result = 4329690886144ULL;
- break;
- case MSDOS_SUPER_MAGIC:
- // 4GB-1, which we want aligned to the block size.
- result = 4294967295;
- result -= (result % writer->block_size());
- break;
- default:
- LOG(ERROR) << "Unknown file system type: " << writer->fs_type();
- break;
- }
-
- // Close and delete the temporary file.
- writer = nullptr;
- unlink(file_path.c_str());
-
- return result;
-}
-
-} // namespace fiemap_writer
-} // namespace android
diff --git a/fs_mgr/libfiemap_writer/utility.h b/fs_mgr/libfiemap_writer/utility.h
deleted file mode 100644
index 2d418da..0000000
--- a/fs_mgr/libfiemap_writer/utility.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <string>
-
-namespace android {
-namespace fiemap_writer {
-
-// Given a file that will be created, determine the maximum size its containing
-// filesystem allows. Note this is a theoretical maximum size; free space is
-// ignored entirely.
-uint64_t DetermineMaximumFileSize(const std::string& file_path);
-
-} // namespace fiemap_writer
-} // namespace android
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
new file mode 100644
index 0000000..3a08049
--- /dev/null
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2018 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.
+//
+
+cc_library {
+ name: "libsnapshot",
+ recovery_available: true,
+ defaults: ["fs_mgr_defaults"],
+ cppflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ ],
+ srcs: [
+ "snapshot.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ static_libs: [
+ "libdm",
+ "libext2_uuid",
+ ],
+ export_include_dirs: ["include"],
+}
diff --git a/fs_mgr/libsnapshot/OWNERS b/fs_mgr/libsnapshot/OWNERS
new file mode 100644
index 0000000..0cfa7e4
--- /dev/null
+++ b/fs_mgr/libsnapshot/OWNERS
@@ -0,0 +1,2 @@
+dvander@google.com
+elsk@google.com
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
new file mode 100644
index 0000000..5cfd7fa
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -0,0 +1,88 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdint.h>
+
+#include <chrono>
+#include <memory>
+#include <string>
+
+namespace android {
+namespace snapshot {
+
+enum class UpdateStatus {
+ // No update or merge is in progress.
+ None,
+
+ // An update is pending, but has not been successfully booted yet.
+ Unverified,
+
+ // The kernel is merging in the background.
+ Merging,
+
+ // Merging is complete, and needs to be acknowledged.
+ MergeCompleted
+};
+
+class SnapshotManager final {
+ public:
+ // Return a new SnapshotManager instance, or null on error.
+ static std::unique_ptr<SnapshotManager> New();
+
+ // Create a new snapshot device with the given name, base device, and COW device
+ // size. The new device path will be returned in |dev_path|. If timeout_ms is
+ // greater than zero, this function will wait the given amount of time for
+ // |dev_path| to become available, and fail otherwise. If timeout_ms is 0, then
+ // no wait will occur and |dev_path| may not yet exist on return.
+ bool CreateSnapshot(const std::string& name, const std::string& base_device, uint64_t cow_size,
+ std::string* dev_path, const std::chrono::milliseconds& timeout_ms);
+
+ // Map a snapshot device that was previously created with CreateSnapshot.
+ // If a merge was previously initiated, the device-mapper table will have a
+ // snapshot-merge target instead of a snapshot target. The timeout parameter
+ // is the same as in CreateSnapshotDevice.
+ bool MapSnapshotDevice(const std::string& name, const std::string& base_device,
+ const std::chrono::milliseconds& timeout_ms, std::string* dev_path);
+
+ // Unmap a snapshot device previously mapped with MapSnapshotDevice().
+ bool UnmapSnapshotDevice(const std::string& name);
+
+ // Remove the backing copy-on-write image for the named snapshot. If the
+ // device is still mapped, this will attempt an Unmap, and fail if the
+ // unmap fails.
+ bool DeleteSnapshot(const std::string& name);
+
+ // Initiate a merge on all snapshot devices. This should only be used after an
+ // update has been marked successful after booting.
+ bool InitiateMerge();
+
+ // Wait for the current merge to finish, then perform cleanup when it
+ // completes. It is necessary to call this after InitiateMerge(), or when
+ // a merge is detected for the first time after boot.
+ bool WaitForMerge();
+
+ // Find the status of the current update, if any.
+ //
+ // |progress| depends on the returned status:
+ // None: 0
+ // Unverified: 0
+ // Merging: Value in the range [0, 100)
+ // MergeCompleted: 100
+ UpdateStatus GetUpdateStatus(double* progress);
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
new file mode 100644
index 0000000..3e80239
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -0,0 +1,72 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <libsnapshot/snapshot.h>
+
+namespace android {
+namespace snapshot {
+
+std::unique_ptr<SnapshotManager> SnapshotManager::New() {
+ return std::make_unique<SnapshotManager>();
+}
+
+bool SnapshotManager::CreateSnapshot(const std::string& name, const std::string& base_device,
+ uint64_t cow_size, std::string* dev_path,
+ const std::chrono::milliseconds& timeout_ms) {
+ // (1) Create COW device using libgsi_image.
+ // (2) Create snapshot device using libdm + DmTargetSnapshot.
+ // (3) Record partition in /metadata/ota.
+ (void)name;
+ (void)base_device;
+ (void)cow_size;
+ (void)dev_path;
+ (void)timeout_ms;
+ return false;
+}
+
+bool SnapshotManager::MapSnapshotDevice(const std::string& name, const std::string& base_device,
+ const std::chrono::milliseconds& timeout_ms,
+ std::string* dev_path) {
+ (void)name;
+ (void)base_device;
+ (void)dev_path;
+ (void)timeout_ms;
+ return false;
+}
+
+bool SnapshotManager::UnmapSnapshotDevice(const std::string& name) {
+ (void)name;
+ return false;
+}
+
+bool SnapshotManager::DeleteSnapshot(const std::string& name) {
+ (void)name;
+ return false;
+}
+
+bool SnapshotManager::InitiateMerge() {
+ return false;
+}
+
+bool SnapshotManager::WaitForMerge() {
+ return false;
+}
+
+UpdateStatus SnapshotManager::GetUpdateStatus(double* progress) {
+ *progress = 0.0f;
+ return UpdateStatus::None;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/init/Android.bp b/init/Android.bp
index ee339dd..ba60085 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -107,7 +107,6 @@
"bootchart.cpp",
"builtins.cpp",
"capabilities.cpp",
- "descriptors.cpp",
"devices.cpp",
"epoll.cpp",
"firmware_handler.cpp",
@@ -159,6 +158,13 @@
},
}
+phony {
+ name: "init",
+ required: [
+ "init_second_stage",
+ ],
+}
+
cc_binary {
name: "init_second_stage",
recovery_available: true,
@@ -254,7 +260,6 @@
"action_manager.cpp",
"action_parser.cpp",
"capabilities.cpp",
- "descriptors.cpp",
"epoll.cpp",
"keychords.cpp",
"import_parser.cpp",
@@ -287,5 +292,3 @@
},
},
}
-
-subdirs = ["*"]
diff --git a/init/README.md b/init/README.md
index 8179bff..2de76a9 100644
--- a/init/README.md
+++ b/init/README.md
@@ -300,7 +300,8 @@
`socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`
> Create a UNIX domain socket named /dev/socket/_name_ and pass its fd to the
- launched process. _type_ must be "dgram", "stream" or "seqpacket". User and
+ launched process. _type_ must be "dgram", "stream" or "seqpacket". _type_
+ may end with "+passcred" to enable SO_PASSCRED on the socket. User and
group default to 0. 'seclabel' is the SELinux security context for the
socket. It defaults to the service security context, as specified by
seclabel or computed based on the service executable file security context.
diff --git a/init/builtins.cpp b/init/builtins.cpp
index ba2c7ac..ceab568 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -42,6 +42,7 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <ApexProperties.sysprop.h>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -130,6 +131,13 @@
if (args.context != kInitContext) {
return Error() << "command 'class_start_post_data' only available in init context";
}
+ static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+
+ if (!is_apex_updatable) {
+ // No need to start these on devices that don't support APEX, since they're not
+ // stopped either.
+ return {};
+ }
for (const auto& service : ServiceList::GetInstance()) {
if (service->classnames().count(args[1])) {
if (auto result = service->StartIfPostData(); !result) {
@@ -155,6 +163,11 @@
if (args.context != kInitContext) {
return Error() << "command 'class_reset_post_data' only available in init context";
}
+ static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+ if (!is_apex_updatable) {
+ // No need to stop these on devices that don't support APEX.
+ return {};
+ }
ForEachServiceInClass(args[1], &Service::ResetIfPostData);
return {};
}
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
deleted file mode 100644
index 6265687..0000000
--- a/init/descriptors.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "descriptors.h"
-
-#include <ctype.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <cutils/android_get_control_file.h>
-#include <cutils/sockets.h>
-
-#include "util.h"
-
-namespace android {
-namespace init {
-
-DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
- gid_t gid, int perm, const std::string& context)
- : name_(name), type_(type), uid_(uid), gid_(gid), perm_(perm), context_(context) {
-}
-
-DescriptorInfo::~DescriptorInfo() {
-}
-
-std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info) {
- return os << " descriptors " << info.name_ << " " << info.type_ << " " << std::oct << info.perm_;
-}
-
-bool DescriptorInfo::operator==(const DescriptorInfo& other) const {
- return name_ == other.name_ && type_ == other.type_ && key() == other.key();
-}
-
-void DescriptorInfo::CreateAndPublish(const std::string& globalContext) const {
- // Create
- const std::string& contextStr = context_.empty() ? globalContext : context_;
- int fd = Create(contextStr);
- if (fd < 0) return;
-
- // Publish
- std::string publishedName = key() + name_;
- std::for_each(publishedName.begin(), publishedName.end(),
- [] (char& c) { c = isalnum(c) ? c : '_'; });
-
- std::string val = std::to_string(fd);
- setenv(publishedName.c_str(), val.c_str(), 1);
-
- // make sure we don't close on exec
- fcntl(fd, F_SETFD, 0);
-}
-
-void DescriptorInfo::Clean() const {
-}
-
-SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid,
- gid_t gid, int perm, const std::string& context)
- : DescriptorInfo(name, type, uid, gid, perm, context) {
-}
-
-void SocketInfo::Clean() const {
- std::string path = android::base::StringPrintf("%s/%s", ANDROID_SOCKET_DIR, name().c_str());
- unlink(path.c_str());
-}
-
-int SocketInfo::Create(const std::string& context) const {
- auto types = android::base::Split(type(), "+");
- int flags =
- ((types[0] == "stream" ? SOCK_STREAM : (types[0] == "dgram" ? SOCK_DGRAM : SOCK_SEQPACKET)));
- bool passcred = types.size() > 1 && types[1] == "passcred";
- return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str());
-}
-
-const std::string SocketInfo::key() const {
- return ANDROID_SOCKET_ENV_PREFIX;
-}
-
-FileInfo::FileInfo(const std::string& name, const std::string& type, uid_t uid,
- gid_t gid, int perm, const std::string& context)
- // defaults OK for uid,..., they are ignored for this class.
- : DescriptorInfo(name, type, uid, gid, perm, context) {
-}
-
-int FileInfo::Create(const std::string&) const {
- int flags = (type() == "r") ? O_RDONLY :
- (type() == "w") ? O_WRONLY :
- O_RDWR;
-
- // Make sure we do not block on open (eg: devices can chose to block on
- // carrier detect). Our intention is never to delay launch of a service
- // for such a condition. The service can perform its own blocking on
- // carrier detect.
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name().c_str(),
- flags | O_NONBLOCK)));
-
- if (fd < 0) {
- PLOG(ERROR) << "Failed to open file '" << name().c_str() << "'";
- return -1;
- }
-
- // Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.
- fcntl(fd, F_SETFL, flags);
-
- LOG(INFO) << "Opened file '" << name().c_str() << "'"
- << ", flags " << std::oct << flags << std::dec;
-
- return fd.release();
-}
-
-const std::string FileInfo::key() const {
- return ANDROID_FILE_ENV_PREFIX;
-}
-
-} // namespace init
-} // namespace android
diff --git a/init/descriptors.h b/init/descriptors.h
deleted file mode 100644
index 3bdddfe..0000000
--- a/init/descriptors.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef _INIT_DESCRIPTORS_H
-#define _INIT_DESCRIPTORS_H
-
-#include <sys/types.h>
-
-#include <string>
-
-namespace android {
-namespace init {
-
-class DescriptorInfo {
- public:
- DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
- gid_t gid, int perm, const std::string& context);
- virtual ~DescriptorInfo();
-
- friend std::ostream& operator<<(std::ostream& os, const class DescriptorInfo& info);
- bool operator==(const DescriptorInfo& other) const;
-
- void CreateAndPublish(const std::string& globalContext) const;
- virtual void Clean() const;
-
- protected:
- const std::string& name() const { return name_; }
- const std::string& type() const { return type_; }
- uid_t uid() const { return uid_; }
- gid_t gid() const { return gid_; }
- int perm() const { return perm_; }
- const std::string& context() const { return context_; }
-
- private:
- std::string name_;
- std::string type_;
- uid_t uid_;
- gid_t gid_;
- int perm_;
- std::string context_;
-
- virtual int Create(const std::string& globalContext) const = 0;
- virtual const std::string key() const = 0;
-};
-
-std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info);
-
-class SocketInfo : public DescriptorInfo {
- public:
- SocketInfo(const std::string& name, const std::string& type, uid_t uid,
- gid_t gid, int perm, const std::string& context);
- void Clean() const override;
- private:
- virtual int Create(const std::string& context) const override;
- virtual const std::string key() const override;
-};
-
-class FileInfo : public DescriptorInfo {
- public:
- FileInfo(const std::string& name, const std::string& type, uid_t uid,
- gid_t gid, int perm, const std::string& context);
- private:
- virtual int Create(const std::string& context) const override;
- virtual const std::string key() const override;
-};
-
-} // namespace init
-} // namespace android
-
-#endif
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 8623c30..3761750 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -994,10 +994,11 @@
void StartPropertyService(Epoll* epoll) {
property_set("ro.property_service.version", "2");
- property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- false, 0666, 0, 0, nullptr);
- if (property_set_fd == -1) {
- PLOG(FATAL) << "start_property_service socket creation failed";
+ if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ false, 0666, 0, 0, {})) {
+ property_set_fd = *result;
+ } else {
+ PLOG(FATAL) << "start_property_service socket creation failed: " << result.error();
}
listen(property_set_fd, 8);
diff --git a/init/reboot.cpp b/init/reboot.cpp
index cb54d34..b0b5b54 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -670,11 +670,18 @@
<< err;
}
} else if (reboot_target == "recovery") {
- const std::vector<std::string> options = {};
- std::string err;
- if (!write_bootloader_message(options, &err)) {
- LOG(ERROR) << "Failed to set bootloader message: " << err;
- return false;
+ bootloader_message boot = {};
+ if (std::string err; !read_bootloader_message(&boot, &err)) {
+ LOG(ERROR) << "Failed to read bootloader message: " << err;
+ }
+ // Update the boot command field if it's empty, and preserve
+ // the other arguments in the bootloader message.
+ if (boot.command[0] == '\0') {
+ strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
+ if (std::string err; !write_bootloader_message(boot, &err)) {
+ LOG(ERROR) << "Failed to set bootloader message: " << err;
+ return false;
+ }
}
} else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
reboot_target == "fastboot") {
diff --git a/init/service.cpp b/init/service.cpp
index f95b675..47f4db9 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -31,6 +31,7 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <cutils/sockets.h>
#include <processgroup/processgroup.h>
#include <selinux/selinux.h>
@@ -227,9 +228,11 @@
KillProcessGroup(SIGKILL);
}
- // Remove any descriptor resources we may have created.
- std::for_each(descriptors_.begin(), descriptors_.end(),
- std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
+ // Remove any socket resources we may have created.
+ for (const auto& socket : sockets_) {
+ auto path = ANDROID_SOCKET_DIR "/" + socket.name;
+ unlink(path.c_str());
+ }
for (const auto& f : reap_callbacks_) {
f(siginfo);
@@ -300,8 +303,12 @@
LOG(INFO) << "service " << name_;
LOG(INFO) << " class '" << Join(classnames_, " ") << "'";
LOG(INFO) << " exec " << Join(args_, " ");
- std::for_each(descriptors_.begin(), descriptors_.end(),
- [] (const auto& info) { LOG(INFO) << *info; });
+ for (const auto& socket : sockets_) {
+ LOG(INFO) << " socket " << socket.name;
+ }
+ for (const auto& file : files_) {
+ LOG(INFO) << " file " << file.name;
+ }
}
@@ -419,8 +426,17 @@
setenv(key.c_str(), value.c_str(), 1);
}
- std::for_each(descriptors_.begin(), descriptors_.end(),
- std::bind(&DescriptorInfo::CreateAndPublish, std::placeholders::_1, scon));
+ for (const auto& socket : sockets_) {
+ if (auto result = socket.CreateAndPublish(scon); !result) {
+ LOG(INFO) << "Could not create socket '" << socket.name << "': " << result.error();
+ }
+ }
+
+ for (const auto& file : files_) {
+ if (auto result = file.CreateAndPublish(); !result) {
+ LOG(INFO) << "Could not open file '" << file.name << "': " << result.error();
+ }
+ }
if (auto result = WritePidToFiles(&writepid_files_); !result) {
LOG(ERROR) << "failed to write pid to files: " << result.error();
diff --git a/init/service.h b/init/service.h
index cc35a8d..cdf31bb 100644
--- a/init/service.h
+++ b/init/service.h
@@ -31,7 +31,6 @@
#include "action.h"
#include "capabilities.h"
-#include "descriptors.h"
#include "keyword_map.h"
#include "parser.h"
#include "service_utils.h"
@@ -151,7 +150,8 @@
std::string seclabel_;
- std::vector<std::unique_ptr<DescriptorInfo>> descriptors_;
+ std::vector<SocketDescriptor> sockets_;
+ std::vector<FileDescriptor> files_;
std::vector<std::pair<std::string, std::string>> environment_vars_;
Action onrestart_; // Commands to execute on restart.
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 88ce364..0fbbeb8 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -17,6 +17,8 @@
#include "service_parser.h"
#include <linux/input.h>
+#include <stdlib.h>
+#include <sys/socket.h>
#include <algorithm>
#include <sstream>
@@ -28,6 +30,7 @@
#include <system/thread_defs.h>
#include "rlimit_parser.h"
+#include "service_utils.h"
#include "util.h"
#if defined(__ANDROID__)
@@ -344,64 +347,98 @@
return {};
}
-template <typename T>
-Result<void> ServiceParser::AddDescriptor(std::vector<std::string>&& args) {
- int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
- Result<uid_t> uid = 0;
- Result<gid_t> gid = 0;
- std::string context = args.size() > 6 ? args[6] : "";
+// name type perm [ uid gid context ]
+Result<void> ServiceParser::ParseSocket(std::vector<std::string>&& args) {
+ SocketDescriptor socket;
+ socket.name = std::move(args[1]);
+
+ auto types = Split(args[2], "+");
+ if (types[0] == "stream") {
+ socket.type = SOCK_STREAM;
+ } else if (types[0] == "dgram") {
+ socket.type = SOCK_DGRAM;
+ } else if (types[0] == "seqpacket") {
+ socket.type = SOCK_SEQPACKET;
+ } else {
+ return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket', got '" << types[0]
+ << "' instead.";
+ }
+
+ if (types.size() > 1) {
+ if (types.size() == 2 && types[1] == "passcred") {
+ socket.passcred = true;
+ } else {
+ return Error() << "Only 'passcred' may be used to modify the socket type";
+ }
+ }
+
+ errno = 0;
+ char* end = nullptr;
+ socket.perm = strtol(args[3].c_str(), &end, 8);
+ if (errno != 0) {
+ return ErrnoError() << "Unable to parse permissions '" << args[3] << "'";
+ }
+ if (end == args[3].c_str() || *end != '\0') {
+ errno = EINVAL;
+ return ErrnoError() << "Unable to parse permissions '" << args[3] << "'";
+ }
if (args.size() > 4) {
- uid = DecodeUid(args[4]);
+ auto uid = DecodeUid(args[4]);
if (!uid) {
return Error() << "Unable to find UID for '" << args[4] << "': " << uid.error();
}
+ socket.uid = *uid;
}
if (args.size() > 5) {
- gid = DecodeUid(args[5]);
+ auto gid = DecodeUid(args[5]);
if (!gid) {
return Error() << "Unable to find GID for '" << args[5] << "': " << gid.error();
}
+ socket.gid = *gid;
}
- auto descriptor = std::make_unique<T>(args[1], args[2], *uid, *gid, perm, context);
+ socket.context = args.size() > 6 ? args[6] : "";
- auto old = std::find_if(
- service_->descriptors_.begin(), service_->descriptors_.end(),
- [&descriptor](const auto& other) { return descriptor.get() == other.get(); });
+ auto old = std::find_if(service_->sockets_.begin(), service_->sockets_.end(),
+ [&socket](const auto& other) { return socket.name == other.name; });
- if (old != service_->descriptors_.end()) {
- return Error() << "duplicate descriptor " << args[1] << " " << args[2];
+ if (old != service_->sockets_.end()) {
+ return Error() << "duplicate socket descriptor '" << socket.name << "'";
}
- service_->descriptors_.emplace_back(std::move(descriptor));
+ service_->sockets_.emplace_back(std::move(socket));
+
return {};
}
-// name type perm [ uid gid context ]
-Result<void> ServiceParser::ParseSocket(std::vector<std::string>&& args) {
- if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") &&
- !StartsWith(args[2], "seqpacket")) {
- return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket'";
- }
- return AddDescriptor<SocketInfo>(std::move(args));
-}
-
-// name type perm [ uid gid context ]
+// name type
Result<void> ServiceParser::ParseFile(std::vector<std::string>&& args) {
if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
return Error() << "file type must be 'r', 'w' or 'rw'";
}
- std::string expanded;
- if (!expand_props(args[1], &expanded)) {
+
+ FileDescriptor file;
+ file.type = args[2];
+
+ if (!expand_props(args[1], &file.name)) {
return Error() << "Could not expand property in file path '" << args[1] << "'";
}
- args[1] = std::move(expanded);
- if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
+ if (file.name[0] != '/' || file.name.find("../") != std::string::npos) {
return Error() << "file name must not be relative";
}
- return AddDescriptor<FileInfo>(std::move(args));
+
+ auto old = std::find_if(service_->files_.begin(), service_->files_.end(),
+ [&file](const auto& other) { return other.name == file.name; });
+
+ if (old != service_->files_.end()) {
+ return Error() << "duplicate file descriptor '" << file.name << "'";
+ }
+
+ service_->files_.emplace_back(std::move(file));
+
+ return {};
}
Result<void> ServiceParser::ParseUser(std::vector<std::string>&& args) {
diff --git a/init/service_parser.h b/init/service_parser.h
index 5ad26ef..bca0739 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -81,9 +81,6 @@
Result<void> ParseWritepid(std::vector<std::string>&& args);
Result<void> ParseUpdatable(std::vector<std::string>&& args);
- template <typename T>
- Result<void> AddDescriptor(std::vector<std::string>&& args);
-
bool IsValidName(const std::string& name) const;
ServiceList* service_list_;
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index 34aa837..836145d 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -27,9 +27,12 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <cutils/android_get_control_file.h>
+#include <cutils/sockets.h>
#include <processgroup/processgroup.h>
#include "mount_namespace.h"
+#include "util.h"
using android::base::GetProperty;
using android::base::StartsWith;
@@ -135,8 +138,52 @@
dup2(fd, 2);
}
+void PublishDescriptor(const std::string& key, const std::string& name, int fd) {
+ std::string published_name = key + name;
+ for (auto& c : published_name) {
+ c = isalnum(c) ? c : '_';
+ }
+
+ std::string val = std::to_string(fd);
+ setenv(published_name.c_str(), val.c_str(), 1);
+}
+
} // namespace
+Result<void> SocketDescriptor::CreateAndPublish(const std::string& global_context) const {
+ const auto& socket_context = context.empty() ? global_context : context;
+ auto result = CreateSocket(name, type, passcred, perm, uid, gid, socket_context);
+ if (!result) {
+ return result.error();
+ }
+
+ PublishDescriptor(ANDROID_SOCKET_ENV_PREFIX, name, *result);
+
+ return {};
+}
+
+Result<void> FileDescriptor::CreateAndPublish() const {
+ int flags = (type == "r") ? O_RDONLY : (type == "w") ? O_WRONLY : O_RDWR;
+
+ // Make sure we do not block on open (eg: devices can chose to block on carrier detect). Our
+ // intention is never to delay launch of a service for such a condition. The service can
+ // perform its own blocking on carrier detect.
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name.c_str(), flags | O_NONBLOCK)));
+
+ if (fd < 0) {
+ return ErrnoError() << "Failed to open file '" << name << "'";
+ }
+
+ // Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.
+ fcntl(fd, F_SETFL, flags);
+
+ LOG(INFO) << "Opened file '" << name << "', flags " << flags;
+
+ PublishDescriptor(ANDROID_FILE_ENV_PREFIX, name, fd.release());
+
+ return {};
+}
+
Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd) {
for (const auto& [nstype, path] : info.namespaces_to_enter) {
if (auto result = EnterNamespace(nstype, path.c_str()); !result) {
diff --git a/init/service_utils.h b/init/service_utils.h
index 365cb29..befce25 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -29,6 +29,25 @@
namespace android {
namespace init {
+struct SocketDescriptor {
+ std::string name;
+ int type = 0;
+ uid_t uid = 0;
+ gid_t gid = 0;
+ int perm = 0;
+ std::string context;
+ bool passcred = false;
+
+ Result<void> CreateAndPublish(const std::string& global_context) const;
+};
+
+struct FileDescriptor {
+ std::string name;
+ std::string type;
+
+ Result<void> CreateAndPublish() const;
+};
+
struct NamespaceInfo {
int flags;
// Pair of namespace type, path to name.
diff --git a/init/util.cpp b/init/util.cpp
index 058a111..8bfb755 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -34,6 +34,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
@@ -77,32 +78,28 @@
* daemon. We communicate the file descriptor's value via the environment
* variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
*/
-int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
- const char* socketcon) {
- if (socketcon) {
- if (setsockcreatecon(socketcon) == -1) {
- PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
- return -1;
+Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
+ gid_t gid, const std::string& socketcon) {
+ if (!socketcon.empty()) {
+ if (setsockcreatecon(socketcon.c_str()) == -1) {
+ return ErrnoError() << "setsockcreatecon(\"" << socketcon << "\") failed";
}
}
android::base::unique_fd fd(socket(PF_UNIX, type, 0));
if (fd < 0) {
- PLOG(ERROR) << "Failed to open socket '" << name << "'";
- return -1;
+ return ErrnoError() << "Failed to open socket '" << name << "'";
}
- if (socketcon) setsockcreatecon(NULL);
+ if (!socketcon.empty()) setsockcreatecon(nullptr);
struct sockaddr_un addr;
memset(&addr, 0 , sizeof(addr));
addr.sun_family = AF_UNIX;
- snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",
- name);
+ snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR "/%s", name.c_str());
if ((unlink(addr.sun_path) != 0) && (errno != ENOENT)) {
- PLOG(ERROR) << "Failed to unlink old socket '" << name << "'";
- return -1;
+ return ErrnoError() << "Failed to unlink old socket '" << name << "'";
}
std::string secontext;
@@ -113,8 +110,7 @@
if (passcred) {
int on = 1;
if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
- PLOG(ERROR) << "Failed to set SO_PASSCRED '" << name << "'";
- return -1;
+ return ErrnoError() << "Failed to set SO_PASSCRED '" << name << "'";
}
}
@@ -125,19 +121,18 @@
setfscreatecon(nullptr);
}
+ auto guard = android::base::make_scope_guard([&addr] { unlink(addr.sun_path); });
+
if (ret) {
errno = savederrno;
- PLOG(ERROR) << "Failed to bind socket '" << name << "'";
- goto out_unlink;
+ return ErrnoError() << "Failed to bind socket '" << name << "'";
}
if (lchown(addr.sun_path, uid, gid)) {
- PLOG(ERROR) << "Failed to lchown socket '" << addr.sun_path << "'";
- goto out_unlink;
+ return ErrnoError() << "Failed to lchown socket '" << addr.sun_path << "'";
}
if (fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW)) {
- PLOG(ERROR) << "Failed to fchmodat socket '" << addr.sun_path << "'";
- goto out_unlink;
+ return ErrnoError() << "Failed to fchmodat socket '" << addr.sun_path << "'";
}
LOG(INFO) << "Created socket '" << addr.sun_path << "'"
@@ -145,11 +140,8 @@
<< ", user " << uid
<< ", group " << gid;
+ guard.Disable();
return fd.release();
-
-out_unlink:
- unlink(addr.sun_path);
- return -1;
}
Result<std::string> ReadFile(const std::string& path) {
diff --git a/init/util.h b/init/util.h
index 1929cb5..6a12fb6 100644
--- a/init/util.h
+++ b/init/util.h
@@ -38,8 +38,8 @@
static const char kColdBootDoneProp[] = "ro.cold_boot_done";
-int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
- const char* socketcon);
+Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
+ gid_t gid, const std::string& socketcon);
Result<std::string> ReadFile(const std::string& path);
Result<void> WriteFile(const std::string& path, const std::string& content);
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 9ece847..565f2c3 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -97,7 +97,6 @@
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
},
},
- whole_static_libs: ["libdemangle"],
}
cc_test_library {
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 71980d7..3e050ab 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -28,13 +28,13 @@
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
-#include <demangle.h>
-
#include "BacktraceLog.h"
#include "UnwindStack.h"
using android::base::StringPrintf;
+extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
+
//-------------------------------------------------------------------------
// Backtrace functions.
//-------------------------------------------------------------------------
@@ -63,7 +63,14 @@
if (map->start == 0 || (map->flags & PROT_DEVICE_MAP)) {
return "";
}
- return demangle(GetFunctionNameRaw(pc, offset).c_str());
+ std::string name(GetFunctionNameRaw(pc, offset));
+ char* demangled_name = __cxa_demangle(name.c_str(), nullptr, nullptr, nullptr);
+ if (demangled_name != nullptr) {
+ name = demangled_name;
+ free(demangled_name);
+ return name;
+ }
+ return name;
}
bool Backtrace::VerifyReadWordArgs(uint64_t ptr, word_t* out_value) {
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index a128623..624711f 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -24,7 +24,6 @@
#include <string>
#include <backtrace/Backtrace.h>
-#include <demangle.h>
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
@@ -41,6 +40,8 @@
#include "UnwindStack.h"
#include "UnwindStackMap.h"
+extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
+
bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
std::vector<std::string>* skip_names, BacktraceUnwindError* error) {
@@ -115,7 +116,13 @@
back_frame->pc = frame->pc;
back_frame->sp = frame->sp;
- back_frame->func_name = demangle(frame->function_name.c_str());
+ char* demangled_name = __cxa_demangle(frame->function_name.c_str(), nullptr, nullptr, nullptr);
+ if (demangled_name != nullptr) {
+ back_frame->func_name = demangled_name;
+ free(demangled_name);
+ } else {
+ back_frame->func_name = frame->function_name;
+ }
back_frame->func_offset = frame->function_offset;
back_frame->map.name = frame->map_name;
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 63c3793..ae9dab5 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -28,17 +28,10 @@
* mediadrm
* Whose friendly names do not match the #define statements.
*
- * Additionally, AID_OEM_RESERVED_START and AID_OEM_RESERVED_END
- * can be used to define reserved OEM ranges used for sanity checks
- * during the build process. The rules are, they must end with START/END
- * The proper convention is incrementing a number like so:
- * AID_OEM_RESERVED_START
- * AID_OEM_RESERVED_1_START
- * AID_OEM_RESERVED_2_START
- * ...
- * The same applies to the END.
- * They are not required to be in order, but must not overlap each other and
- * must define a START and END'ing range. START must be smaller than END.
+ * This file must only be used for platform (Google managed, and submitted through AOSP), AIDs. 3rd
+ * party AIDs must be added via config.fs, which will place them in the corresponding partition's
+ * passwd and group files. There are ranges in this file reserved for AIDs for each 3rd party
+ * partition, from which the system reads passwd and group files.
*/
#ifndef _ANDROID_FILESYSTEM_CONFIG_H_
@@ -141,8 +134,12 @@
#define AID_CACHE 2001 /* cache access */
#define AID_DIAG 2002 /* access to diagnostic resources */
-/* The range 2900-2999 is reserved for OEM, and must never be
- * used here */
+/* The range 2900-2999 is reserved for the vendor partition */
+/* Note that the two 'OEM' ranges pre-dated the vendor partition, so they take the legacy 'OEM'
+ * name. Additionally, they pre-dated passwd/group files, so there are users and groups named oem_#
+ * created automatically for all values in these ranges. If there is a user/group in a passwd/group
+ * file corresponding to this range, both the oem_# and user/group names will resolve to the same
+ * value. */
#define AID_OEM_RESERVED_START 2900
#define AID_OEM_RESERVED_END 2999
@@ -159,10 +156,26 @@
#define AID_WAKELOCK 3010 /* Allow system wakelock read/write access */
#define AID_UHID 3011 /* Allow read/write to /dev/uhid node */
-/* The range 5000-5999 is also reserved for OEM, and must never be used here. */
+/* The range 5000-5999 is also reserved for vendor partition. */
#define AID_OEM_RESERVED_2_START 5000
#define AID_OEM_RESERVED_2_END 5999
+/* The range 6000-6499 is reserved for the system partition. */
+#define AID_SYSTEM_RESERVED_START 6000
+#define AID_SYSTEM_RESERVED_END 6499
+
+/* The range 6500-6999 is reserved for the odm partition. */
+#define AID_ODM_RESERVED_START 6500
+#define AID_ODM_RESERVED_END 6999
+
+/* The range 7000-7499 is reserved for the product partition. */
+#define AID_PRODUCT_RESERVED_START 7000
+#define AID_PRODUCT_RESERVED_END 7499
+
+/* The range 7500-7999 is reserved for the system_ext partition. */
+#define AID_SYSTEM_EXT_RESERVED_START 7500
+#define AID_SYSTEM_EXT_RESERVED_END 7999
+
#define AID_EVERYBODY 9997 /* shared between all apps in the same profile */
#define AID_MISC 9998 /* access to misc storage */
#define AID_NOBODY 9999
diff --git a/libmeminfo/Android.bp b/libmeminfo/Android.bp
index fc022bd..8dcc77b 100644
--- a/libmeminfo/Android.bp
+++ b/libmeminfo/Android.bp
@@ -26,10 +26,17 @@
"liblog",
"libprocinfo",
],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+
+ },
}
cc_library {
name: "libmeminfo",
+ host_supported: true,
defaults: ["libmeminfo_defaults"],
export_include_dirs: ["include"],
export_shared_lib_headers: ["libbase"],
diff --git a/libmeminfo/include/meminfo/procmeminfo.h b/libmeminfo/include/meminfo/procmeminfo.h
index 1fb4151..f782ec5 100644
--- a/libmeminfo/include/meminfo/procmeminfo.h
+++ b/libmeminfo/include/meminfo/procmeminfo.h
@@ -45,6 +45,9 @@
// vector.
const std::vector<Vma>& MapsWithPageIdle();
+ // Same as Maps() except, do not read the usage stats for each map.
+ const std::vector<Vma>& MapsWithoutUsageStats();
+
// Collect all 'vma' or 'maps' from /proc/<pid>/smaps and store them in 'maps_'. Returns a
// constant reference to the vma vector after the collection is done.
//
@@ -88,7 +91,7 @@
~ProcMemInfo() = default;
private:
- bool ReadMaps(bool get_wss, bool use_pageidle = false);
+ bool ReadMaps(bool get_wss, bool use_pageidle = false, bool get_usage_stats = true);
bool ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss, bool use_pageidle);
pid_t pid_;
diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp
index 5451ca3..4c2be91 100644
--- a/libmeminfo/libmeminfo_test.cpp
+++ b/libmeminfo/libmeminfo_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -60,6 +61,103 @@
EXPECT_FALSE(maps.empty());
}
+TEST(ProcMemInfo, MapsUsageNotEmpty) {
+ ProcMemInfo proc_mem(pid);
+ const std::vector<Vma>& maps = proc_mem.Maps();
+ EXPECT_FALSE(maps.empty());
+ uint64_t total_pss = 0;
+ uint64_t total_rss = 0;
+ uint64_t total_uss = 0;
+ for (auto& map : maps) {
+ ASSERT_NE(0, map.usage.vss);
+ total_rss += map.usage.rss;
+ total_pss += map.usage.pss;
+ total_uss += map.usage.uss;
+ }
+
+ // Crude check that stats are actually being read.
+ EXPECT_NE(0, total_rss) << "RSS zero for all maps, that is not possible.";
+ EXPECT_NE(0, total_pss) << "PSS zero for all maps, that is not possible.";
+ EXPECT_NE(0, total_uss) << "USS zero for all maps, that is not possible.";
+}
+
+TEST(ProcMemInfo, MapsUsageEmpty) {
+ ProcMemInfo proc_mem(pid);
+ const std::vector<Vma>& maps = proc_mem.MapsWithoutUsageStats();
+ EXPECT_FALSE(maps.empty());
+ // Verify that all usage stats are zero in every map.
+ for (auto& map : maps) {
+ ASSERT_EQ(0, map.usage.vss);
+ ASSERT_EQ(0, map.usage.rss);
+ ASSERT_EQ(0, map.usage.pss);
+ ASSERT_EQ(0, map.usage.uss);
+ ASSERT_EQ(0, map.usage.swap);
+ ASSERT_EQ(0, map.usage.swap_pss);
+ ASSERT_EQ(0, map.usage.private_clean);
+ ASSERT_EQ(0, map.usage.private_dirty);
+ ASSERT_EQ(0, map.usage.shared_clean);
+ ASSERT_EQ(0, map.usage.shared_dirty);
+ }
+}
+
+TEST(ProcMemInfo, PageMapPresent) {
+ static constexpr size_t kNumPages = 20;
+ size_t pagesize = getpagesize();
+ void* ptr = mmap(nullptr, pagesize * (kNumPages + 2), PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ ASSERT_NE(MAP_FAILED, ptr);
+
+ // Unmap the first page and the last page so that we guarantee this
+ // map is in a map by itself.
+ ASSERT_EQ(0, munmap(ptr, pagesize));
+ uintptr_t addr = reinterpret_cast<uintptr_t>(ptr) + pagesize;
+ ASSERT_EQ(0, munmap(reinterpret_cast<void*>(addr + kNumPages * pagesize), pagesize));
+
+ ProcMemInfo proc_mem(getpid());
+ const std::vector<Vma>& maps = proc_mem.MapsWithoutUsageStats();
+ ASSERT_FALSE(maps.empty());
+
+ // Find the vma associated with our previously created map.
+ const Vma* test_vma = nullptr;
+ for (const Vma& vma : maps) {
+ if (vma.start == addr) {
+ test_vma = &vma;
+ break;
+ }
+ }
+ ASSERT_TRUE(test_vma != nullptr) << "Cannot find test map.";
+
+ // Verify that none of the pages are listed as present.
+ std::vector<uint64_t> pagemap;
+ ASSERT_TRUE(proc_mem.PageMap(*test_vma, &pagemap));
+ ASSERT_EQ(kNumPages, pagemap.size());
+ for (size_t i = 0; i < pagemap.size(); i++) {
+ EXPECT_FALSE(android::meminfo::page_present(pagemap[i]))
+ << "Page " << i << " is present and it should not be.";
+ }
+
+ // Make some of the pages present and verify that we see them
+ // as present.
+ uint8_t* data = reinterpret_cast<uint8_t*>(addr);
+ data[0] = 1;
+ data[pagesize * 5] = 1;
+ data[pagesize * 11] = 1;
+
+ ASSERT_TRUE(proc_mem.PageMap(*test_vma, &pagemap));
+ ASSERT_EQ(kNumPages, pagemap.size());
+ for (size_t i = 0; i < pagemap.size(); i++) {
+ if (i == 0 || i == 5 || i == 11) {
+ EXPECT_TRUE(android::meminfo::page_present(pagemap[i]))
+ << "Page " << i << " is not present and it should be.";
+ } else {
+ EXPECT_FALSE(android::meminfo::page_present(pagemap[i]))
+ << "Page " << i << " is present and it should not be.";
+ }
+ }
+
+ ASSERT_EQ(0, munmap(reinterpret_cast<void*>(addr), kNumPages * pagesize));
+}
+
TEST(ProcMemInfo, WssEmpty) {
// If we created the object for getting usage,
// the working set must be empty
diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp
index a8b43c1..6f68ab4 100644
--- a/libmeminfo/procmeminfo.cpp
+++ b/libmeminfo/procmeminfo.cpp
@@ -130,6 +130,14 @@
return maps_;
}
+const std::vector<Vma>& ProcMemInfo::MapsWithoutUsageStats() {
+ if (maps_.empty() && !ReadMaps(get_wss_, false, false)) {
+ LOG(ERROR) << "Failed to read maps for Process " << pid_;
+ }
+
+ return maps_;
+}
+
const std::vector<Vma>& ProcMemInfo::Smaps(const std::string& path) {
if (!maps_.empty()) {
return maps_;
@@ -213,29 +221,30 @@
std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid_);
::android::base::unique_fd pagemap_fd(
TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC)));
- if (pagemap_fd < 0) {
+ if (pagemap_fd == -1) {
PLOG(ERROR) << "Failed to open " << pagemap_file;
return false;
}
uint64_t nr_pages = (vma.end - vma.start) / getpagesize();
- pagemap->reserve(nr_pages);
+ pagemap->resize(nr_pages);
- uint64_t idx = vma.start / getpagesize();
- uint64_t last = idx + nr_pages;
- uint64_t val;
- for (; idx < last; idx++) {
- if (pread64(pagemap_fd, &val, sizeof(uint64_t), idx * sizeof(uint64_t)) < 0) {
- PLOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_;
- return false;
- }
- pagemap->emplace_back(val);
+ size_t bytes_to_read = sizeof(uint64_t) * nr_pages;
+ off64_t start_addr = (vma.start / getpagesize()) * sizeof(uint64_t);
+ ssize_t bytes_read = pread64(pagemap_fd, pagemap->data(), bytes_to_read, start_addr);
+ if (bytes_read == -1) {
+ PLOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_;
+ return false;
+ } else if (static_cast<size_t>(bytes_read) != bytes_to_read) {
+ LOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_
+ << ": read bytes " << bytes_read << " expected bytes " << bytes_to_read;
+ return false;
}
return true;
}
-bool ProcMemInfo::ReadMaps(bool get_wss, bool use_pageidle) {
+bool ProcMemInfo::ReadMaps(bool get_wss, bool use_pageidle, bool get_usage_stats) {
// Each object reads /proc/<pid>/maps only once. This is done to make sure programs that are
// running for the lifetime of the system can recycle the objects and don't have to
// unnecessarily retain and update this object in memory (which can get significantly large).
@@ -256,6 +265,10 @@
return false;
}
+ if (!get_usage_stats) {
+ return true;
+ }
+
std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid_);
::android::base::unique_fd pagemap_fd(
TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC)));
diff --git a/libmeminfo/tools/Android.bp b/libmeminfo/tools/Android.bp
index 2e89c41..3968c09 100644
--- a/libmeminfo/tools/Android.bp
+++ b/libmeminfo/tools/Android.bp
@@ -56,6 +56,7 @@
cc_binary {
name: "showmap",
+ host_supported: true,
cflags: [
"-Wall",
"-Werror",
@@ -66,6 +67,12 @@
"libbase",
"libmeminfo",
],
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
cc_binary {
diff --git a/libmeminfo/tools/showmap.cpp b/libmeminfo/tools/showmap.cpp
index a80fa76..8ea2108 100644
--- a/libmeminfo/tools/showmap.cpp
+++ b/libmeminfo/tools/showmap.cpp
@@ -18,6 +18,7 @@
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/signal.h>
#include <sys/types.h>
#include <unistd.h>
@@ -56,7 +57,7 @@
static VmaInfo g_total;
static std::vector<VmaInfo> g_vmas;
-[[noreturn]] static void usage(int exit_status) {
+[[noreturn]] static void usage(const char* progname, int exit_status) {
fprintf(stderr,
"%s [-aqtv] [-f FILE] PID\n"
"-a\taddresses (show virtual memory map)\n"
@@ -64,7 +65,7 @@
"-t\tterse (show only items with private pages)\n"
"-v\tverbose (don't coalesce maps with the same name)\n"
"-f\tFILE (read from input from FILE instead of PID)\n",
- getprogname());
+ progname);
exit(exit_status);
}
@@ -239,22 +240,22 @@
g_filename = optarg;
break;
case 'h':
- usage(EXIT_SUCCESS);
+ usage(argv[0], EXIT_SUCCESS);
default:
- usage(EXIT_FAILURE);
+ usage(argv[0], EXIT_FAILURE);
}
}
if (g_filename.empty()) {
if ((argc - 1) < optind) {
fprintf(stderr, "Invalid arguments: Must provide <pid> at the end\n");
- usage(EXIT_FAILURE);
+ usage(argv[0], EXIT_FAILURE);
}
g_pid = atoi(argv[optind]);
if (g_pid <= 0) {
fprintf(stderr, "Invalid process id %s\n", argv[optind]);
- usage(EXIT_FAILURE);
+ usage(argv[0], EXIT_FAILURE);
}
g_filename = ::android::base::StringPrintf("/proc/%d/smaps", g_pid);
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index debc43f..d1c8351 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -70,3 +70,26 @@
host_supported: true,
export_include_dirs: ["include"],
}
+
+cc_test {
+ name: "libnativeloader_test",
+ srcs: [
+ "native_loader_test.cpp",
+ "native_loader.cpp",
+ "library_namespaces.cpp",
+ "native_loader_namespace.cpp",
+ "public_libraries.cpp",
+ ],
+ cflags: ["-DANDROID"],
+ static_libs: [
+ "libbase",
+ "liblog",
+ "libnativehelper",
+ "libgmock",
+ ],
+ header_libs: [
+ "libnativebridge-headers",
+ "libnativeloader-headers",
+ ],
+ system_shared_libs: ["libc", "libm"],
+}
diff --git a/libnativeloader/library_namespaces.h b/libnativeloader/library_namespaces.h
index fd46cdc..6e9a190 100644
--- a/libnativeloader/library_namespaces.h
+++ b/libnativeloader/library_namespaces.h
@@ -42,7 +42,10 @@
LibraryNamespaces& operator=(const LibraryNamespaces&) = delete;
void Initialize();
- void Reset() { namespaces_.clear(); }
+ void Reset() {
+ namespaces_.clear();
+ initialized_ = false;
+ }
NativeLoaderNamespace* Create(JNIEnv* env, uint32_t target_sdk_version, jobject class_loader,
bool is_shared, jstring dex_path, jstring java_library_path,
jstring java_permitted_path, std::string* error_msg);
diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp
new file mode 100644
index 0000000..9648aad
--- /dev/null
+++ b/libnativeloader/native_loader_test.cpp
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dlfcn.h>
+#include <memory>
+#include <unordered_map>
+
+#include <android-base/strings.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <jni.h>
+
+#include "native_loader_namespace.h"
+#include "nativeloader/dlext_namespaces.h"
+#include "nativeloader/native_loader.h"
+#include "public_libraries.h"
+
+using namespace ::testing;
+
+namespace android {
+namespace nativeloader {
+
+// gmock interface that represents interested platform APIs on libdl and libnativebridge
+class Platform {
+ public:
+ virtual ~Platform() {}
+
+ // libdl APIs
+ virtual void* dlopen(const char* filename, int flags) = 0;
+ virtual int dlclose(void* handle) = 0;
+ virtual char* dlerror(void) = 0;
+
+ // These mock_* are the APIs semantically the same across libdl and libnativebridge.
+ // Instead of having two set of mock APIs for the two, define only one set with an additional
+ // argument 'bool bridged' to identify the context (i.e., called for libdl or libnativebridge).
+ typedef char* mock_namespace_handle;
+ virtual bool mock_init_anonymous_namespace(bool bridged, const char* sonames,
+ const char* search_paths) = 0;
+ virtual mock_namespace_handle mock_create_namespace(
+ bool bridged, const char* name, const char* ld_library_path, const char* default_library_path,
+ uint64_t type, const char* permitted_when_isolated_path, mock_namespace_handle parent) = 0;
+ virtual bool mock_link_namespaces(bool bridged, mock_namespace_handle from,
+ mock_namespace_handle to, const char* sonames) = 0;
+ virtual mock_namespace_handle mock_get_exported_namespace(bool bridged, const char* name) = 0;
+ virtual void* mock_dlopen_ext(bool bridged, const char* filename, int flags,
+ mock_namespace_handle ns) = 0;
+
+ // libnativebridge APIs for which libdl has no corresponding APIs
+ virtual bool NativeBridgeInitialized() = 0;
+ virtual const char* NativeBridgeGetError() = 0;
+ virtual bool NativeBridgeIsPathSupported(const char*) = 0;
+ virtual bool NativeBridgeIsSupported(const char*) = 0;
+
+ // To mock "ClassLoader Object.getParent()"
+ virtual const char* JniObject_getParent(const char*) = 0;
+};
+
+// The mock does not actually create a namespace object. But simply casts the pointer to the
+// string for the namespace name as the handle to the namespace object.
+#define TO_ANDROID_NAMESPACE(str) \
+ reinterpret_cast<struct android_namespace_t*>(const_cast<char*>(str))
+
+#define TO_BRIDGED_NAMESPACE(str) \
+ reinterpret_cast<struct native_bridge_namespace_t*>(const_cast<char*>(str))
+
+#define TO_MOCK_NAMESPACE(ns) reinterpret_cast<Platform::mock_namespace_handle>(ns)
+
+// These represents built-in namespaces created by the linker according to ld.config.txt
+static std::unordered_map<std::string, Platform::mock_namespace_handle> namespaces = {
+ {"platform", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("platform"))},
+ {"default", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("default"))},
+ {"runtime", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("runtime"))},
+ {"sphal", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("sphal"))},
+ {"vndk", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("vndk"))},
+};
+
+// The actual gmock object
+class MockPlatform : public Platform {
+ public:
+ MockPlatform(bool is_bridged) : is_bridged_(is_bridged) {
+ ON_CALL(*this, NativeBridgeIsSupported(_)).WillByDefault(Return(is_bridged_));
+ ON_CALL(*this, NativeBridgeIsPathSupported(_)).WillByDefault(Return(is_bridged_));
+ ON_CALL(*this, mock_get_exported_namespace(_, _))
+ .WillByDefault(Invoke([](bool, const char* name) -> mock_namespace_handle {
+ if (namespaces.find(name) != namespaces.end()) {
+ return namespaces[name];
+ }
+ return nullptr;
+ }));
+ }
+
+ // Mocking libdl APIs
+ MOCK_METHOD2(dlopen, void*(const char*, int));
+ MOCK_METHOD1(dlclose, int(void*));
+ MOCK_METHOD0(dlerror, char*());
+
+ // Mocking the common APIs
+ MOCK_METHOD3(mock_init_anonymous_namespace, bool(bool, const char*, const char*));
+ MOCK_METHOD7(mock_create_namespace,
+ mock_namespace_handle(bool, const char*, const char*, const char*, uint64_t,
+ const char*, mock_namespace_handle));
+ MOCK_METHOD4(mock_link_namespaces,
+ bool(bool, mock_namespace_handle, mock_namespace_handle, const char*));
+ MOCK_METHOD2(mock_get_exported_namespace, mock_namespace_handle(bool, const char*));
+ MOCK_METHOD4(mock_dlopen_ext, void*(bool, const char*, int, mock_namespace_handle));
+
+ // Mocking libnativebridge APIs
+ MOCK_METHOD0(NativeBridgeInitialized, bool());
+ MOCK_METHOD0(NativeBridgeGetError, const char*());
+ MOCK_METHOD1(NativeBridgeIsPathSupported, bool(const char*));
+ MOCK_METHOD1(NativeBridgeIsSupported, bool(const char*));
+
+ // Mocking "ClassLoader Object.getParent()"
+ MOCK_METHOD1(JniObject_getParent, const char*(const char*));
+
+ private:
+ bool is_bridged_;
+};
+
+static std::unique_ptr<MockPlatform> mock;
+
+// Provide C wrappers for the mock object.
+extern "C" {
+void* dlopen(const char* file, int flag) {
+ return mock->dlopen(file, flag);
+}
+
+int dlclose(void* handle) {
+ return mock->dlclose(handle);
+}
+
+char* dlerror(void) {
+ return mock->dlerror();
+}
+
+bool android_init_anonymous_namespace(const char* sonames, const char* search_path) {
+ return mock->mock_init_anonymous_namespace(false, sonames, search_path);
+}
+
+struct android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path,
+ const char* default_library_path,
+ uint64_t type,
+ const char* permitted_when_isolated_path,
+ struct android_namespace_t* parent) {
+ return TO_ANDROID_NAMESPACE(
+ mock->mock_create_namespace(false, name, ld_library_path, default_library_path, type,
+ permitted_when_isolated_path, TO_MOCK_NAMESPACE(parent)));
+}
+
+bool android_link_namespaces(struct android_namespace_t* from, struct android_namespace_t* to,
+ const char* sonames) {
+ return mock->mock_link_namespaces(false, TO_MOCK_NAMESPACE(from), TO_MOCK_NAMESPACE(to), sonames);
+}
+
+struct android_namespace_t* android_get_exported_namespace(const char* name) {
+ return TO_ANDROID_NAMESPACE(mock->mock_get_exported_namespace(false, name));
+}
+
+void* android_dlopen_ext(const char* filename, int flags, const android_dlextinfo* info) {
+ return mock->mock_dlopen_ext(false, filename, flags, TO_MOCK_NAMESPACE(info->library_namespace));
+}
+
+// libnativebridge APIs
+bool NativeBridgeIsSupported(const char* libpath) {
+ return mock->NativeBridgeIsSupported(libpath);
+}
+
+struct native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name) {
+ return TO_BRIDGED_NAMESPACE(mock->mock_get_exported_namespace(true, name));
+}
+
+struct native_bridge_namespace_t* NativeBridgeCreateNamespace(
+ const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
+ const char* permitted_when_isolated_path, struct native_bridge_namespace_t* parent) {
+ return TO_BRIDGED_NAMESPACE(
+ mock->mock_create_namespace(true, name, ld_library_path, default_library_path, type,
+ permitted_when_isolated_path, TO_MOCK_NAMESPACE(parent)));
+}
+
+bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
+ struct native_bridge_namespace_t* to, const char* sonames) {
+ return mock->mock_link_namespaces(true, TO_MOCK_NAMESPACE(from), TO_MOCK_NAMESPACE(to), sonames);
+}
+
+void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
+ struct native_bridge_namespace_t* ns) {
+ return mock->mock_dlopen_ext(true, libpath, flag, TO_MOCK_NAMESPACE(ns));
+}
+
+bool NativeBridgeInitialized() {
+ return mock->NativeBridgeInitialized();
+}
+
+bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
+ const char* anon_ns_library_path) {
+ return mock->mock_init_anonymous_namespace(true, public_ns_sonames, anon_ns_library_path);
+}
+
+const char* NativeBridgeGetError() {
+ return mock->NativeBridgeGetError();
+}
+
+bool NativeBridgeIsPathSupported(const char* path) {
+ return mock->NativeBridgeIsPathSupported(path);
+}
+
+} // extern "C"
+
+// A very simple JNI mock.
+// jstring is a pointer to utf8 char array. We don't need utf16 char here.
+// jobject, jclass, and jmethodID are also a pointer to utf8 char array
+// Only a few JNI methods that are actually used in libnativeloader are mocked.
+JNINativeInterface* CreateJNINativeInterface() {
+ JNINativeInterface* inf = new JNINativeInterface();
+ memset(inf, 0, sizeof(JNINativeInterface));
+
+ inf->GetStringUTFChars = [](JNIEnv*, jstring s, jboolean*) -> const char* {
+ return reinterpret_cast<const char*>(s);
+ };
+
+ inf->ReleaseStringUTFChars = [](JNIEnv*, jstring, const char*) -> void { return; };
+
+ inf->NewStringUTF = [](JNIEnv*, const char* bytes) -> jstring {
+ return reinterpret_cast<jstring>(const_cast<char*>(bytes));
+ };
+
+ inf->FindClass = [](JNIEnv*, const char* name) -> jclass {
+ return reinterpret_cast<jclass>(const_cast<char*>(name));
+ };
+
+ inf->CallObjectMethodV = [](JNIEnv*, jobject obj, jmethodID mid, va_list) -> jobject {
+ if (strcmp("getParent", reinterpret_cast<const char*>(mid)) == 0) {
+ // JniObject_getParent can be a valid jobject or nullptr if there is
+ // no parent classloader.
+ const char* ret = mock->JniObject_getParent(reinterpret_cast<const char*>(obj));
+ return reinterpret_cast<jobject>(const_cast<char*>(ret));
+ }
+ return nullptr;
+ };
+
+ inf->GetMethodID = [](JNIEnv*, jclass, const char* name, const char*) -> jmethodID {
+ return reinterpret_cast<jmethodID>(const_cast<char*>(name));
+ };
+
+ inf->NewWeakGlobalRef = [](JNIEnv*, jobject obj) -> jobject { return obj; };
+
+ inf->IsSameObject = [](JNIEnv*, jobject a, jobject b) -> jboolean {
+ return strcmp(reinterpret_cast<const char*>(a), reinterpret_cast<const char*>(b)) == 0;
+ };
+
+ return inf;
+}
+
+static void* const any_nonnull = reinterpret_cast<void*>(0x12345678);
+
+// Custom matcher for comparing namespace handles
+MATCHER_P(NsEq, other, "") {
+ *result_listener << "comparing " << reinterpret_cast<const char*>(arg) << " and " << other;
+ return strcmp(reinterpret_cast<const char*>(arg), reinterpret_cast<const char*>(other)) == 0;
+}
+
+/////////////////////////////////////////////////////////////////
+
+// Test fixture
+class NativeLoaderTest : public ::testing::TestWithParam<bool> {
+ protected:
+ bool IsBridged() { return GetParam(); }
+
+ void SetUp() override {
+ mock = std::make_unique<NiceMock<MockPlatform>>(IsBridged());
+
+ env = std::make_unique<JNIEnv>();
+ env->functions = CreateJNINativeInterface();
+ }
+
+ void SetExpectations() {
+ std::vector<std::string> default_public_libs =
+ android::base::Split(default_public_libraries(), ":");
+ for (auto l : default_public_libs) {
+ EXPECT_CALL(*mock, dlopen(StrEq(l.c_str()), RTLD_NOW | RTLD_NODELETE))
+ .WillOnce(Return(any_nonnull));
+ }
+ }
+
+ void RunTest() { InitializeNativeLoader(); }
+
+ void TearDown() override {
+ ResetNativeLoader();
+ delete env->functions;
+ mock.reset();
+ }
+
+ std::unique_ptr<JNIEnv> env;
+};
+
+/////////////////////////////////////////////////////////////////
+
+TEST_P(NativeLoaderTest, InitializeLoadsDefaultPublicLibraries) {
+ SetExpectations();
+ RunTest();
+}
+
+INSTANTIATE_TEST_SUITE_P(NativeLoaderTests, NativeLoaderTest, testing::Bool());
+
+/////////////////////////////////////////////////////////////////
+
+class NativeLoaderTest_Create : public NativeLoaderTest {
+ protected:
+ // Test inputs (initialized to the default values). Overriding these
+ // must be done before calling SetExpectations() and RunTest().
+ uint32_t target_sdk_version = 29;
+ std::string class_loader = "my_classloader";
+ bool is_shared = false;
+ std::string dex_path = "/data/app/foo/classes.dex";
+ std::string library_path = "/data/app/foo/lib/arm";
+ std::string permitted_path = "/data/app/foo/lib";
+
+ // expected output (.. for the default test inputs)
+ std::string expected_namespace_name = "classloader-namespace";
+ uint64_t expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
+ std::string expected_library_path = library_path;
+ std::string expected_permitted_path = std::string("/data:/mnt/expand:") + permitted_path;
+ std::string expected_parent_namespace = "platform";
+ bool expected_link_with_platform_ns = true;
+ bool expected_link_with_runtime_ns = true;
+ bool expected_link_with_sphal_ns = true;
+ bool expected_link_with_vndk_ns = false;
+ bool expected_link_with_default_ns = false;
+ std::string expected_shared_libs_to_platform_ns = default_public_libraries();
+ std::string expected_shared_libs_to_runtime_ns = runtime_public_libraries();
+ std::string expected_shared_libs_to_sphal_ns = vendor_public_libraries();
+ std::string expected_shared_libs_to_vndk_ns = vndksp_libraries();
+ std::string expected_shared_libs_to_default_ns = default_public_libraries();
+
+ void SetExpectations() {
+ NativeLoaderTest::SetExpectations();
+
+ ON_CALL(*mock, JniObject_getParent(StrEq(class_loader))).WillByDefault(Return(nullptr));
+
+ EXPECT_CALL(*mock, NativeBridgeIsPathSupported(_)).Times(AnyNumber());
+ EXPECT_CALL(*mock, NativeBridgeInitialized()).Times(AnyNumber());
+
+ if (IsBridged()) {
+ EXPECT_CALL(*mock,
+ mock_init_anonymous_namespace(false, StrEq(default_public_libraries()), nullptr))
+ .WillOnce(Return(true));
+
+ EXPECT_CALL(*mock, NativeBridgeInitialized()).WillOnce(Return(true));
+ }
+
+ EXPECT_CALL(*mock, mock_init_anonymous_namespace(
+ Eq(IsBridged()), StrEq(default_public_libraries()), StrEq(library_path)))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock, mock_create_namespace(
+ Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
+ StrEq(expected_library_path), expected_namespace_flags,
+ StrEq(expected_permitted_path), NsEq(expected_parent_namespace.c_str())))
+ .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(dex_path.c_str()))));
+ if (expected_link_with_platform_ns) {
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("platform"),
+ StrEq(expected_shared_libs_to_platform_ns)))
+ .WillOnce(Return(true));
+ }
+ if (expected_link_with_runtime_ns) {
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("runtime"),
+ StrEq(expected_shared_libs_to_runtime_ns)))
+ .WillOnce(Return(true));
+ }
+ if (expected_link_with_sphal_ns) {
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("sphal"),
+ StrEq(expected_shared_libs_to_sphal_ns)))
+ .WillOnce(Return(true));
+ }
+ if (expected_link_with_vndk_ns) {
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("vndk"),
+ StrEq(expected_shared_libs_to_vndk_ns)))
+ .WillOnce(Return(true));
+ }
+ if (expected_link_with_default_ns) {
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("default"),
+ StrEq(expected_shared_libs_to_default_ns)))
+ .WillOnce(Return(true));
+ }
+ }
+
+ void RunTest() {
+ NativeLoaderTest::RunTest();
+
+ jstring err = CreateClassLoaderNamespace(
+ env(), target_sdk_version, env()->NewStringUTF(class_loader.c_str()), is_shared,
+ env()->NewStringUTF(dex_path.c_str()), env()->NewStringUTF(library_path.c_str()),
+ env()->NewStringUTF(permitted_path.c_str()));
+
+ // no error
+ EXPECT_EQ(err, nullptr);
+
+ if (!IsBridged()) {
+ struct android_namespace_t* ns =
+ FindNamespaceByClassLoader(env(), env()->NewStringUTF(class_loader.c_str()));
+
+ // The created namespace is for this apk
+ EXPECT_EQ(dex_path.c_str(), reinterpret_cast<const char*>(ns));
+ } else {
+ struct NativeLoaderNamespace* ns =
+ FindNativeLoaderNamespaceByClassLoader(env(), env()->NewStringUTF(class_loader.c_str()));
+
+ // The created namespace is for the this apk
+ EXPECT_STREQ(dex_path.c_str(),
+ reinterpret_cast<const char*>(ns->ToRawNativeBridgeNamespace()));
+ }
+ }
+
+ JNIEnv* env() { return NativeLoaderTest::env.get(); }
+};
+
+TEST_P(NativeLoaderTest_Create, DownloadedApp) {
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, BundledSystemApp) {
+ dex_path = "/system/app/foo/foo.apk";
+ is_shared = true;
+
+ expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, BundledVendorApp) {
+ dex_path = "/vendor/app/foo/foo.apk";
+ is_shared = true;
+
+ expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, UnbundledVendorApp) {
+ dex_path = "/vendor/app/foo/foo.apk";
+ is_shared = false;
+
+ expected_namespace_name = "vendor-classloader-namespace";
+ expected_library_path = expected_library_path + ":/vendor/lib";
+ expected_permitted_path = expected_permitted_path + ":/vendor/lib";
+ expected_shared_libs_to_platform_ns =
+ expected_shared_libs_to_platform_ns + ":" + llndk_libraries();
+ expected_link_with_vndk_ns = true;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, BundledProductApp_pre30) {
+ dex_path = "/product/app/foo/foo.apk";
+ is_shared = true;
+
+ expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, BundledProductApp_post30) {
+ dex_path = "/product/app/foo/foo.apk";
+ is_shared = true;
+ target_sdk_version = 30;
+
+ expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, UnbundledProductApp_pre30) {
+ dex_path = "/product/app/foo/foo.apk";
+ is_shared = false;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, UnbundledProductApp_post30) {
+ dex_path = "/product/app/foo/foo.apk";
+ is_shared = false;
+ target_sdk_version = 30;
+
+ expected_namespace_name = "vendor-classloader-namespace";
+ expected_library_path = expected_library_path + ":/product/lib:/system/product/lib";
+ expected_permitted_path = expected_permitted_path + ":/product/lib:/system/product/lib";
+ expected_shared_libs_to_platform_ns =
+ expected_shared_libs_to_platform_ns + ":" + llndk_libraries();
+ expected_link_with_vndk_ns = true;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, TwoApks) {
+ SetExpectations();
+ const uint32_t second_app_target_sdk_version = 29;
+ const std::string second_app_class_loader = "second_app_classloader";
+ const bool second_app_is_shared = false;
+ const std::string second_app_dex_path = "/data/app/bar/classes.dex";
+ const std::string second_app_library_path = "/data/app/bar/lib/arm";
+ const std::string second_app_permitted_path = "/data/app/bar/lib";
+ const std::string expected_second_app_permitted_path =
+ std::string("/data:/mnt/expand:") + second_app_permitted_path;
+ const std::string expected_second_app_parent_namespace = "classloader-namespace";
+
+ // The scenario is that second app is loaded by the first app.
+ // So the first app's classloader (`classloader`) is parent of the second
+ // app's classloader.
+ ON_CALL(*mock, JniObject_getParent(StrEq(second_app_class_loader)))
+ .WillByDefault(Return(class_loader.c_str()));
+
+ // namespace for the second app is created. Its parent is set to the namespace
+ // of the first app.
+ EXPECT_CALL(*mock, mock_create_namespace(Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
+ StrEq(second_app_library_path), expected_namespace_flags,
+ StrEq(expected_second_app_permitted_path),
+ NsEq(dex_path.c_str())))
+ .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(second_app_dex_path.c_str()))));
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), NsEq(second_app_dex_path.c_str()), _, _))
+ .WillRepeatedly(Return(true));
+
+ RunTest();
+ jstring err = CreateClassLoaderNamespace(
+ env(), second_app_target_sdk_version, env()->NewStringUTF(second_app_class_loader.c_str()),
+ second_app_is_shared, env()->NewStringUTF(second_app_dex_path.c_str()),
+ env()->NewStringUTF(second_app_library_path.c_str()),
+ env()->NewStringUTF(second_app_permitted_path.c_str()));
+
+ // success
+ EXPECT_EQ(err, nullptr);
+
+ if (!IsBridged()) {
+ struct android_namespace_t* ns =
+ FindNamespaceByClassLoader(env(), env()->NewStringUTF(second_app_class_loader.c_str()));
+
+ // The created namespace is for the second apk
+ EXPECT_EQ(second_app_dex_path.c_str(), reinterpret_cast<const char*>(ns));
+ } else {
+ struct NativeLoaderNamespace* ns = FindNativeLoaderNamespaceByClassLoader(
+ env(), env()->NewStringUTF(second_app_class_loader.c_str()));
+
+ // The created namespace is for the second apk
+ EXPECT_STREQ(second_app_dex_path.c_str(),
+ reinterpret_cast<const char*>(ns->ToRawNativeBridgeNamespace()));
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(NativeLoaderTests_Create, NativeLoaderTest_Create, testing::Bool());
+
+// TODO(b/130388701#comment22) add a test for anonymous namespace
+
+} // namespace nativeloader
+} // namespace android
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index a0a6b4f..73237e6 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -125,10 +125,6 @@
},
},
- whole_static_libs: [
- "libdemangle"
- ],
-
static_libs: [
"libprocinfo",
],
@@ -162,6 +158,7 @@
cc_test {
name: "libunwindstack_test",
defaults: ["libunwindstack_flags"],
+ isolated: true,
srcs: [
"tests/ArmExidxDecodeTest.cpp",
@@ -184,6 +181,7 @@
"tests/ElfInterfaceTest.cpp",
"tests/ElfTest.cpp",
"tests/ElfTestUtils.cpp",
+ "tests/IsolatedSettings.cpp",
"tests/JitDebugTest.cpp",
"tests/LocalUnwinderTest.cpp",
"tests/LogFake.cpp",
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index c95f852..7556482 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -27,8 +27,6 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <demangle.h>
-
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/MapInfo.h>
@@ -40,6 +38,9 @@
#include <unwindstack/DexFiles.h>
#endif
+// Use the demangler from libc++.
+extern "C" char* __cxa_demangle(const char*, char*, size_t*, int* status);
+
namespace unwindstack {
// Inject extra 'virtual' frame that represents the dex pc data.
@@ -330,7 +331,14 @@
}
if (!frame.function_name.empty()) {
- data += " (" + demangle(frame.function_name.c_str());
+ char* demangled_name = __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr, nullptr);
+ if (demangled_name == nullptr) {
+ data += " (" + frame.function_name;
+ } else {
+ data += " (";
+ data += demangled_name;
+ free(demangled_name);
+ }
if (frame.function_offset != 0) {
data += android::base::StringPrintf("+%" PRId64, frame.function_offset);
}
diff --git a/libunwindstack/tests/IsolatedSettings.cpp b/libunwindstack/tests/IsolatedSettings.cpp
new file mode 100644
index 0000000..dbd8bd6
--- /dev/null
+++ b/libunwindstack/tests/IsolatedSettings.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+extern "C" bool GetInitialArgs(const char*** args, size_t* num_args) {
+ static const char* initial_args[2] = {"--slow_threshold_ms=90000",
+ "--deadline_threshold_ms=120000"};
+ *args = initial_args;
+ *num_args = 2;
+ return true;
+}
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 5b4ca7c..6c1cfa2 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -58,7 +58,7 @@
ASSERT_TRUE(android::base::WriteFully(fd, buffer.data(), buffer.size()));
}
- static void SetUpTestSuite() {
+ void SetUp() override {
std::vector<uint8_t> buffer(12288, 0);
memcpy(buffer.data(), ELFMAG, SELFMAG);
buffer[EI_CLASS] = ELFCLASS32;
@@ -72,9 +72,7 @@
InitElf<Elf32_Ehdr, Elf32_Shdr>(elf32_at_map_.fd, 0x1000, 0x2000, ELFCLASS32);
InitElf<Elf64_Ehdr, Elf64_Shdr>(elf64_at_map_.fd, 0x2000, 0x3000, ELFCLASS64);
- }
- void SetUp() override {
memory_ = new MemoryFake;
process_memory_.reset(memory_);
}
@@ -82,17 +80,13 @@
MemoryFake* memory_;
std::shared_ptr<Memory> process_memory_;
- static TemporaryFile elf_;
+ TemporaryFile elf_;
- static TemporaryFile elf_at_1000_;
+ TemporaryFile elf_at_1000_;
- static TemporaryFile elf32_at_map_;
- static TemporaryFile elf64_at_map_;
+ TemporaryFile elf32_at_map_;
+ TemporaryFile elf64_at_map_;
};
-TemporaryFile MapInfoCreateMemoryTest::elf_;
-TemporaryFile MapInfoCreateMemoryTest::elf_at_1000_;
-TemporaryFile MapInfoCreateMemoryTest::elf32_at_map_;
-TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
TEST_F(MapInfoCreateMemoryTest, end_le_start) {
MapInfo info(nullptr, 0x100, 0x100, 0, 0, elf_.path);
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index e6158a2..bded57a 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -1482,11 +1482,15 @@
" #09 pc 0000000000ed5e25 perfetto_unittests "
"(testing::internal::UnitTestImpl::RunAllTests()+581)\n"
" #10 pc 0000000000ef63f3 perfetto_unittests "
- "(_ZN7testing8internal38HandleSehExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_"
- "MS4_FS3_vEPKc+131)\n"
+ "(bool "
+ "testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, "
+ "bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char "
+ "const*)+131)\n"
" #11 pc 0000000000ee2a21 perfetto_unittests "
- "(_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_"
- "FS3_vEPKc+113)\n"
+ "(bool "
+ "testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, "
+ "bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char "
+ "const*)+113)\n"
" #12 pc 0000000000ed5bb9 perfetto_unittests (testing::UnitTest::Run()+185)\n"
" #13 pc 0000000000e900f0 perfetto_unittests (RUN_ALL_TESTS()+16)\n"
" #14 pc 0000000000e900d8 perfetto_unittests (main+56)\n"
diff --git a/libutils/include/utils/LightRefBase.h b/libutils/include/utils/LightRefBase.h
index e488e60..b04e5c1 100644
--- a/libutils/include/utils/LightRefBase.h
+++ b/libutils/include/utils/LightRefBase.h
@@ -47,8 +47,6 @@
return mCount.load(std::memory_order_relaxed);
}
- typedef LightRefBase<T> basetype;
-
protected:
inline ~LightRefBase() { }
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index 3a02a8a..42c6efb 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -296,8 +296,6 @@
getWeakRefs()->trackMe(enable, retain);
}
- typedef RefBase basetype;
-
protected:
RefBase();
virtual ~RefBase();
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index e5b536c..f4544a1 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -3,7 +3,7 @@
cc_library {
name: "libvndksupport",
native_bridge_supported: true,
- srcs: ["linker.c"],
+ srcs: ["linker.cpp"],
cflags: [
"-Wall",
"-Werror",
diff --git a/libvndksupport/linker.c b/libvndksupport/linker.c
deleted file mode 100644
index 84c2132..0000000
--- a/libvndksupport/linker.c
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "linker.h"
-
-#include <android/dlext.h>
-#include <dlfcn.h>
-
-#define LOG_TAG "vndksupport"
-#include <log/log.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-__attribute__((weak)) extern struct android_namespace_t* android_get_exported_namespace(const char*);
-__attribute__((weak)) extern void* android_dlopen_ext(const char*, int, const android_dlextinfo*);
-
-static const char* namespace_name = NULL;
-
-static struct android_namespace_t* get_vendor_namespace() {
- const char* namespace_names[] = {"sphal", "default", NULL};
- static struct android_namespace_t* vendor_namespace = NULL;
- if (vendor_namespace == NULL) {
- int name_idx = 0;
- while (namespace_names[name_idx] != NULL) {
- if (android_get_exported_namespace != NULL) {
- vendor_namespace = android_get_exported_namespace(namespace_names[name_idx]);
- }
- if (vendor_namespace != NULL) {
- namespace_name = namespace_names[name_idx];
- break;
- }
- name_idx++;
- }
- }
- return vendor_namespace;
-}
-
-int android_is_in_vendor_process() {
- // Special case init, since when init runs, ld.config.<ver>.txt hasn't been
- // loaded (sysprop service isn't up for init to know <ver>).
- if (getpid() == 1) {
- return 0;
- }
- if (android_get_exported_namespace == NULL) {
- ALOGD("android_get_exported_namespace() not available. Assuming system process.");
- return 0;
- }
-
- // In vendor process, 'vndk' namespace is not visible, whereas in system
- // process, it is.
- return android_get_exported_namespace("vndk") == NULL;
-}
-
-void* android_load_sphal_library(const char* name, int flag) {
- struct android_namespace_t* vendor_namespace = get_vendor_namespace();
- if (vendor_namespace != NULL) {
- const android_dlextinfo dlextinfo = {
- .flags = ANDROID_DLEXT_USE_NAMESPACE, .library_namespace = vendor_namespace,
- };
- void* handle = NULL;
- if (android_dlopen_ext != NULL) {
- handle = android_dlopen_ext(name, flag, &dlextinfo);
- }
- if (!handle) {
- ALOGE("Could not load %s from %s namespace: %s.", name, namespace_name, dlerror());
- }
- return handle;
- } else {
- ALOGD("Loading %s from current namespace instead of sphal namespace.", name);
- return dlopen(name, flag);
- }
-}
-
-int android_unload_sphal_library(void* handle) {
- return dlclose(handle);
-}
diff --git a/libvndksupport/linker.cpp b/libvndksupport/linker.cpp
new file mode 100644
index 0000000..cf0f618
--- /dev/null
+++ b/libvndksupport/linker.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "vndksupport"
+
+#include "linker.h"
+
+#include <android/dlext.h>
+#include <dlfcn.h>
+#include <log/log.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <initializer_list>
+
+__attribute__((weak)) extern "C" android_namespace_t* android_get_exported_namespace(const char*);
+__attribute__((weak)) extern "C" void* android_dlopen_ext(const char*, int,
+ const android_dlextinfo*);
+
+namespace {
+
+struct VendorNamespace {
+ android_namespace_t* ptr = nullptr;
+ const char* name = nullptr;
+};
+
+} // anonymous namespace
+
+static VendorNamespace get_vendor_namespace() {
+ static VendorNamespace result = ([] {
+ for (const char* name : {"sphal", "default"}) {
+ if (android_get_exported_namespace != nullptr) {
+ if (android_namespace_t* ns = android_get_exported_namespace(name)) {
+ return VendorNamespace{ns, name};
+ }
+ }
+ }
+ return VendorNamespace{};
+ })();
+ return result;
+}
+
+int android_is_in_vendor_process() {
+ // Special case init, since when init runs, ld.config.<ver>.txt hasn't been
+ // loaded (sysprop service isn't up for init to know <ver>).
+ if (getpid() == 1) {
+ return 0;
+ }
+ if (android_get_exported_namespace == nullptr) {
+ ALOGD("android_get_exported_namespace() not available. Assuming system process.");
+ return 0;
+ }
+
+ // In vendor process, 'vndk' namespace is not visible, whereas in system
+ // process, it is.
+ return android_get_exported_namespace("vndk") == nullptr;
+}
+
+void* android_load_sphal_library(const char* name, int flag) {
+ VendorNamespace vendor_namespace = get_vendor_namespace();
+ if (vendor_namespace.ptr != nullptr) {
+ const android_dlextinfo dlextinfo = {
+ .flags = ANDROID_DLEXT_USE_NAMESPACE,
+ .library_namespace = vendor_namespace.ptr,
+ };
+ void* handle = nullptr;
+ if (android_dlopen_ext != nullptr) {
+ handle = android_dlopen_ext(name, flag, &dlextinfo);
+ }
+ if (!handle) {
+ ALOGE("Could not load %s from %s namespace: %s.", name, vendor_namespace.name,
+ dlerror());
+ }
+ return handle;
+ } else {
+ ALOGD("Loading %s from current namespace instead of sphal namespace.", name);
+ return dlopen(name, flag);
+ }
+}
+
+int android_unload_sphal_library(void* handle) {
+ return dlclose(handle);
+}
diff --git a/libvndksupport/tests/linker_test.cpp b/libvndksupport/tests/linker_test.cpp
index 7ce27d4..d0c8ef7 100644
--- a/libvndksupport/tests/linker_test.cpp
+++ b/libvndksupport/tests/linker_test.cpp
@@ -21,11 +21,6 @@
#include <vndksupport/linker.h>
#include <string>
-// Since the test executable will be in /data and ld.config.txt does not
-// configure sphal namespace for executables in /data, the call to
-// android_load_sphal_library will always fallback to the plain dlopen from the
-// default namespace.
-
// Let's use libEGL_<chipset>.so as a SP-HAL in test
static std::string find_sphal_lib() {
const char* path =
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 96ffa69..d22e9a7 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -38,6 +38,13 @@
# Allow up to 32K FDs per process
setrlimit nofile 32768 32768
+ # Create directory to keep ld.config.txt
+ mkdir /dev/linkerconfig 0755
+
+ # Generate ld.config.txt for early executed processes
+ exec -- /system/bin/linkerconfig --target /dev/linkerconfig/ld.config.txt
+ chmod 444 /dev/linkerconfig/ld.config.txt
+
start ueventd
# Run apexd-bootstrap so that APEXes that provide critical libraries
@@ -288,6 +295,11 @@
chown system system /sys/power/wakeup_count
chmod 0660 /sys/power/state
+ chown radio wakelock /sys/power/wake_lock
+ chown radio wakelock /sys/power/wake_unlock
+ chmod 0660 /sys/power/wake_lock
+ chmod 0660 /sys/power/wake_unlock
+
# Start logd before any other services run to ensure we capture all of their logs.
start logd
@@ -402,6 +414,7 @@
mkdir /metadata/vold
chmod 0700 /metadata/vold
mkdir /metadata/password_slots 0771 root system
+ mkdir /metadata/ota 0700 root system
mkdir /metadata/apex 0700 root system
mkdir /metadata/apex/sessions 0700 root system
@@ -499,7 +512,6 @@
mkdir /data/misc/ethernet 0770 system system
mkdir /data/misc/dhcp 0770 dhcp dhcp
mkdir /data/misc/user 0771 root root
- mkdir /data/misc/perfprofd 0775 root root
# give system access to wpa_supplicant.conf for backup and restore
chmod 0660 /data/misc/wifi/wpa_supplicant.conf
mkdir /data/local 0751 root root
@@ -682,10 +694,6 @@
chown radio system /sys/android_power/acquire_partial_wake_lock
chown radio system /sys/android_power/release_wake_lock
chown system system /sys/power/autosleep
- chown radio wakelock /sys/power/wake_lock
- chown radio wakelock /sys/power/wake_unlock
- chmod 0660 /sys/power/wake_lock
- chmod 0660 /sys/power/wake_unlock
chown system system /sys/devices/system/cpu/cpufreq/interactive/timer_rate
chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/timer_rate
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
index c949a4f..ee29afc 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -88,7 +88,7 @@
# $(2): output file with the filtered list of lib names
$(LOCAL_BUILT_MODULE): private-filter-out-private-libs = \
paste -sd ":" $(1) > $(2) && \
- cat $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) | xargs -n 1 -I privatelib bash -c "sed -i.bak 's/privatelib//' $(2)" && \
+ while read -r privatelib; do sed -i.bak "s/$$privatelib//" $(2) ; done < $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) && \
sed -i.bak -e 's/::\+/:/g ; s/^:\+// ; s/:\+$$//' $(2) && \
rm -f $(2).bak
$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES_FILE := $(llndk_libraries_file)
@@ -139,8 +139,9 @@
endif
$(hide) echo -n > $(PRIVATE_INTERMEDIATES_DIR)/private_llndk && \
- cat $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) | \
- xargs -n 1 -I privatelib bash -c "(grep privatelib $(PRIVATE_LLNDK_LIBRARIES_FILE) || true) >> $(PRIVATE_INTERMEDIATES_DIR)/private_llndk" && \
+ while read -r privatelib; \
+ do (grep $$privatelib $(PRIVATE_LLNDK_LIBRARIES_FILE) || true) >> $(PRIVATE_INTERMEDIATES_DIR)/private_llndk ; \
+ done < $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) && \
paste -sd ":" $(PRIVATE_INTERMEDIATES_DIR)/private_llndk | \
sed -i.bak -e "s?%PRIVATE_LLNDK_LIBRARIES%?$$(cat -)?g" $@