Merge changes Iacf0093a,I40be3504
* changes:
adb: fdevent: add fdevent_context_epoll.
adb: extract soon-to-be-common code.
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index faad03d..66cba12 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -16,6 +16,7 @@
#pragma once
+#include <charconv>
#include <condition_variable>
#include <mutex>
#include <string>
@@ -112,33 +113,17 @@
// Base-10 stroll on a string_view.
template <typename T>
inline bool ParseUint(T* result, std::string_view str, std::string_view* remaining = nullptr) {
- if (str.empty() || !isdigit(str[0])) {
+ T value;
+ const auto res = std::from_chars(str.begin(), str.end(), value);
+ if (res.ec != std::errc{}) {
return false;
}
-
- T value = 0;
- std::string_view::iterator it;
- constexpr T max = std::numeric_limits<T>::max();
- for (it = str.begin(); it != str.end() && isdigit(*it); ++it) {
- if (value > max / 10) {
- return false;
- }
-
- value *= 10;
-
- T digit = *it - '0';
- if (value > max - digit) {
- return false;
- }
-
- value += digit;
+ if (res.ptr != str.end() && !remaining) {
+ return false;
+ }
+ if (remaining) {
+ *remaining = std::string_view(res.ptr, str.end() - res.ptr);
}
*result = value;
- if (remaining) {
- *remaining = str.substr(it - str.begin());
- } else {
- return it == str.end();
- }
-
return true;
}
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 599e0e6..fe2bdfe 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -1704,10 +1704,23 @@
error_exit("tcpip: invalid port: %s", argv[1]);
}
return adb_connect_command(android::base::StringPrintf("tcpip:%d", port));
+ } else if (!strcmp(argv[0], "remount")) {
+ FeatureSet features;
+ std::string error;
+ if (!adb_get_feature_set(&features, &error)) {
+ fprintf(stderr, "error: %s\n", error.c_str());
+ return 1;
+ }
+
+ if (CanUseFeature(features, kFeatureRemountShell)) {
+ const char* arg[2] = {"shell", "remount"};
+ return adb_shell(2, arg);
+ } else {
+ return adb_connect_command("remount:");
+ }
}
// clang-format off
- else if (!strcmp(argv[0], "remount") ||
- !strcmp(argv[0], "reboot") ||
+ else if (!strcmp(argv[0], "reboot") ||
!strcmp(argv[0], "reboot-bootloader") ||
!strcmp(argv[0], "reboot-fastboot") ||
!strcmp(argv[0], "usb") ||
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index ce494ee..6bd7855 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -48,6 +48,8 @@
dup2(fd, STDERR_FILENO);
execl(kRemountCmd, kRemountCmd, cmd.empty() ? nullptr : cmd.c_str(), nullptr);
+ const char* msg = "failed to exec remount\n";
+ write(STDERR_FILENO, msg, strlen(msg));
_exit(errno);
}
@@ -83,6 +85,6 @@
}
void remount_service(unique_fd fd, const std::string& cmd) {
- const char* success = do_remount(fd.get(), cmd) ? "succeeded" : "failed";
- WriteFdFmt(fd.get(), "remount %s\n", success);
+ do_remount(fd.get(), cmd);
+ // The remount command will print success or failure for us.
}
diff --git a/adb/daemon/usb_ffs.cpp b/adb/daemon/usb_ffs.cpp
index a64ce40..338d776 100644
--- a/adb/daemon/usb_ffs.cpp
+++ b/adb/daemon/usb_ffs.cpp
@@ -299,7 +299,6 @@
}
// Signal only when writing the descriptors to ffs
android::base::SetProperty("sys.usb.ffs.ready", "1");
- *out_control = std::move(control);
}
bulk_out.reset(adb_open(USB_FFS_ADB_OUT, O_RDONLY));
@@ -314,6 +313,7 @@
return false;
}
+ *out_control = std::move(control);
*out_bulk_in = std::move(bulk_in);
*out_bulk_out = std::move(bulk_out);
return true;
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index b0e7fa0..987f994 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -33,6 +33,7 @@
// Include this before open/close/unlink are defined as macros below.
#include <android-base/errors.h>
#include <android-base/macros.h>
+#include <android-base/off64_t.h>
#include <android-base/unique_fd.h>
#include <android-base/utf8.h>
@@ -91,11 +92,14 @@
extern int adb_open(const char* path, int options);
extern int adb_creat(const char* path, int mode);
extern int adb_read(borrowed_fd fd, void* buf, int len);
+extern int adb_pread(borrowed_fd fd, void* buf, int len, off64_t offset);
extern int adb_write(borrowed_fd fd, const void* buf, int len);
+extern int adb_pwrite(borrowed_fd fd, const void* buf, int len, off64_t offset);
extern int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where);
extern int adb_shutdown(borrowed_fd fd, int direction = SHUT_RDWR);
extern int adb_close(int fd);
extern int adb_register_socket(SOCKET s);
+extern HANDLE adb_get_os_handle(borrowed_fd fd);
// See the comments for the !defined(_WIN32) version of unix_close().
static __inline__ int unix_close(int fd) {
@@ -115,6 +119,9 @@
#undef read
#define read ___xxx_read
+#undef pread
+#define pread ___xxx_pread
+
// See the comments for the !defined(_WIN32) version of unix_write().
static __inline__ int unix_write(borrowed_fd fd, const void* buf, size_t len) {
return write(fd.get(), buf, len);
@@ -122,6 +129,9 @@
#undef write
#define write ___xxx_write
+#undef pwrite
+#define pwrite ___xxx_pwrite
+
// See the comments for the !defined(_WIN32) version of unix_lseek().
static __inline__ int unix_lseek(borrowed_fd fd, int pos, int where) {
return lseek(fd.get(), pos, where);
@@ -415,6 +425,14 @@
return TEMP_FAILURE_RETRY(read(fd.get(), buf, len));
}
+static __inline__ int adb_pread(int fd, void* buf, size_t len, off64_t offset) {
+#if defined(__APPLE__)
+ return TEMP_FAILURE_RETRY(pread(fd, buf, len, offset));
+#else
+ return TEMP_FAILURE_RETRY(pread64(fd, buf, len, offset));
+#endif
+}
+
// Like unix_read(), but does not handle EINTR.
static __inline__ int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len) {
return read(fd.get(), buf, len);
@@ -422,12 +440,25 @@
#undef read
#define read ___xxx_read
+#undef pread
+#define pread ___xxx_pread
static __inline__ int adb_write(borrowed_fd fd, const void* buf, size_t len) {
return TEMP_FAILURE_RETRY(write(fd.get(), buf, len));
}
+
+static __inline__ int adb_pwrite(int fd, const void* buf, size_t len, off64_t offset) {
+#if defined(__APPLE__)
+ return TEMP_FAILURE_RETRY(pwrite(fd, buf, len, offset));
+#else
+ return TEMP_FAILURE_RETRY(pwrite64(fd, buf, len, offset));
+#endif
+}
+
#undef write
#define write ___xxx_write
+#undef pwrite
+#define pwrite ___xxx_pwrite
static __inline__ int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where) {
#if defined(__APPLE__)
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 6372b3d..4d6cf3d 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -60,6 +60,7 @@
int (*_fh_read)(FH, void*, int);
int (*_fh_write)(FH, const void*, int);
int (*_fh_writev)(FH, const adb_iovec*, int);
+ intptr_t (*_fh_get_os_handle)(FH);
} FHClassRec;
static void _fh_file_init(FH);
@@ -68,14 +69,11 @@
static int _fh_file_read(FH, void*, int);
static int _fh_file_write(FH, const void*, int);
static int _fh_file_writev(FH, const adb_iovec*, int);
+static intptr_t _fh_file_get_os_handle(FH f);
static const FHClassRec _fh_file_class = {
- _fh_file_init,
- _fh_file_close,
- _fh_file_lseek,
- _fh_file_read,
- _fh_file_write,
- _fh_file_writev,
+ _fh_file_init, _fh_file_close, _fh_file_lseek, _fh_file_read,
+ _fh_file_write, _fh_file_writev, _fh_file_get_os_handle,
};
static void _fh_socket_init(FH);
@@ -84,14 +82,11 @@
static int _fh_socket_read(FH, void*, int);
static int _fh_socket_write(FH, const void*, int);
static int _fh_socket_writev(FH, const adb_iovec*, int);
+static intptr_t _fh_socket_get_os_handle(FH f);
static const FHClassRec _fh_socket_class = {
- _fh_socket_init,
- _fh_socket_close,
- _fh_socket_lseek,
- _fh_socket_read,
- _fh_socket_write,
- _fh_socket_writev,
+ _fh_socket_init, _fh_socket_close, _fh_socket_lseek, _fh_socket_read,
+ _fh_socket_write, _fh_socket_writev, _fh_socket_get_os_handle,
};
#if defined(assert)
@@ -331,6 +326,10 @@
return li.QuadPart;
}
+static intptr_t _fh_file_get_os_handle(FH f) {
+ return reinterpret_cast<intptr_t>(f->u.handle);
+}
+
/**************************************************************************/
/**************************************************************************/
/***** *****/
@@ -456,6 +455,26 @@
return f->clazz->_fh_read(f, buf, len);
}
+int adb_pread(borrowed_fd fd, void* buf, int len, off64_t offset) {
+ OVERLAPPED overlapped = {};
+ overlapped.Offset = static_cast<DWORD>(offset);
+ overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
+ DWORD bytes_read;
+ if (!::ReadFile(adb_get_os_handle(fd), buf, static_cast<DWORD>(len), &bytes_read,
+ &overlapped)) {
+ D("adb_pread: could not read %d bytes from FD %d", len, fd.get());
+ switch (::GetLastError()) {
+ case ERROR_IO_PENDING:
+ errno = EAGAIN;
+ return -1;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ return static_cast<int>(bytes_read);
+}
+
int adb_write(borrowed_fd fd, const void* buf, int len) {
FH f = _fh_from_int(fd, __func__);
@@ -478,6 +497,25 @@
return f->clazz->_fh_writev(f, iov, iovcnt);
}
+int adb_pwrite(borrowed_fd fd, const void* buf, int len, off64_t offset) {
+ OVERLAPPED params = {};
+ params.Offset = static_cast<DWORD>(offset);
+ params.OffsetHigh = static_cast<DWORD>(offset >> 32);
+ DWORD bytes_written = 0;
+ if (!::WriteFile(adb_get_os_handle(fd), buf, len, &bytes_written, ¶ms)) {
+ D("adb_pwrite: could not write %d bytes to FD %d", len, fd.get());
+ switch (::GetLastError()) {
+ case ERROR_IO_PENDING:
+ errno = EAGAIN;
+ return -1;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ return static_cast<int>(bytes_written);
+}
+
int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where) {
FH f = _fh_from_int(fd, __func__);
if (!f) {
@@ -500,6 +538,20 @@
return 0;
}
+HANDLE adb_get_os_handle(borrowed_fd fd) {
+ FH f = _fh_from_int(fd, __func__);
+
+ if (!f) {
+ errno = EBADF;
+ return nullptr;
+ }
+
+ D("adb_get_os_handle: %s", f->name);
+ const intptr_t intptr_handle = f->clazz->_fh_get_os_handle(f);
+ const HANDLE handle = reinterpret_cast<const HANDLE>(intptr_handle);
+ return handle;
+}
+
/**************************************************************************/
/**************************************************************************/
/***** *****/
@@ -688,12 +740,16 @@
android::base::SystemErrorCodeToString(err).c_str());
}
_socket_set_errno(err);
- result = -1;
+ return -1;
}
CHECK_GE(static_cast<DWORD>(std::numeric_limits<int>::max()), bytes_written);
return static_cast<int>(bytes_written);
}
+static intptr_t _fh_socket_get_os_handle(FH f) {
+ return f->u.socket;
+}
+
/**************************************************************************/
/**************************************************************************/
/***** *****/
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 3d1d620..d9749ac 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -73,6 +73,7 @@
const char* const kFeatureAbb = "abb";
const char* const kFeatureFixedPushSymlinkTimestamp = "fixed_push_symlink_timestamp";
const char* const kFeatureAbbExec = "abb_exec";
+const char* const kFeatureRemountShell = "remount_shell";
namespace {
@@ -1049,6 +1050,7 @@
kFeatureAbb,
kFeatureFixedPushSymlinkTimestamp,
kFeatureAbbExec,
+ kFeatureRemountShell,
// Increment ADB_SERVER_VERSION when adding a feature that adbd needs
// to know about. Otherwise, the client can be stuck running an old
// version of the server even after upgrading their copy of adb.
diff --git a/adb/transport.h b/adb/transport.h
index f4490ed..245037e 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -69,6 +69,7 @@
extern const char* const kFeatureAbb;
// adbd properly updates symlink timestamps on push.
extern const char* const kFeatureFixedPushSymlinkTimestamp;
+extern const char* const kFeatureRemountShell;
TransportId NextTransportId();
diff --git a/base/include/android-base/endian.h b/base/include/android-base/endian.h
index cbbd8c9..2d0f614 100644
--- a/base/include/android-base/endian.h
+++ b/base/include/android-base/endian.h
@@ -18,6 +18,9 @@
/* A cross-platform equivalent of bionic's <sys/endian.h>. */
+/* For __BIONIC__ and __GLIBC__ */
+#include <sys/cdefs.h>
+
#if defined(__BIONIC__)
#include <sys/endian.h>
@@ -38,26 +41,32 @@
#define betoh16(x) be16toh(x)
#define betoh32(x) be32toh(x)
#define betoh64(x) be64toh(x)
+#define letoh16(x) le16toh(x)
+#define letoh32(x) le32toh(x)
+#define letoh64(x) le64toh(x)
#else
-/* Mac OS and Windows have nothing. */
-
-#define __LITTLE_ENDIAN 1234
-#define LITTLE_ENDIAN __LITTLE_ENDIAN
-
-#define __BIG_ENDIAN 4321
-#define BIG_ENDIAN __BIG_ENDIAN
-
-#define __BYTE_ORDER __LITTLE_ENDIAN
-#define BYTE_ORDER __BYTE_ORDER
-
+#if defined(__APPLE__)
+/* macOS has some of the basics. */
+#include <sys/_endian.h>
+#else
+/* Windows has even less. */
+#include <sys/param.h>
#define htons(x) __builtin_bswap16(x)
#define htonl(x) __builtin_bswap32(x)
-#define htonq(x) __builtin_bswap64(x)
-
#define ntohs(x) __builtin_bswap16(x)
#define ntohl(x) __builtin_bswap32(x)
+#endif
+
+/* Neither macOS nor Windows have the rest. */
+
+#define __LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
+#define __BYTE_ORDER __LITTLE_ENDIAN
+
+#define htonq(x) __builtin_bswap64(x)
+
#define ntohq(x) __builtin_bswap64(x)
#define htobe16(x) __builtin_bswap16(x)
diff --git a/base/include/android-base/mapped_file.h b/base/include/android-base/mapped_file.h
index 2ab49ab..6a19f1b 100644
--- a/base/include/android-base/mapped_file.h
+++ b/base/include/android-base/mapped_file.h
@@ -28,15 +28,17 @@
#include <windows.h>
#define PROT_READ 1
#define PROT_WRITE 2
+using os_handle = HANDLE;
#else
#include <sys/mman.h>
+using os_handle = int;
#endif
namespace android {
namespace base {
/**
- * A region of a file mapped into memory, also known as MmapFile.
+ * A region of a file mapped into memory (for grepping: also known as MmapFile or file mapping).
*/
class MappedFile {
public:
@@ -49,16 +51,33 @@
int prot);
/**
+ * Same thing, but using the raw OS file handle instead of a CRT wrapper.
+ */
+ static MappedFile FromOsHandle(os_handle h, off64_t offset, size_t length, int prot);
+
+ /**
* Removes the mapping.
*/
~MappedFile();
- char* data() { return base_ + offset_; }
- size_t size() { return size_; }
+ /**
+ * Not copyable but movable.
+ */
+ MappedFile(MappedFile&& other);
+ MappedFile& operator=(MappedFile&& other);
+
+ char* data() const { return base_ + offset_; }
+ size_t size() const { return size_; }
+
+ bool isValid() const { return base_ != nullptr; }
+
+ explicit operator bool() const { return isValid(); }
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(MappedFile);
+ void Close();
+
char* base_;
size_t size_;
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
index f60de56..862b73b 100644
--- a/base/mapped_file.cpp
+++ b/base/mapped_file.cpp
@@ -16,13 +16,15 @@
#include "android-base/mapped_file.h"
-#include <errno.h>
+#include <utility>
-#include "android-base/unique_fd.h"
+#include <errno.h>
namespace android {
namespace base {
+static constexpr char kEmptyBuffer[] = {'0'};
+
static off64_t InitPageSize() {
#if defined(_WIN32)
SYSTEM_INFO si;
@@ -35,51 +37,86 @@
std::unique_ptr<MappedFile> MappedFile::FromFd(borrowed_fd fd, off64_t offset, size_t length,
int prot) {
- static off64_t page_size = InitPageSize();
+#if defined(_WIN32)
+ auto file =
+ FromOsHandle(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), offset, length, prot);
+#else
+ auto file = FromOsHandle(fd.get(), offset, length, prot);
+#endif
+ return file ? std::make_unique<MappedFile>(std::move(file)) : std::unique_ptr<MappedFile>{};
+}
+
+MappedFile MappedFile::FromOsHandle(os_handle h, off64_t offset, size_t length, int prot) {
+ static const off64_t page_size = InitPageSize();
size_t slop = offset % page_size;
off64_t file_offset = offset - slop;
off64_t file_length = length + slop;
#if defined(_WIN32)
- HANDLE handle =
- CreateFileMapping(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), nullptr,
- (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
+ HANDLE handle = CreateFileMappingW(
+ h, nullptr, (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
if (handle == nullptr) {
// http://b/119818070 "app crashes when reading asset of zero length".
// Return a MappedFile that's only valid for reading the size.
- if (length == 0) {
- return std::unique_ptr<MappedFile>(new MappedFile{nullptr, 0, 0, nullptr});
+ if (length == 0 && ::GetLastError() == ERROR_FILE_INVALID) {
+ return MappedFile{const_cast<char*>(kEmptyBuffer), 0, 0, nullptr};
}
- return nullptr;
+ return MappedFile(nullptr, 0, 0, nullptr);
}
void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
file_offset, file_length);
if (base == nullptr) {
CloseHandle(handle);
- return nullptr;
+ return MappedFile(nullptr, 0, 0, nullptr);
}
- return std::unique_ptr<MappedFile>(
- new MappedFile{static_cast<char*>(base), length, slop, handle});
+ return MappedFile{static_cast<char*>(base), length, slop, handle};
#else
- void* base = mmap(nullptr, file_length, prot, MAP_SHARED, fd.get(), file_offset);
+ void* base = mmap(nullptr, file_length, prot, MAP_SHARED, h, file_offset);
if (base == MAP_FAILED) {
// http://b/119818070 "app crashes when reading asset of zero length".
// mmap fails with EINVAL for a zero length region.
if (errno == EINVAL && length == 0) {
- return std::unique_ptr<MappedFile>(new MappedFile{nullptr, 0, 0});
+ return MappedFile{const_cast<char*>(kEmptyBuffer), 0, 0};
}
- return nullptr;
+ return MappedFile(nullptr, 0, 0);
}
- return std::unique_ptr<MappedFile>(new MappedFile{static_cast<char*>(base), length, slop});
+ return MappedFile{static_cast<char*>(base), length, slop};
#endif
}
+MappedFile::MappedFile(MappedFile&& other)
+ : base_(std::exchange(other.base_, nullptr)),
+ size_(std::exchange(other.size_, 0)),
+ offset_(std::exchange(other.offset_, 0))
+#ifdef _WIN32
+ ,
+ handle_(std::exchange(other.handle_, nullptr))
+#endif
+{
+}
+
+MappedFile& MappedFile::operator=(MappedFile&& other) {
+ Close();
+ base_ = std::exchange(other.base_, nullptr);
+ size_ = std::exchange(other.size_, 0);
+ offset_ = std::exchange(other.offset_, 0);
+#ifdef _WIN32
+ handle_ = std::exchange(other.handle_, nullptr);
+#endif
+ return *this;
+}
+
MappedFile::~MappedFile() {
+ Close();
+}
+
+void MappedFile::Close() {
#if defined(_WIN32)
- if (base_ != nullptr) UnmapViewOfFile(base_);
+ if (base_ != nullptr && size_ != 0) UnmapViewOfFile(base_);
if (handle_ != nullptr) CloseHandle(handle_);
+ handle_ = nullptr;
#else
- if (base_ != nullptr) munmap(base_, size_ + offset_);
+ if (base_ != nullptr && size_ != 0) munmap(base_, size_ + offset_);
#endif
base_ = nullptr;
diff --git a/base/mapped_file_test.cpp b/base/mapped_file_test.cpp
index cfde73c..3629108 100644
--- a/base/mapped_file_test.cpp
+++ b/base/mapped_file_test.cpp
@@ -44,5 +44,8 @@
ASSERT_TRUE(tf.fd != -1);
auto m = android::base::MappedFile::FromFd(tf.fd, 4096, 0, PROT_READ);
- ASSERT_EQ(0u, m->size());
+ ASSERT_NE(nullptr, m);
+ EXPECT_TRUE((bool)*m);
+ EXPECT_EQ(0u, m->size());
+ EXPECT_NE(nullptr, m->data());
}
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index e01e39b..b3f2d5f 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -56,8 +56,16 @@
if (!path) {
return false;
}
+
+ CreateLogicalPartitionParams params = {
+ .block_device = *path,
+ .metadata_slot = slot_number,
+ .partition_name = partition_name,
+ .force_writable = true,
+ .timeout_ms = 5s,
+ };
std::string dm_path;
- if (!CreateLogicalPartition(path->c_str(), slot_number, partition_name, true, 5s, &dm_path)) {
+ if (!CreateLogicalPartition(params, &dm_path)) {
LOG(ERROR) << "Could not map partition: " << partition_name;
return false;
}
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 04ba0bf..eaa515a 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -109,26 +109,6 @@
return true;
}
-static bool CreateLogicalPartition(const LpMetadata& metadata, const LpMetadataPartition& partition,
- bool force_writable, const std::chrono::milliseconds& timeout_ms,
- const std::string& super_device, std::string* path) {
- DeviceMapper& dm = DeviceMapper::Instance();
-
- DmTable table;
- if (!CreateDmTable(metadata, partition, super_device, &table)) {
- return false;
- }
- if (force_writable) {
- table.set_readonly(false);
- }
- std::string name = GetPartitionName(partition);
- if (!dm.CreateDevice(name, table, path, timeout_ms)) {
- return false;
- }
- LINFO << "Created logical partition " << name << " on device " << *path;
- return true;
-}
-
bool CreateLogicalPartitions(const std::string& block_device) {
uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
auto metadata = ReadMetadata(block_device.c_str(), slot);
@@ -145,13 +125,20 @@
}
bool CreateLogicalPartitions(const LpMetadata& metadata, const std::string& super_device) {
+ CreateLogicalPartitionParams params = {
+ .block_device = super_device,
+ .metadata = &metadata,
+ };
for (const auto& partition : metadata.partitions) {
if (!partition.num_extents) {
LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition);
continue;
}
- std::string path;
- if (!CreateLogicalPartition(metadata, partition, false, {}, super_device, &path)) {
+
+ params.partition = &partition;
+
+ std::string ignore_path;
+ if (!CreateLogicalPartition(params, &ignore_path)) {
LERROR << "Could not create logical partition: " << GetPartitionName(partition);
return false;
}
@@ -159,29 +146,58 @@
return true;
}
-bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
- const std::string& partition_name, bool force_writable,
- const std::chrono::milliseconds& timeout_ms, std::string* path) {
- for (const auto& partition : metadata.partitions) {
- if (GetPartitionName(partition) == partition_name) {
- return CreateLogicalPartition(metadata, partition, force_writable, timeout_ms,
- block_device, path);
+bool CreateLogicalPartition(const CreateLogicalPartitionParams& params, std::string* path) {
+ const LpMetadata* metadata = params.metadata;
+
+ // Read metadata if needed.
+ std::unique_ptr<LpMetadata> local_metadata;
+ if (!metadata) {
+ if (!params.metadata_slot) {
+ LOG(ERROR) << "Either metadata or a metadata slot must be specified.";
+ return false;
+ }
+ auto slot = *params.metadata_slot;
+ if (local_metadata = ReadMetadata(params.block_device, slot); !local_metadata) {
+ LOG(ERROR) << "Could not read partition table for: " << params.block_device;
+ return false;
+ }
+ metadata = local_metadata.get();
+ }
+
+ // Find the partition by name if needed.
+ const LpMetadataPartition* partition = params.partition;
+ if (!partition) {
+ for (const auto& iter : metadata->partitions) {
+ if (GetPartitionName(iter) == params.partition_name) {
+ partition = &iter;
+ break;
+ }
+ }
+ if (!partition) {
+ LERROR << "Could not find any partition with name: " << params.partition_name;
+ return false;
}
}
- LERROR << "Could not find any partition with name: " << partition_name;
- return false;
-}
-bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
- const std::string& partition_name, bool force_writable,
- const std::chrono::milliseconds& timeout_ms, std::string* path) {
- auto metadata = ReadMetadata(block_device.c_str(), metadata_slot);
- if (!metadata) {
- LOG(ERROR) << "Could not read partition table.";
- return true;
+ DmTable table;
+ if (!CreateDmTable(*metadata, *partition, params.block_device, &table)) {
+ return false;
}
- return CreateLogicalPartition(block_device, *metadata.get(), partition_name, force_writable,
- timeout_ms, path);
+ if (params.force_writable) {
+ table.set_readonly(false);
+ }
+
+ std::string device_name = params.device_name;
+ if (device_name.empty()) {
+ device_name = GetPartitionName(*partition);
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.CreateDevice(device_name, table, path, params.timeout_ms)) {
+ return false;
+ }
+ LINFO << "Created logical partition " << device_name << " on device " << *path;
+ return true;
}
bool UnmapDevice(const std::string& name) {
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 4ee7db9..d7ea81d 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -133,7 +133,7 @@
return ret | !rmdir(test_directory.c_str());
}
-// At less than 1% free space return value of false,
+// At less than 1% or 8MB of free space return value of false,
// means we will try to wrap with overlayfs.
bool fs_mgr_filesystem_has_space(const std::string& mount_point) {
// If we have access issues to find out space remaining, return true
@@ -145,9 +145,11 @@
return true;
}
- static constexpr int kPercentThreshold = 1; // 1%
+ static constexpr int kPercentThreshold = 1; // 1%
+ static constexpr unsigned long kSizeThreshold = 8 * 1024 * 1024; // 8MB
- return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100));
+ return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100)) &&
+ (vst.f_bfree * vst.f_bsize) >= kSizeThreshold;
}
const auto kPhysicalDevice = "/dev/block/by-name/"s;
@@ -959,9 +961,16 @@
}
if (changed || partition_create) {
- if (!CreateLogicalPartition(super_device, slot_number, partition_name, true, 10s,
- scratch_device))
+ CreateLogicalPartitionParams params = {
+ .block_device = super_device,
+ .metadata_slot = slot_number,
+ .partition_name = partition_name,
+ .force_writable = true,
+ .timeout_ms = 10s,
+ };
+ if (!CreateLogicalPartition(params, scratch_device)) {
return false;
+ }
if (change) *change = true;
}
@@ -1182,11 +1191,15 @@
if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
auto scratch_device = fs_mgr_overlayfs_scratch_device();
if (scratch_device.empty()) {
- auto slot_number = fs_mgr_overlayfs_slot_number();
- auto super_device = fs_mgr_overlayfs_super_device(slot_number);
- const auto partition_name = android::base::Basename(kScratchMountPoint);
- CreateLogicalPartition(super_device, slot_number, partition_name, true, 10s,
- &scratch_device);
+ auto metadata_slot = fs_mgr_overlayfs_slot_number();
+ CreateLogicalPartitionParams params = {
+ .block_device = fs_mgr_overlayfs_super_device(metadata_slot),
+ .metadata_slot = metadata_slot,
+ .partition_name = android::base::Basename(kScratchMountPoint),
+ .force_writable = true,
+ .timeout_ms = 10s,
+ };
+ CreateLogicalPartition(params, &scratch_device);
}
mount_scratch = fs_mgr_overlayfs_mount_scratch(scratch_device,
fs_mgr_overlayfs_scratch_mount_type());
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 149bee3..93bba68 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -82,13 +82,7 @@
void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
const char* file, unsigned int line, const char* message) {
- static const char log_characters[] = "VD\0WEFF";
- if (severity < sizeof(log_characters)) {
- auto severity_char = log_characters[severity];
- if (severity_char) fprintf(stderr, "%c ", severity_char);
- }
fprintf(stderr, "%s\n", message);
-
static auto logd = android::base::LogdLogger();
logd(id, severity, tag, file, line, message);
}
@@ -107,11 +101,9 @@
} // namespace
-int main(int argc, char* argv[]) {
- android::base::InitLogging(argv, MyLogger);
-
+static int do_remount(int argc, char* argv[]) {
enum {
- SUCCESS,
+ SUCCESS = 0,
NOT_USERDEBUG,
BADARG,
NOT_ROOT,
@@ -165,7 +157,7 @@
// Make sure we are root.
if (::getuid() != 0) {
- LOG(ERROR) << "must be run as root";
+ LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
return NOT_ROOT;
}
@@ -390,3 +382,10 @@
return retval;
}
+
+int main(int argc, char* argv[]) {
+ android::base::InitLogging(argv, MyLogger);
+ int result = do_remount(argc, argv);
+ printf("remount %s\n", result ? "failed" : "succeeded");
+ return result;
+}
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index a1dc2dc..6eb541c 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -29,6 +29,7 @@
#include <chrono>
#include <memory>
+#include <optional>
#include <string>
#include <vector>
@@ -49,22 +50,32 @@
// method for ReadMetadata and CreateLogicalPartitions.
bool CreateLogicalPartitions(const std::string& block_device);
-// Create a block device for a single logical partition, given metadata and
-// the partition name. On success, a path to the partition's block device is
-// returned. If |force_writable| is true, the "readonly" flag will be ignored
-// so the partition can be flashed.
-//
-// If |timeout_ms| is non-zero, then CreateLogicalPartition will block for the
-// given amount of time until the path returned in |path| is available.
-bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
- const std::string& partition_name, bool force_writable,
- const std::chrono::milliseconds& timeout_ms, std::string* path);
+struct CreateLogicalPartitionParams {
+ // Block device of the super partition.
+ std::string block_device;
-// Same as above, but with a given metadata object. Care should be taken that
-// the metadata represents a valid partition layout.
-bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
- const std::string& partition_name, bool force_writable,
- const std::chrono::milliseconds& timeout_ms, std::string* path);
+ // If |metadata| is null, the slot will be read using |metadata_slot|.
+ const LpMetadata* metadata = nullptr;
+ std::optional<uint32_t> metadata_slot;
+
+ // If |partition| is not set, it will be found via |partition_name|.
+ const LpMetadataPartition* partition = nullptr;
+ std::string partition_name;
+
+ // Force the device to be read-write even if it was specified as readonly
+ // in the metadata.
+ bool force_writable = false;
+
+ // If |timeout_ms| is non-zero, then CreateLogicalPartition will block for
+ // the given amount of time until the path returned in |path| is available.
+ std::chrono::milliseconds timeout_ms = {};
+
+ // If this is non-empty, it will override the device mapper name (by
+ // default the partition name will be used).
+ std::string device_name;
+};
+
+bool CreateLogicalPartition(const CreateLogicalPartitionParams& params, std::string* path);
// Destroy the block device for a logical partition, by name. If |timeout_ms|
// is non-zero, then this will block until the device path has been unlinked.
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 777743c..77c9c28 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -140,7 +140,7 @@
}
if (opener) {
for (size_t i = 0; i < builder->block_devices_.size(); i++) {
- std::string partition_name = GetBlockDevicePartitionName(builder->block_devices_[i]);
+ std::string partition_name = builder->GetBlockDevicePartitionName(i);
BlockDeviceInfo device_info;
if (opener->GetInfo(partition_name, &device_info)) {
builder->UpdateBlockDeviceInfo(i, device_info);
@@ -164,13 +164,20 @@
// name and system properties.
// See comments for UpdateMetadataForOtherSuper.
auto super_device = GetMetadataSuperBlockDevice(*metadata.get());
- if (GetBlockDevicePartitionName(*super_device) != "super" &&
+ if (android::fs_mgr::GetBlockDevicePartitionName(*super_device) != "super" &&
IsRetrofitDynamicPartitionsDevice()) {
if (!UpdateMetadataForOtherSuper(metadata.get(), source_slot_number, target_slot_number)) {
return nullptr;
}
}
+ if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false)) {
+ if (!UpdateMetadataForInPlaceSnapshot(metadata.get(), source_slot_number,
+ target_slot_number)) {
+ return nullptr;
+ }
+ }
+
return New(*metadata.get(), &opener);
}
@@ -192,7 +199,8 @@
// Translate block devices.
auto source_block_devices = std::move(metadata->block_devices);
for (const auto& source_block_device : source_block_devices) {
- std::string partition_name = GetBlockDevicePartitionName(source_block_device);
+ std::string partition_name =
+ android::fs_mgr::GetBlockDevicePartitionName(source_block_device);
std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
if (slot_suffix.empty() || slot_suffix != source_slot_suffix) {
// This should never happen. It means that the source metadata
@@ -375,7 +383,7 @@
block_devices_.emplace_back(out);
}
}
- if (GetBlockDevicePartitionName(block_devices_[0]) != super_partition) {
+ if (GetBlockDevicePartitionName(0) != super_partition) {
LERROR << "No super partition was specified.";
return false;
}
@@ -458,7 +466,7 @@
return nullptr;
}
-PartitionGroup* MetadataBuilder::FindGroup(const std::string& group_name) {
+PartitionGroup* MetadataBuilder::FindGroup(std::string_view group_name) {
for (const auto& group : groups_) {
if (group->name() == group_name) {
return group.get();
@@ -582,8 +590,7 @@
CHECK_NE(sectors_per_block, 0);
CHECK(sectors_needed % sectors_per_block == 0);
- if (IsABDevice() && !IsRetrofitMetadata() &&
- GetPartitionSlotSuffix(partition->name()) == "_b") {
+ if (IsABDevice() && ShouldHalveSuper() && GetPartitionSlotSuffix(partition->name()) == "_b") {
// Allocate "a" partitions top-down and "b" partitions bottom-up, to
// minimize fragmentation during OTA.
free_regions = PrioritizeSecondHalfOfSuper(free_regions);
@@ -849,7 +856,7 @@
bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name,
uint32_t* index) const {
for (size_t i = 0; i < block_devices_.size(); i++) {
- if (GetBlockDevicePartitionName(block_devices_[i]) == partition_name) {
+ if (GetBlockDevicePartitionName(i) == partition_name) {
*index = i;
return true;
}
@@ -974,7 +981,8 @@
// Note: we don't compare alignment, since it's a performance thing and
// won't affect whether old extents continue to work.
return first.first_logical_sector == second.first_logical_sector && first.size == second.size &&
- GetBlockDevicePartitionName(first) == GetBlockDevicePartitionName(second);
+ android::fs_mgr::GetBlockDevicePartitionName(first) ==
+ android::fs_mgr::GetBlockDevicePartitionName(second);
}
bool MetadataBuilder::ImportPartitions(const LpMetadata& metadata,
@@ -1056,8 +1064,9 @@
false);
}
-bool MetadataBuilder::IsRetrofitMetadata() const {
- return GetBlockDevicePartitionName(block_devices_[0]) != LP_METADATA_DEFAULT_PARTITION_NAME;
+bool MetadataBuilder::ShouldHalveSuper() const {
+ return GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
+ !IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false);
}
bool MetadataBuilder::AddLinearExtent(Partition* partition, const std::string& block_device,
@@ -1083,7 +1092,7 @@
return partitions;
}
-bool MetadataBuilder::ChangePartitionGroup(Partition* partition, const std::string& group_name) {
+bool MetadataBuilder::ChangePartitionGroup(Partition* partition, std::string_view group_name) {
if (!FindGroup(group_name)) {
LERROR << "Partition cannot change to unknown group: " << group_name;
return false;
@@ -1121,5 +1130,11 @@
return true;
}
+std::string MetadataBuilder::GetBlockDevicePartitionName(uint64_t index) const {
+ return index < block_devices_.size()
+ ? android::fs_mgr::GetBlockDevicePartitionName(block_devices_[index])
+ : "";
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 6d27873..b3c13e6 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -35,7 +35,7 @@
IPropertyFetcher::OverrideForTesting(std::make_unique<NiceMock<MockPropertyFetcher>>());
}
-MockPropertyFetcher* GetMockedInstance() {
+MockPropertyFetcher* GetMockedPropertyFetcher() {
return static_cast<MockPropertyFetcher*>(IPropertyFetcher::GetInstance());
}
@@ -789,7 +789,7 @@
// A and B slots should be allocated from separate halves of the partition,
// to mitigate allocating too many extents. (b/120433288)
- ON_CALL(*GetMockedInstance(), GetProperty("ro.boot.slot_suffix", _))
+ ON_CALL(*GetMockedPropertyFetcher(), GetProperty("ro.boot.slot_suffix", _))
.WillByDefault(Return("_a"));
auto builder = MetadataBuilder::New(device_info, 65536, 2);
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 3b229bd..c3f6e91 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -128,7 +128,7 @@
private:
void ShrinkTo(uint64_t aligned_size);
- void set_group_name(const std::string& group_name) { group_name_ = group_name; }
+ void set_group_name(std::string_view group_name) { group_name_ = group_name; }
std::string name_;
std::string group_name_;
@@ -227,7 +227,7 @@
Partition* FindPartition(const std::string& name);
// Find a group by name. If no group is found, nullptr is returned.
- PartitionGroup* FindGroup(const std::string& name);
+ PartitionGroup* FindGroup(std::string_view name);
// Add a predetermined extent to a partition.
bool AddLinearExtent(Partition* partition, const std::string& block_device,
@@ -252,7 +252,7 @@
// the metadata is exported, to avoid errors during potential group and
// size shuffling operations. This will return false if the new group does
// not exist.
- bool ChangePartitionGroup(Partition* partition, const std::string& group_name);
+ bool ChangePartitionGroup(Partition* partition, std::string_view group_name);
// Changes the size of a partition group. Size constraints will not be
// checked until metadata is exported, to avoid errors during group
@@ -287,6 +287,9 @@
// Return true if a block device is found, else false.
bool HasBlockDevice(const std::string& partition_name) const;
+ // Return the name of the block device at |index|.
+ std::string GetBlockDevicePartitionName(uint64_t index) const;
+
private:
MetadataBuilder();
MetadataBuilder(const MetadataBuilder&) = delete;
@@ -314,8 +317,8 @@
// Return true if the device is retrofitting dynamic partitions.
static bool IsRetrofitDynamicPartitionsDevice();
- // Return true if "this" metadata represents a metadata on a retrofit device.
- bool IsRetrofitMetadata() const;
+ // Return true if _b partitions should be prioritized at the second half of the device.
+ bool ShouldHalveSuper() const;
bool ValidatePartitionGroups() const;
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 2990863..3dace25 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -668,7 +668,7 @@
}
TEST(liblp, UpdateRetrofit) {
- ON_CALL(*GetMockedInstance(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
+ ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
.WillByDefault(Return(true));
unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
@@ -700,7 +700,7 @@
}
TEST(liblp, UpdateNonRetrofit) {
- ON_CALL(*GetMockedInstance(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
+ ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
.WillByDefault(Return(false));
unique_fd fd = CreateFlashedDisk();
diff --git a/fs_mgr/liblp/mock_property_fetcher.h b/fs_mgr/liblp/mock_property_fetcher.h
index eb91de2..0c28710 100644
--- a/fs_mgr/liblp/mock_property_fetcher.h
+++ b/fs_mgr/liblp/mock_property_fetcher.h
@@ -44,4 +44,4 @@
} // namespace fs_mgr
} // namespace android
-android::fs_mgr::MockPropertyFetcher* GetMockedInstance();
+android::fs_mgr::MockPropertyFetcher* GetMockedPropertyFetcher();
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 338b525..17f05aa 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -24,6 +24,10 @@
#include <sys/ioctl.h>
#endif
+#include <map>
+#include <string>
+#include <vector>
+
#include <android-base/file.h>
#include <ext4_utils/ext4_utils.h>
#include <openssl/sha.h>
@@ -182,6 +186,14 @@
return true;
}
+bool UpdatePartitionName(LpMetadataPartition* partition, const std::string& name) {
+ if (name.size() > sizeof(partition->name)) {
+ return false;
+ }
+ strncpy(partition->name, name.c_str(), sizeof(partition->name));
+ return true;
+}
+
bool SetBlockReadonly(int fd, bool readonly) {
#if defined(__linux__)
int val = readonly;
@@ -207,5 +219,70 @@
return base::unique_fd(open(path, flags));
}
+bool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot_number,
+ uint32_t target_slot_number) {
+ std::string source_slot_suffix = SlotSuffixForSlotNumber(source_slot_number);
+ std::string target_slot_suffix = SlotSuffixForSlotNumber(target_slot_number);
+
+ // There can be leftover groups with target suffix on retrofit devices.
+ // They are useless now, so delete.
+ std::vector<LpMetadataPartitionGroup*> new_group_ptrs;
+ for (auto& group : metadata->groups) {
+ std::string group_name = GetPartitionGroupName(group);
+ std::string slot_suffix = GetPartitionSlotSuffix(group_name);
+ // Don't add groups with target slot suffix.
+ if (slot_suffix == target_slot_suffix) continue;
+ // Replace source slot suffix with target slot suffix.
+ if (slot_suffix == source_slot_suffix) {
+ std::string new_name = group_name.substr(0, group_name.size() - slot_suffix.size()) +
+ target_slot_suffix;
+ if (!UpdatePartitionGroupName(&group, new_name)) {
+ LERROR << "Group name too long: " << new_name;
+ return false;
+ }
+ }
+ new_group_ptrs.push_back(&group);
+ }
+
+ std::vector<LpMetadataPartition*> new_partition_ptrs;
+ for (auto& partition : metadata->partitions) {
+ std::string partition_name = GetPartitionName(partition);
+ std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
+ // Don't add partitions with target slot suffix.
+ if (slot_suffix == target_slot_suffix) continue;
+ // Replace source slot suffix with target slot suffix.
+ if (slot_suffix == source_slot_suffix) {
+ std::string new_name =
+ partition_name.substr(0, partition_name.size() - slot_suffix.size()) +
+ target_slot_suffix;
+ if (!UpdatePartitionName(&partition, new_name)) {
+ LERROR << "Partition name too long: " << new_name;
+ return false;
+ }
+ }
+ // Update group index.
+ auto it = std::find(new_group_ptrs.begin(), new_group_ptrs.end(),
+ &metadata->groups[partition.group_index]);
+ if (it == new_group_ptrs.end()) {
+ LWARN << "Removing partition " << partition_name << " from group "
+ << GetPartitionGroupName(metadata->groups[partition.group_index])
+ << "; this partition should not belong to this group!";
+ continue; // not adding to new_partition_ptrs
+ }
+ partition.group_index = std::distance(new_group_ptrs.begin(), it);
+ new_partition_ptrs.push_back(&partition);
+ }
+
+ std::vector<LpMetadataPartition> new_partitions;
+ for (auto* p : new_partition_ptrs) new_partitions.emplace_back(std::move(*p));
+ metadata->partitions = std::move(new_partitions);
+
+ std::vector<LpMetadataPartitionGroup> new_groups;
+ for (auto* g : new_group_ptrs) new_groups.emplace_back(std::move(*g));
+ metadata->groups = std::move(new_groups);
+
+ return true;
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 96f1717..25ab66b 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -89,12 +89,17 @@
// Update names from C++ strings.
bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name);
bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name);
+bool UpdatePartitionName(LpMetadataPartition* partition, const std::string& name);
// Call BLKROSET ioctl on fd so that fd is readonly / read-writable.
bool SetBlockReadonly(int fd, bool readonly);
::android::base::unique_fd GetControlFileOrOpen(const char* path, int flags);
+// For Virtual A/B updates, modify |metadata| so that it can be written to |target_slot_number|.
+bool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot_number,
+ uint32_t target_slot_number);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index 15f7fff..cac3989 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -15,6 +15,7 @@
*/
#include <gtest/gtest.h>
+#include <liblp/builder.h>
#include <liblp/liblp.h>
#include "utility.h"
@@ -75,3 +76,81 @@
EXPECT_EQ(GetPartitionSlotSuffix("system_a"), "_a");
EXPECT_EQ(GetPartitionSlotSuffix("system_b"), "_b");
}
+
+namespace android {
+namespace fs_mgr {
+// Equality comparison for testing. In reality, equality of device_index doesn't
+// necessary mean equality of the block device.
+bool operator==(const LinearExtent& l, const LinearExtent& r) {
+ return l.device_index() == r.device_index() && l.physical_sector() == r.physical_sector() &&
+ l.end_sector() == r.end_sector();
+}
+} // namespace fs_mgr
+} // namespace android
+
+static std::vector<LinearExtent> GetPartitionExtents(Partition* p) {
+ std::vector<LinearExtent> extents;
+ for (auto&& extent : p->extents()) {
+ auto linear_extent = extent->AsLinearExtent();
+ if (!linear_extent) return {};
+ extents.push_back(*linear_extent);
+ }
+ return extents;
+}
+
+TEST(liblp, UpdateMetadataForInPlaceSnapshot) {
+ using std::unique_ptr;
+
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("group_a", 256 * 1024));
+ Partition* system_a = builder->AddPartition("system_a", "group_a", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system_a, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(system_a, 40 * 1024));
+ Partition* vendor_a = builder->AddPartition("vendor_a", "group_a", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(vendor_a, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(vendor_a, 20 * 1024));
+
+ ASSERT_TRUE(builder->AddGroup("group_b", 258 * 1024));
+ Partition* system_b = builder->AddPartition("system_b", "group_b", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system_b, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(system_b, 36 * 1024));
+ Partition* vendor_b = builder->AddPartition("vendor_b", "group_b", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(vendor_b, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(vendor_b, 32 * 1024));
+
+ auto system_a_extents = GetPartitionExtents(system_a);
+ ASSERT_FALSE(system_a_extents.empty());
+
+ auto vendor_a_extents = GetPartitionExtents(vendor_a);
+ ASSERT_FALSE(vendor_a_extents.empty());
+
+ auto metadata = builder->Export();
+ ASSERT_NE(nullptr, metadata);
+
+ ASSERT_TRUE(UpdateMetadataForInPlaceSnapshot(metadata.get(), 0, 1));
+
+ auto new_builder = MetadataBuilder::New(*metadata);
+ ASSERT_NE(nullptr, new_builder);
+
+ EXPECT_EQ(nullptr, new_builder->FindGroup("group_a"));
+ EXPECT_EQ(nullptr, new_builder->FindPartition("system_a"));
+ EXPECT_EQ(nullptr, new_builder->FindPartition("vendor_a"));
+
+ auto group_b = new_builder->FindGroup("group_b");
+ ASSERT_NE(nullptr, group_b);
+ ASSERT_EQ(256 * 1024, group_b->maximum_size());
+
+ auto new_system_b = new_builder->FindPartition("system_b");
+ ASSERT_NE(nullptr, new_system_b);
+ EXPECT_EQ(40 * 1024, new_system_b->size());
+ auto new_system_b_extents = GetPartitionExtents(new_system_b);
+ EXPECT_EQ(system_a_extents, new_system_b_extents);
+
+ auto new_vendor_b = new_builder->FindPartition("vendor_b");
+ ASSERT_NE(nullptr, new_vendor_b);
+ EXPECT_EQ(20 * 1024, new_vendor_b->size());
+ auto new_vendor_b_extents = GetPartitionExtents(new_vendor_b);
+ EXPECT_EQ(vendor_a_extents, new_vendor_b_extents);
+}
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 52aad12..128a4f9 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -32,7 +32,13 @@
whole_static_libs: [
"libext2_uuid",
"libext4_utils",
- "libfiemap",
+ "libfstab",
+ ],
+ header_libs: [
+ "libfiemap_headers",
+ ],
+ export_header_lib_headers: [
+ "libfiemap_headers",
],
export_include_dirs: ["include"],
}
@@ -48,7 +54,7 @@
name: "libsnapshot",
defaults: ["libsnapshot_defaults"],
srcs: [":libsnapshot_sources"],
- static_libs: [
+ whole_static_libs: [
"libfiemap_binder",
],
}
@@ -58,6 +64,9 @@
defaults: ["libsnapshot_defaults"],
srcs: [":libsnapshot_sources"],
recovery_available: true,
+ whole_static_libs: [
+ "libfiemap_passthrough",
+ ],
}
cc_test {
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index f7608dc..1630ee5 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -71,11 +71,7 @@
virtual ~IDeviceInfo() {}
virtual std::string GetGsidDir() const = 0;
virtual std::string GetMetadataDir() const = 0;
-
- // Return true if the device is currently running off snapshot devices,
- // indicating that we have booted after applying (but not merging) an
- // OTA.
- virtual bool IsRunningSnapshot() const = 0;
+ virtual std::string GetSlotSuffix() const = 0;
};
~SnapshotManager();
@@ -93,6 +89,11 @@
// state != Initiated or None.
bool CancelUpdate();
+ // Mark snapshot writes as having completed. After this, new snapshots cannot
+ // be created, and the device must either cancel the OTA (either before
+ // rebooting or after rolling back), or merge the OTA.
+ bool FinishedSnapshotWrites();
+
// Initiate a merge on all snapshot devices. This should only be used after an
// update has been marked successful after booting.
bool InitiateMerge();
@@ -218,9 +219,12 @@
bool WriteUpdateState(LockedFile* file, UpdateState state);
std::string GetStateFilePath() const;
+ enum class SnapshotState : int { Created, Merging, MergeCompleted };
+ static std::string to_string(SnapshotState state);
+
// This state is persisted per-snapshot in /metadata/ota/snapshots/.
struct SnapshotStatus {
- std::string state;
+ SnapshotState state;
uint64_t device_size;
uint64_t snapshot_size;
// These are non-zero when merging.
@@ -261,6 +265,9 @@
bool ReadSnapshotStatus(LockedFile* lock, const std::string& name, SnapshotStatus* status);
std::string GetSnapshotStatusFilePath(const std::string& name);
+ std::string GetSnapshotBootIndicatorPath();
+ void RemoveSnapshotBootIndicator();
+
// Return the name of the device holding the "snapshot" or "snapshot-merge"
// target. This may not be the final device presented via MapSnapshot(), if
// for example there is a linear segment.
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 63a01f3..fef5c06 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -27,6 +27,7 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <ext4_utils/ext4_utils.h>
+#include <fstab/fstab.h>
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
@@ -48,18 +49,15 @@
// Unit is sectors, this is a 4K chunk.
static constexpr uint32_t kSnapshotChunkSize = 8;
+static constexpr char kSnapshotBootIndicatorFile[] = "snapshot-boot";
+
class DeviceInfo final : public SnapshotManager::IDeviceInfo {
public:
std::string GetGsidDir() const override { return "ota"s; }
std::string GetMetadataDir() const override { return "/metadata/ota"s; }
- bool IsRunningSnapshot() const override;
+ std::string GetSlotSuffix() const override { return fs_mgr_get_slot_suffix(); }
};
-bool DeviceInfo::IsRunningSnapshot() const {
- // :TODO: implement this check.
- return true;
-}
-
// Note: IIMageManager is an incomplete type in the header, so the default
// destructor doesn't work.
SnapshotManager::~SnapshotManager() {}
@@ -115,6 +113,27 @@
return true;
}
+bool SnapshotManager::FinishedSnapshotWrites() {
+ auto lock = LockExclusive();
+ if (!lock) return false;
+
+ if (ReadUpdateState(lock.get()) != UpdateState::Initiated) {
+ LOG(ERROR) << "Can only transition to the Unverified state from the Initiated state.";
+ return false;
+ }
+
+ // This file acts as both a quick indicator for init (it can use access(2)
+ // to decide how to do first-stage mounts), and it stores the old slot, so
+ // we can tell whether or not we performed a rollback.
+ auto contents = device_->GetSlotSuffix();
+ auto boot_file = GetSnapshotBootIndicatorPath();
+ if (!android::base::WriteStringToFile(contents, boot_file)) {
+ PLOG(ERROR) << "write failed: " << boot_file;
+ return false;
+ }
+ return WriteUpdateState(lock.get(), UpdateState::Unverified);
+}
+
bool SnapshotManager::CreateSnapshot(LockedFile* lock, const std::string& name,
uint64_t device_size, uint64_t snapshot_size,
uint64_t cow_size) {
@@ -146,7 +165,7 @@
// actual backing image. This is harmless, since it'll get removed when
// CancelUpdate is called.
SnapshotStatus status = {
- .state = "created",
+ .state = SnapshotState::Created,
.device_size = device_size,
.snapshot_size = snapshot_size,
};
@@ -171,7 +190,7 @@
if (!ReadSnapshotStatus(lock, name, &status)) {
return false;
}
- if (status.state == "merge-completed") {
+ if (status.state == SnapshotState::MergeCompleted) {
LOG(ERROR) << "Should not create a snapshot device for " << name
<< " after merging has completed.";
return false;
@@ -294,7 +313,8 @@
// There may be an extra device, since the kernel doesn't let us have a
// snapshot and linear target in the same table.
auto dm_name = GetSnapshotDeviceName(name, status);
- if (name != dm_name && !dm.DeleteDevice(dm_name)) {
+ if (name != dm_name && dm.GetState(dm_name) != DmDeviceState::INVALID &&
+ !dm.DeleteDevice(dm_name)) {
LOG(ERROR) << "Could not delete inner snapshot device: " << dm_name;
return false;
}
@@ -339,8 +359,16 @@
LOG(ERROR) << "Cannot begin a merge if an update has not been verified";
return false;
}
- if (!device_->IsRunningSnapshot()) {
- LOG(ERROR) << "Cannot begin a merge if the device is not booted off a snapshot";
+
+ std::string old_slot;
+ auto boot_file = GetSnapshotBootIndicatorPath();
+ if (!android::base::ReadFileToString(boot_file, &old_slot)) {
+ LOG(ERROR) << "Could not determine the previous slot; aborting merge";
+ return false;
+ }
+ auto new_slot = device_->GetSlotSuffix();
+ if (new_slot == old_slot) {
+ LOG(ERROR) << "Device cannot merge while booting off old slot " << old_slot;
return false;
}
@@ -395,8 +423,8 @@
if (!ReadSnapshotStatus(lock, name, &status)) {
return false;
}
- if (status.state != "created") {
- LOG(WARNING) << "Snapshot " << name << " has unexpected state: " << status.state;
+ if (status.state != SnapshotState::Created) {
+ LOG(WARNING) << "Snapshot " << name << " has unexpected state: " << to_string(status.state);
}
// After this, we return true because we technically did switch to a merge
@@ -406,7 +434,7 @@
return false;
}
- status.state = "merging";
+ status.state = SnapshotState::Merging;
DmTargetSnapshot::Status dm_status;
if (!QuerySnapshotStatus(dm_name, nullptr, &dm_status)) {
@@ -630,7 +658,7 @@
// rebooted after this check, the device will still be a snapshot-merge
// target. If the have rebooted, the device will now be a linear target,
// and we can try cleanup again.
- if (snapshot_status.state == "merge-complete" && !IsSnapshotDevice(dm_name)) {
+ if (snapshot_status.state == SnapshotState::MergeCompleted && !IsSnapshotDevice(dm_name)) {
// NB: It's okay if this fails now, we gave cleanup our best effort.
OnSnapshotMergeComplete(lock, name, snapshot_status);
return UpdateState::MergeCompleted;
@@ -651,7 +679,7 @@
// These two values are equal when merging is complete.
if (status.sectors_allocated != status.metadata_sectors) {
- if (snapshot_status.state == "merge-complete") {
+ if (snapshot_status.state == SnapshotState::MergeCompleted) {
LOG(ERROR) << "Snapshot " << name << " is merging after being marked merge-complete.";
return UpdateState::MergeFailed;
}
@@ -666,7 +694,7 @@
// This makes it simpler to reason about the next reboot: no matter what
// part of cleanup failed, first-stage init won't try to create another
// snapshot device for this partition.
- snapshot_status.state = "merge-complete";
+ snapshot_status.state = SnapshotState::MergeCompleted;
if (!WriteSnapshotStatus(lock, name, snapshot_status)) {
return UpdateState::MergeFailed;
}
@@ -676,7 +704,23 @@
return UpdateState::MergeCompleted;
}
+std::string SnapshotManager::GetSnapshotBootIndicatorPath() {
+ return metadata_dir_ + "/" + kSnapshotBootIndicatorFile;
+}
+
+void SnapshotManager::RemoveSnapshotBootIndicator() {
+ // It's okay if this fails - first-stage init performs a deeper check after
+ // reading the indicator file, so it's not a problem if it still exists
+ // after the update completes.
+ auto boot_file = GetSnapshotBootIndicatorPath();
+ if (unlink(boot_file.c_str()) == -1 && errno != ENOENT) {
+ PLOG(ERROR) << "unlink " << boot_file;
+ }
+}
+
void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
+ RemoveSnapshotBootIndicator();
+
if (!WriteUpdateState(lock, UpdateState::None)) {
// We'll try again next reboot, ad infinitum.
return;
@@ -1025,7 +1069,16 @@
return false;
}
- status->state = pieces[0];
+ if (pieces[0] == "created") {
+ status->state = SnapshotState::Created;
+ } else if (pieces[0] == "merging") {
+ status->state = SnapshotState::Merging;
+ } else if (pieces[0] == "merge-completed") {
+ status->state = SnapshotState::MergeCompleted;
+ } else {
+ LOG(ERROR) << "Unrecognized state " << pieces[0] << " for snapshot: " << name;
+ }
+
if (!android::base::ParseUint(pieces[1], &status->device_size)) {
LOG(ERROR) << "Invalid device size in status line for: " << path;
return false;
@@ -1045,6 +1098,20 @@
return true;
}
+std::string SnapshotManager::to_string(SnapshotState state) {
+ switch (state) {
+ case SnapshotState::Created:
+ return "created";
+ case SnapshotState::Merging:
+ return "merging";
+ case SnapshotState::MergeCompleted:
+ return "merge-completed";
+ default:
+ LOG(ERROR) << "Unknown snapshot state: " << (int)state;
+ return "unknown";
+ }
+}
+
bool SnapshotManager::WriteSnapshotStatus(LockedFile* lock, const std::string& name,
const SnapshotStatus& status) {
// The caller must take an exclusive lock to modify snapshots.
@@ -1059,7 +1126,7 @@
}
std::vector<std::string> pieces = {
- status.state,
+ to_string(status.state),
std::to_string(status.device_size),
std::to_string(status.snapshot_size),
std::to_string(status.sectors_allocated),
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 4903224..438ec2f 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -36,6 +36,7 @@
using android::base::unique_fd;
using android::dm::DeviceMapper;
using android::dm::DmDeviceState;
+using android::fiemap::IImageManager;
using namespace std::chrono_literals;
using namespace std::string_literals;
@@ -43,14 +44,16 @@
public:
std::string GetGsidDir() const override { return "ota/test"s; }
std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
- bool IsRunningSnapshot() const override { return is_running_snapshot_; }
+ std::string GetSlotSuffix() const override { return slot_suffix_; }
- void set_is_running_snapshot(bool value) { is_running_snapshot_ = value; }
+ void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
private:
- bool is_running_snapshot_;
+ std::string slot_suffix_ = "_a";
};
+// These are not reset between each test because it's expensive to reconnect
+// to gsid each time.
std::unique_ptr<SnapshotManager> sm;
TestDeviceInfo* test_device = nullptr;
@@ -60,16 +63,14 @@
protected:
void SetUp() override {
- test_device->set_is_running_snapshot(false);
-
- if (sm->GetUpdateState() != UpdateState::None) {
- CleanupTestArtifacts();
- }
- ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->EnsureImageManager());
-
image_manager_ = sm->image_manager();
- ASSERT_NE(image_manager_, nullptr);
+
+ test_device->set_slot_suffix("_a");
+
+ CleanupTestArtifacts();
+
+ ASSERT_TRUE(sm->BeginUpdate());
}
void TearDown() override {
@@ -81,20 +82,25 @@
void CleanupTestArtifacts() {
// Normally cancelling inside a merge is not allowed. Since these
// are tests, we don't care, destroy everything that might exist.
+ // Note we hardcode this list because of an annoying quirk: when
+ // completing a merge, the snapshot stops existing, so we can't
+ // get an accurate list to remove.
+ lock_ = nullptr;
+
std::vector<std::string> snapshots = {"test-snapshot"};
for (const auto& snapshot : snapshots) {
DeleteSnapshotDevice(snapshot);
- temp_images_.emplace_back(snapshot + "-cow");
+ DeleteBackingImage(image_manager_, snapshot + "-cow");
auto status_file = sm->GetSnapshotStatusFilePath(snapshot);
android::base::RemoveFileIfExists(status_file);
}
- // Remove all images.
- temp_images_.emplace_back("test-snapshot-cow");
- for (const auto& temp_image : temp_images_) {
- image_manager_->UnmapImageDevice(temp_image);
- image_manager_->DeleteBackingImage(temp_image);
+ // Remove all images. We hardcode the list of names since this can run
+ // before the test (when cleaning up from a crash).
+ std::vector<std::string> temp_partitions = {"base-device"};
+ for (const auto& partition : temp_partitions) {
+ DeleteBackingImage(image_manager_, partition);
}
if (sm->GetUpdateState() != UpdateState::None) {
@@ -112,23 +118,29 @@
if (!image_manager_->CreateBackingImage(name, size, false)) {
return false;
}
- temp_images_.emplace_back(name);
return image_manager_->MapImageDevice(name, 10s, path);
}
- bool DeleteSnapshotDevice(const std::string& snapshot) {
+ void DeleteSnapshotDevice(const std::string& snapshot) {
if (dm_.GetState(snapshot) != DmDeviceState::INVALID) {
- if (!dm_.DeleteDevice(snapshot)) return false;
+ ASSERT_TRUE(dm_.DeleteDevice(snapshot));
}
if (dm_.GetState(snapshot + "-inner") != DmDeviceState::INVALID) {
- if (!dm_.DeleteDevice(snapshot + "-inner")) return false;
+ ASSERT_TRUE(dm_.DeleteDevice(snapshot + "-inner"));
}
- return true;
+ }
+
+ void DeleteBackingImage(IImageManager* manager, const std::string& name) {
+ if (manager->IsImageMapped(name)) {
+ ASSERT_TRUE(manager->UnmapImageDevice(name));
+ }
+ if (manager->BackingImageExists(name)) {
+ ASSERT_TRUE(manager->DeleteBackingImage(name));
+ }
}
DeviceMapper& dm_;
std::unique_ptr<SnapshotManager::LockedFile> lock_;
- std::vector<std::string> temp_images_;
android::fiemap::IImageManager* image_manager_ = nullptr;
};
@@ -148,7 +160,7 @@
{
SnapshotManager::SnapshotStatus status;
ASSERT_TRUE(sm->ReadSnapshotStatus(lock_.get(), "test-snapshot", &status));
- ASSERT_EQ(status.state, "created");
+ ASSERT_EQ(status.state, SnapshotManager::SnapshotState::Created);
ASSERT_EQ(status.device_size, kDeviceSize);
ASSERT_EQ(status.snapshot_size, kDeviceSize);
}
@@ -189,15 +201,9 @@
}
TEST_F(SnapshotTest, NoMergeBeforeReboot) {
- ASSERT_TRUE(AcquireLock());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites());
- // Set the state to Unverified, as if we finished an update.
- ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Unverified));
-
- // Release the lock.
- lock_ = nullptr;
-
- // Merge should fail, since we didn't mark the device as rebooted.
+ // Merge should fail, since the slot hasn't changed.
ASSERT_FALSE(sm->InitiateMerge());
}
@@ -231,7 +237,7 @@
// Release the lock.
lock_ = nullptr;
- test_device->set_is_running_snapshot(true);
+ test_device->set_slot_suffix("_b");
ASSERT_TRUE(sm->InitiateMerge());
// The device should have been switched to a snapshot-merge target.
@@ -273,20 +279,19 @@
unique_fd fd(open(cow_path.c_str(), O_RDONLY | O_CLOEXEC));
ASSERT_GE(fd, 0);
- // Set the state to Unverified, as if we finished an update.
- ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Unverified));
-
// Release the lock.
lock_ = nullptr;
- test_device->set_is_running_snapshot(true);
+ ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+ test_device->set_slot_suffix("_b");
ASSERT_TRUE(sm->InitiateMerge());
// COW cannot be removed due to open fd, so expect a soft failure.
ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeNeedsReboot);
// Forcefully delete the snapshot device, so it looks like we just rebooted.
- ASSERT_TRUE(DeleteSnapshotDevice("test-snapshot"));
+ DeleteSnapshotDevice("test-snapshot");
// Map snapshot should fail now, because we're in a merge-complete state.
ASSERT_TRUE(AcquireLock());
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 642f2c1..ed6d0e3 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -709,8 +709,9 @@
EXPECT_EQ "${lval}" "${rval}" ${*}
return
fi
- EXPECT_EQ "${lval}" "${rval}" ||
+ if ! EXPECT_EQ "${lval}" "${rval}"; then
die "${@}"
+ fi
}
[ "USAGE: check_ne <lval> <rval> [--warning [message]]
@@ -724,8 +725,9 @@
EXPECT_NE "${lval}" "${rval}" ${*}
return
fi
- EXPECT_NE "${lval}" "${rval}" ||
+ if ! EXPECT_NE "${lval}" "${rval}"; then
die "${@}"
+ fi
}
[ "USAGE: skip_administrative_mounts [data] < /proc/mounts
@@ -847,6 +849,8 @@
# Do something.
+# Collect characteristics of the device and report.
+
D=`get_property ro.serialno`
[ -n "${D}" ] || D=`get_property ro.boot.serialno`
[ -z "${D}" -o -n "${ANDROID_SERIAL}" ] || ANDROID_SERIAL=${D}
@@ -869,35 +873,78 @@
[ -z "${ACTIVE_SLOT}" ] ||
echo "${BLUE}[ INFO ]${NORMAL} active slot is ${ACTIVE_SLOT}" >&2
+# Acquire list of system partitions
+
+PARTITIONS=`adb_su cat /vendor/etc/fstab* |
+ skip_administrative_mounts |
+ sed -n "s@^\([^ ${TAB}/][^ ${TAB}/]*\)[ ${TAB}].*[, ${TAB}]ro[, ${TAB}].*@\1@p" |
+ sort -u |
+ tr '\n' ' '`
+PARTITIONS="${PARTITIONS:-system vendor}"
+# KISS (we do not support sub-mounts for system partitions currently)
+MOUNTS="`for i in ${PARTITIONS}; do
+ echo /${i}
+ done |
+ tr '\n' ' '`"
+echo "${BLUE}[ INFO ]${NORMAL} System Partitions list: ${PARTITIONS}" >&2
+
# Report existing partition sizes
-adb_sh ls -l /dev/block/by-name/ </dev/null 2>/dev/null |
+adb_sh ls -l /dev/block/by-name/ /dev/block/mapper/ </dev/null 2>/dev/null |
sed -n 's@.* \([^ ]*\) -> /dev/block/\([^ ]*\)$@\1 \2@p' |
while read name device; do
- case ${name} in
- system_[ab] | system | vendor_[ab] | vendor | super | cache)
- case ${device} in
- sd*)
- device=${device%%[0-9]*}/${device}
- ;;
- esac
- size=`adb_su cat /sys/block/${device}/size 2>/dev/null </dev/null` &&
- size=`expr ${size} / 2` &&
- echo "${BLUE}[ INFO ]${NORMAL} partition ${name} device ${device} size ${size}K" >&2
+ [ super = ${name} -o cache = ${name} ] ||
+ (
+ for i in ${PARTITIONS}; do
+ [ ${i} = ${name} -o ${i} = ${name%_[ab]} ] && exit
+ done
+ exit 1
+ ) ||
+ continue
+
+ case ${device} in
+ sd*)
+ device=${device%%[0-9]*}/${device}
;;
esac
+ size=`adb_su cat /sys/block/${device}/size 2>/dev/null </dev/null` &&
+ size=`expr ${size} / 2` &&
+ echo "${BLUE}[ INFO ]${NORMAL} partition ${name} device ${device} size ${size}K" >&2
done
# If reboot too soon after fresh flash, could trip device update failure logic
wait_for_screen
# Can we test remount -R command?
+OVERLAYFS_BACKING="cache mnt/scratch"
overlayfs_supported=true
-if [ "orange" = "`get_property ro.boot.verifiedbootstate`" -a \
- "2" = "`get_property partition.system.verified`" ]; then
+if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
+ "2" != "`get_property partition.system.verified`" ]; then
restore() {
${overlayfs_supported} || return 0
inFastboot &&
fastboot reboot &&
- adb_wait ${ADB_WAIT}
+ adb_wait ${ADB_WAIT} ||
+ true
+ if inAdb; then
+ reboot=false
+ for d in ${OVERLAYFS_BACKING}; do
+ if adb_su ls -d /${d}/overlay </dev/null >/dev/null 2>/dev/null; then
+ adb_su rm -rf /${d}/overlay </dev/null
+ reboot=true
+ fi
+ done
+ if ${reboot}; then
+ adb_reboot &&
+ adb_wait ${ADB_WAIT}
+ fi
+ fi
+ }
+else
+ restore() {
+ ${overlayfs_supported} || return 0
+ inFastboot &&
+ fastboot reboot &&
+ adb_wait ${ADB_WAIT} ||
+ true
inAdb &&
adb_root &&
adb enable-verity >/dev/null 2>/dev/null &&
@@ -956,7 +1003,6 @@
# So lets do our best to surgically wipe the overlayfs state without
# having to go through enable-verity transition.
reboot=false
-OVERLAYFS_BACKING="cache mnt/scratch"
for d in ${OVERLAYFS_BACKING}; do
if adb_sh ls -d /${d}/overlay </dev/null >/dev/null 2>/dev/null; then
echo "${ORANGE}[ WARNING ]${NORMAL} /${d}/overlay is setup, surgically wiping" >&2
@@ -1085,7 +1131,7 @@
# Feed log with selinux denials as baseline before overlays
adb_unroot
-adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
+adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null
adb_root
D=`adb remount 2>&1`
@@ -1177,21 +1223,19 @@
# Check something.
-echo "${GREEN}[ RUN ]${NORMAL} push content to /system and /vendor" >&2
+echo "${GREEN}[ RUN ]${NORMAL} push content to ${MOUNTS}" >&2
A="Hello World! $(date)"
-echo "${A}" | adb_sh cat - ">/system/hello"
+for i in ${MOUNTS}; do
+ echo "${A}" | adb_sh cat - ">${i}/hello"
+ B="`adb_cat ${i}/hello`" ||
+ die "${i#/} hello"
+ check_eq "${A}" "${B}" ${i} before reboot
+done
echo "${A}" | adb_sh cat - ">/system/priv-app/hello"
-echo "${A}" | adb_sh cat - ">/vendor/hello"
-B="`adb_cat /system/hello`" ||
- die "system hello"
-check_eq "${A}" "${B}" /system before reboot
B="`adb_cat /system/priv-app/hello`" ||
die "system priv-app hello"
check_eq "${A}" "${B}" /system/priv-app before reboot
-B="`adb_cat /vendor/hello`" ||
- die "vendor hello"
-check_eq "${A}" "${B}" /vendor before reboot
SYSTEM_DEVT=`adb_sh stat --format=%D /system/hello </dev/null`
VENDOR_DEVT=`adb_sh stat --format=%D /vendor/hello </dev/null`
SYSTEM_INO=`adb_sh stat --format=%i /system/hello </dev/null`
@@ -1266,24 +1310,23 @@
echo "${GREEN}[ OK ]${NORMAL} /vendor content correct MAC after reboot" >&2
# Feed unprivileged log with selinux denials as a result of overlays
wait_for_screen
- adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
+ adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null
fi
-B="`adb_cat /system/hello`"
-check_eq "${A}" "${B}" /system after reboot
# If overlayfs has a nested security problem, this will fail.
B="`adb_ls /system/`" ||
- dir "adb ls /system"
+ die "adb ls /system"
[ X"${B}" != X"${B#*priv-app}" ] ||
- dir "adb ls /system/priv-app"
+ die "adb ls /system/priv-app"
B="`adb_cat /system/priv-app/hello`"
check_eq "${A}" "${B}" /system/priv-app after reboot
-echo "${GREEN}[ OK ]${NORMAL} /system content remains after reboot" >&2
# Only root can read vendor if sepolicy permissions are as expected.
adb_root ||
die "adb root"
-B="`adb_cat /vendor/hello`"
-check_eq "${A}" "${B}" vendor after reboot
-echo "${GREEN}[ OK ]${NORMAL} /vendor content remains after reboot" >&2
+for i in ${MOUNTS}; do
+ B="`adb_cat ${i}/hello`"
+ check_eq "${A}" "${B}" ${i#/} after reboot
+ echo "${GREEN}[ OK ]${NORMAL} ${i} content remains after reboot" >&2
+done
check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" system devt after reboot
check_eq "${VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/hello </dev/null`" vendor devt after reboot
@@ -1294,7 +1337,7 @@
check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" devt for su after reboot
# Feed log with selinux denials as a result of overlays
-adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
+adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null
# Check if the updated libc.so is persistent after reboot.
adb_root &&
@@ -1399,9 +1442,9 @@
B="`adb_cat /system/hello`"
check_eq "${A}" "${B}" system after flash vendor
B="`adb_ls /system/`" ||
- dir "adb ls /system"
+ die "adb ls /system"
[ X"${B}" != X"${B#*priv-app}" ] ||
- dir "adb ls /system/priv-app"
+ die "adb ls /system/priv-app"
B="`adb_cat /system/priv-app/hello`"
check_eq "${A}" "${B}" system/priv-app after flash vendor
adb_root ||
@@ -1454,6 +1497,9 @@
check_eq "cat: /system/priv-app/hello: No such file or directory" "${B}" after rm
B="`adb_cat /vendor/hello`"
check_eq "cat: /vendor/hello: No such file or directory" "${B}" after rm
+for i in ${MOUNTS}; do
+ adb_sh rm ${i}/hello </dev/null 2>/dev/null || true
+done
if ${is_bootloader_fastboot} && [ -n "${scratch_partition}" ]; then
@@ -1468,7 +1514,7 @@
}
dd if=/dev/zero of=${img} bs=4096 count=16 2>/dev/null &&
fastboot_wait ${FASTBOOT_WAIT} ||
- die "reboot into fastboot `usb_status`"
+ die "reboot into fastboot to flash scratch `usb_status`"
fastboot flash --force ${scratch_partition} ${img}
err=${?}
cleanup
diff --git a/init/Android.bp b/init/Android.bp
index 3233cc3..38d495f 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -269,11 +269,13 @@
shared_libs: [
"libcutils",
"libhidl-gen-utils",
- "libjsoncpp",
"liblog",
"libprocessgroup",
"libprotobuf-cpp-lite",
],
+ header_libs: [
+ "libjsoncpp_headers",
+ ],
srcs: [
"action.cpp",
"action_manager.cpp",
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
new file mode 100644
index 0000000..c592c37
--- /dev/null
+++ b/init/README.ueventd.md
@@ -0,0 +1,112 @@
+# Ueventd
+-------
+Ueventd manages `/dev`, sets permissions for `/sys`, and handles firmware uevents. It has default
+behavior described below, along with a scripting language that allows customizing this behavior,
+built on the same parser as init.
+
+Ueventd has one generic customization parameter, the size of rcvbuf_size for the ueventd socket. It
+is customized by the `uevent_socket_rcvbuf_size` parameter, which takes the format of
+
+ uevent_socket_rcvbuf_size <size>
+For example
+
+ uevent_socket_rcvbuf_size 16M
+Sets the uevent socket rcvbuf_size to 16 megabytes.
+
+## /dev
+----
+Ueventd listens to the kernel uevent sockets and creates/deletes nodes in `/dev` based on the
+incoming add/remove uevents. It defaults to using `0600` mode and `root` user/group. It always
+creates the nodes with the SELabel from the current loaded SEPolicy. It has three default behaviors
+for the node path:
+
+ 1. Block devices are created as `/dev/block/<basename uevent DEVPATH>`. There are symlinks created
+ to this node at `/dev/block/<type>/<parent device>/<basename uevent DEVPATH>`,
+ `/dev/block/<type>/<parent device>/by-name/<uevent PARTNAME>`, and `/dev/block/by-name/<uevent
+ PARTNAME>` if the device is a boot device.
+ 2. USB devices are created as `/dev/<uevent DEVNAME>` if `DEVNAME` was specified for the uevent,
+ otherwise as `/dev/bus/usb/<bus_id>/<device_id>` where `bus_id` is `uevent MINOR / 128 + 1` and
+ `device_id` is `uevent MINOR % 128 + 1`.
+ 3. All other devices are created as `/dev/<basename uevent DEVPATH>`
+
+The permissions can be modified using a ueventd.rc script and a line that beings with `/dev`. These
+lines take the format of
+
+ devname mode uid gid
+For example
+
+ /dev/null 0666 root root
+When `/dev/null` is created, its mode will be set to `0666`, its user to `root` and its group to
+`root`.
+
+The path can be modified using a ueventd.rc script and a `subsystem` section. There are three to set
+for a subsystem: the subsystem name, which device name to use, and which directory to place the
+device in. The section takes the below format of
+
+ subsystem <subsystem_name>
+ devname uevent_devname|uevent_devpath
+ [dirname <directory>]
+
+`subsystem_name` is used to match uevent `SUBSYSTEM` value
+
+`devname` takes one of two options
+ 1. `uevent_devname` specifies that the name of the node will be the uevent `DEVNAME`
+ 2. `uevent_devpath` specified that the name of the node will be basename uevent `DEVPATH`
+
+`dirname` is an optional parameter that specifies a directory within `/dev` where the node will be
+created.
+
+For example
+
+ subsystem sound
+ devname uevent_devpath
+ dirname /dev/snd
+Indicates that all uevents with `SUBSYSTEM=sound` will create nodes as `/dev/snd/<basename uevent
+DEVPATH>`.
+
+## /sys
+----
+Ueventd by default takes no action for `/sys`, however it can be instructed to set permissions for
+certain files in `/sys` when matching uevents are generated. This is done using a ueventd.rc script
+and a line that begins with `/sys`. These lines take the format of
+
+ nodename attr mode uid gid
+For example
+
+ /sys/devices/system/cpu/cpu* cpufreq/scaling_max_freq 0664 system system
+When a uevent that matches the pattern `/sys/devices/system/cpu/cpu*` is sent, the matching sysfs
+attribute, `cpufreq/scaling_max_freq`, will have its mode set to `0664`, its user to to `system` and
+its group set to `system`.
+
+Note that `*` matches as a wildcard and can be used anywhere in a path.
+
+## Firmware loading
+----------------
+Ueventd automatically serves firmware requests by searching through a list of firmware directories
+for a file matching the uevent `FIRMWARE`. It then forks a process to serve this firmware to the
+kernel.
+
+The list of firmware directories is customized by a `firmware_directories` line in a ueventd.rc
+file. This line takes the format of
+
+ firmware_directories <firmware_directory> [ <firmware_directory> ]*
+For example
+
+ firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/
+Adds those 4 directories, in that order to the list of firmware directories that will be tried by
+ueventd. Note that this option always accumulates to the list; it is not possible to remove previous
+entries.
+
+Ueventd will wait until after `post-fs` in init, to keep retrying before believing the firmwares are
+not present.
+
+## Coldboot
+--------
+Ueventd must create devices in `/dev` for all devices that have already sent their uevents before
+ueventd has started. To do so, when ueventd is started it does what it calls a 'coldboot' on `/sys`,
+in which it writes 'add' to every 'uevent' file that it finds in `/sys/class`, `/sys/block`, and
+`/sys/devices`. This causes the kernel to regenerate the uevents for these paths, and thus for
+ueventd to create the nodes.
+
+For boot time purposes, this is done in parallel across a set of child processes. `ueventd.cpp` in
+this directory contains documentation on how the parallelization is done.
diff --git a/init/action.cpp b/init/action.cpp
index 1a66eee..69e40d0 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -195,10 +195,11 @@
found = true;
}
} else {
- std::string prop_val = android::base::GetProperty(trigger_name, "");
- if (prop_val.empty() || (trigger_value != "*" && trigger_value != prop_val)) {
- return false;
+ std::string prop_value = android::base::GetProperty(trigger_name, "");
+ if (trigger_value == "*" && !prop_value.empty()) {
+ continue;
}
+ if (trigger_value != prop_value) return false;
}
}
return found;
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index b60c450..fd2d766 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -121,9 +121,9 @@
_exit(127);
}
ioctl(fd, TIOCSCTTY, 0);
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
close(fd);
const char* path = "/system/bin/sh";
@@ -291,6 +291,10 @@
const char* path = "/system/bin/init";
const char* args[] = {path, "selinux_setup", nullptr};
+ auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
execv(path, const_cast<char**>(args));
// execv() only returns if an error happened, in which case we
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 143cdfd..fd42256 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -523,6 +523,7 @@
// This function initializes SELinux then execs init to run in the init SELinux context.
int SetupSelinux(char** argv) {
+ SetStdioToDevNull(argv);
InitKernelLogging(argv);
if (REBOOT_BOOTLOADER_ON_PANIC) {
diff --git a/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp b/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp
index eb53e57..7bba599 100644
--- a/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp
+++ b/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp
@@ -17,6 +17,7 @@
#include <inttypes.h>
#include <linux/dma-buf.h>
#include <poll.h>
+#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
@@ -230,7 +231,7 @@
DmaBufTester() : ion_fd(ion_open()), ion_heap_mask(get_ion_heap_mask()) {}
~DmaBufTester() {
- if (is_valid()) {
+ if (ion_fd >= 0) {
ion_close(ion_fd);
}
}
@@ -241,12 +242,16 @@
int fd;
int err = ion_alloc_fd(ion_fd, size, 0, ion_heap_mask, 0, &fd);
if (err < 0) {
- return unique_fd{err};
+ printf("Failed ion_alloc_fd, return value: %d\n", err);
+ return unique_fd{};
}
if (!name.empty()) {
- err = ioctl(fd, DMA_BUF_SET_NAME, name.c_str());
- if (err < 0) return unique_fd{-errno};
+ if (ioctl(fd, DMA_BUF_SET_NAME, name.c_str()) == -1) {
+ printf("Failed ioctl(DMA_BUF_SET_NAME): %s\n", strerror(errno));
+ close(fd);
+ return unique_fd{};
+ }
}
return unique_fd{fd};
@@ -306,7 +311,7 @@
return ret;
}
- unique_fd ion_fd;
+ int ion_fd;
const int ion_heap_mask;
};
diff --git a/libnativeloader/include/nativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
index 2d6ce85..8937636 100644
--- a/libnativeloader/include/nativeloader/dlext_namespaces.h
+++ b/libnativeloader/include/nativeloader/dlext_namespaces.h
@@ -22,18 +22,6 @@
__BEGIN_DECLS
-/*
- * Initializes anonymous namespaces. The shared_libs_sonames is the list of sonames
- * to be shared by default namespace separated by colon. Example: "libc.so:libm.so:libdl.so".
- *
- * The library_search_path is the search path for anonymous namespace. The anonymous namespace
- * is used in the case when linker cannot identify the caller of dlopen/dlsym. This happens
- * for the code not loaded by dynamic linker; for example calls from the mono-compiled code.
- */
-extern bool android_init_anonymous_namespace(const char* shared_libs_sonames,
- const char* library_search_path);
-
-
enum {
/* A regular namespace is the namespace with a custom search path that does
* not impose any restrictions on the location of native libraries.
@@ -62,8 +50,18 @@
*/
ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED = 0x08000000,
- ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED = ANDROID_NAMESPACE_TYPE_SHARED |
- ANDROID_NAMESPACE_TYPE_ISOLATED,
+ /* This flag instructs linker to use this namespace as the anonymous
+ * namespace. The anonymous namespace is used in the case when linker cannot
+ * identify the caller of dlopen/dlsym. This happens for the code not loaded
+ * by dynamic linker; for example calls from the mono-compiled code. There can
+ * be only one anonymous namespace in a process. If there already is an
+ * anonymous namespace in the process, using this flag when creating a new
+ * namespace causes an error.
+ */
+ ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS = 0x10000000,
+
+ ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED =
+ ANDROID_NAMESPACE_TYPE_SHARED | ANDROID_NAMESPACE_TYPE_ISOLATED,
};
/*
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
index a9eea8c..7f5768c 100644
--- a/libnativeloader/library_namespaces.cpp
+++ b/libnativeloader/library_namespaces.cpp
@@ -159,13 +159,6 @@
}
}
- // Initialize the anonymous namespace with the first non-empty library path.
- Result<void> ret;
- if (!library_path.empty() && !initialized_ &&
- !(ret = InitPublicNamespace(library_path.c_str()))) {
- return ret.error();
- }
-
LOG_ALWAYS_FATAL_IF(FindNamespaceByClassLoader(env, class_loader) != nullptr,
"There is already a namespace associated with this classloader");
@@ -215,13 +208,22 @@
// Create the app namespace
NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
- auto app_ns =
- NativeLoaderNamespace::Create(namespace_name, library_path, permitted_path, parent_ns,
- is_shared, target_sdk_version < 24 /* is_greylist_enabled */);
+ // Heuristic: the first classloader with non-empty library_path is assumed to
+ // be the main classloader for app
+ // TODO(b/139178525) remove this heuristic by determining this in LoadedApk (or its
+ // friends) and then passing it down to here.
+ bool is_main_classloader = app_main_namespace_ == nullptr && !library_path.empty();
+ // Policy: the namespace for the main classloader is also used as the
+ // anonymous namespace.
+ bool also_used_as_anonymous = is_main_classloader;
+ // Note: this function is executed with g_namespaces_mutex held, thus no
+ // racing here.
+ auto app_ns = NativeLoaderNamespace::Create(
+ namespace_name, library_path, permitted_path, parent_ns, is_shared,
+ target_sdk_version < 24 /* is_greylist_enabled */, also_used_as_anonymous);
if (!app_ns) {
return app_ns.error();
}
-
// ... and link to other namespaces to allow access to some public libraries
bool is_bridged = app_ns->IsBridged();
@@ -278,6 +280,9 @@
}
namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), *app_ns));
+ if (is_main_classloader) {
+ app_main_namespace_ = &(*app_ns);
+ }
return &(namespaces_.back().second);
}
@@ -295,32 +300,6 @@
return nullptr;
}
-Result<void> LibraryNamespaces::InitPublicNamespace(const char* library_path) {
- // Ask native bride if this apps library path should be handled by it
- bool is_native_bridge = NativeBridgeIsPathSupported(library_path);
-
- // (http://b/25844435) - Some apps call dlopen from generated code (mono jited
- // code is one example) unknown to linker in which case linker uses anonymous
- // namespace. The second argument specifies the search path for the anonymous
- // namespace which is the library_path of the classloader.
- initialized_ = android_init_anonymous_namespace(default_public_libraries().c_str(),
- is_native_bridge ? nullptr : library_path);
- if (!initialized_) {
- return Error() << dlerror();
- }
-
- // and now initialize native bridge namespaces if necessary.
- if (NativeBridgeInitialized()) {
- initialized_ = NativeBridgeInitAnonymousNamespace(default_public_libraries().c_str(),
- is_native_bridge ? library_path : nullptr);
- if (!initialized_) {
- return Error() << NativeBridgeGetError();
- }
- }
-
- return {};
-}
-
NativeLoaderNamespace* LibraryNamespaces::FindParentNamespaceByClassLoader(JNIEnv* env,
jobject class_loader) {
jobject parent_class_loader = GetParentClassLoader(env, class_loader);
diff --git a/libnativeloader/library_namespaces.h b/libnativeloader/library_namespaces.h
index e54bc0a..7b3efff 100644
--- a/libnativeloader/library_namespaces.h
+++ b/libnativeloader/library_namespaces.h
@@ -38,7 +38,7 @@
// object for a given ClassLoader.
class LibraryNamespaces {
public:
- LibraryNamespaces() : initialized_(false) {}
+ LibraryNamespaces() : initialized_(false), app_main_namespace_(nullptr) {}
LibraryNamespaces(LibraryNamespaces&&) = default;
LibraryNamespaces(const LibraryNamespaces&) = delete;
@@ -48,6 +48,7 @@
void Reset() {
namespaces_.clear();
initialized_ = false;
+ app_main_namespace_ = nullptr;
}
Result<NativeLoaderNamespace*> Create(JNIEnv* env, uint32_t target_sdk_version,
jobject class_loader, bool is_shared, jstring dex_path,
@@ -59,6 +60,7 @@
NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
bool initialized_;
+ NativeLoaderNamespace* app_main_namespace_;
std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
};
diff --git a/libnativeloader/native_loader_namespace.cpp b/libnativeloader/native_loader_namespace.cpp
index 4add6e6..a81fddf 100644
--- a/libnativeloader/native_loader_namespace.cpp
+++ b/libnativeloader/native_loader_namespace.cpp
@@ -85,7 +85,8 @@
Result<NativeLoaderNamespace> NativeLoaderNamespace::Create(
const std::string& name, const std::string& search_paths, const std::string& permitted_paths,
- const NativeLoaderNamespace* parent, bool is_shared, bool is_greylist_enabled) {
+ const NativeLoaderNamespace* parent, bool is_shared, bool is_greylist_enabled,
+ bool also_used_as_anonymous) {
bool is_bridged = false;
if (parent != nullptr) {
is_bridged = parent->IsBridged();
@@ -100,7 +101,17 @@
}
const NativeLoaderNamespace& effective_parent = parent != nullptr ? *parent : *platform_ns;
+ // All namespaces for apps are isolated
uint64_t type = ANDROID_NAMESPACE_TYPE_ISOLATED;
+
+ // The namespace is also used as the anonymous namespace
+ // which is used when the linker fails to determine the caller address
+ if (also_used_as_anonymous) {
+ type |= ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS;
+ }
+
+ // Bundled apps have access to all system libraries that are currently loaded
+ // in the default namespace
if (is_shared) {
type |= ANDROID_NAMESPACE_TYPE_SHARED;
}
diff --git a/libnativeloader/native_loader_namespace.h b/libnativeloader/native_loader_namespace.h
index 29b759c..7200ee7 100644
--- a/libnativeloader/native_loader_namespace.h
+++ b/libnativeloader/native_loader_namespace.h
@@ -39,7 +39,8 @@
const std::string& search_paths,
const std::string& permitted_paths,
const NativeLoaderNamespace* parent, bool is_shared,
- bool is_greylist_enabled);
+ bool is_greylist_enabled,
+ bool also_used_as_anonymous);
NativeLoaderNamespace(NativeLoaderNamespace&&) = default;
NativeLoaderNamespace(const NativeLoaderNamespace&) = default;
diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp
index b939eee..a641109 100644
--- a/libnativeloader/native_loader_test.cpp
+++ b/libnativeloader/native_loader_test.cpp
@@ -331,7 +331,8 @@
// expected output (.. for the default test inputs)
std::string expected_namespace_name = "classloader-namespace";
- uint64_t expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
+ uint64_t expected_namespace_flags =
+ ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS;
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";
@@ -356,17 +357,6 @@
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,
@@ -443,7 +433,7 @@
dex_path = "/system/app/foo/foo.apk";
is_shared = true;
- expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED;
+ expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
SetExpectations();
RunTest();
}
@@ -452,7 +442,7 @@
dex_path = "/vendor/app/foo/foo.apk";
is_shared = true;
- expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED;
+ expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
SetExpectations();
RunTest();
}
@@ -475,7 +465,7 @@
dex_path = "/product/app/foo/foo.apk";
is_shared = true;
- expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED;
+ expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
SetExpectations();
RunTest();
}
@@ -485,7 +475,7 @@
is_shared = true;
target_sdk_version = 30;
- expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED;
+ expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
SetExpectations();
RunTest();
}
@@ -512,6 +502,22 @@
RunTest();
}
+TEST_P(NativeLoaderTest_Create, NamespaceForSharedLibIsNotUsedAsAnonymousNamespace) {
+ if (IsBridged()) {
+ // There is no shared lib in translated arch
+ // TODO(jiyong): revisit this
+ return;
+ }
+ // compared to apks, for java shared libs, library_path is empty; java shared
+ // libs don't have their own native libs. They use platform's.
+ library_path = "";
+ expected_library_path = library_path;
+ // no ALSO_USED_AS_ANONYMOUS
+ expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
+ SetExpectations();
+ RunTest();
+}
+
TEST_P(NativeLoaderTest_Create, TwoApks) {
SetExpectations();
const uint32_t second_app_target_sdk_version = 29;
@@ -523,6 +529,8 @@
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";
+ // no ALSO_USED_AS_ANONYMOUS
+ const uint64_t expected_second_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
// The scenario is that second app is loaded by the first app.
// So the first app's classloader (`classloader`) is parent of the second
@@ -532,10 +540,10 @@
// 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())))
+ EXPECT_CALL(*mock, mock_create_namespace(
+ Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
+ StrEq(second_app_library_path), expected_second_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));
@@ -568,7 +576,5 @@
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/libpackagelistparser/packagelistparser.cpp b/libpackagelistparser/packagelistparser.cpp
index ddf558b..59c3a74 100644
--- a/libpackagelistparser/packagelistparser.cpp
+++ b/libpackagelistparser/packagelistparser.cpp
@@ -92,10 +92,7 @@
info->uid = uid;
// Integer to bool conversions.
- if (debuggable != 0 && debuggable != 1) return false;
info->debuggable = debuggable;
-
- if (profileable_from_shell != 0 && profileable_from_shell != 1) return false;
info->profileable_from_shell = profileable_from_shell;
return true;
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index 5b8179f..c5c4960 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -17,6 +17,7 @@
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
+#include <algorithm>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
@@ -48,13 +49,6 @@
#define off64_t off_t
#endif
-#define min(a, b) \
- ({ \
- typeof(a) _a = (a); \
- typeof(b) _b = (b); \
- (_a < _b) ? _a : _b; \
- })
-
#define SPARSE_HEADER_MAJOR_VER 1
#define SPARSE_HEADER_MINOR_VER 0
#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
@@ -231,7 +225,7 @@
struct output_file_gz* outgz = to_output_file_gz(out);
while (len > 0) {
- ret = gzwrite(outgz->gz_fd, data, min(len, (unsigned int)INT_MAX));
+ ret = gzwrite(outgz->gz_fd, data, std::min<unsigned int>(len, (unsigned int)INT_MAX));
if (ret == 0) {
error("gzwrite %s", gzerror(outgz->gz_fd, nullptr));
return -1;
@@ -268,7 +262,7 @@
int ret;
while (off > 0) {
- to_write = min(off, (int64_t)INT_MAX);
+ to_write = std::min(off, (int64_t)INT_MAX);
ret = outc->write(outc->priv, nullptr, to_write);
if (ret < 0) {
return ret;
@@ -470,7 +464,7 @@
}
while (len) {
- write_len = min(len, out->block_size);
+ write_len = std::min(len, out->block_size);
ret = out->ops->write(out, out->fill_buf, write_len);
if (ret < 0) {
return ret;
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 3321425..a639592 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -633,15 +633,15 @@
namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.links = default
-namespace.neuralnetworks.link.default.shared_libs = libc.so
-namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
-namespace.neuralnetworks.link.default.shared_libs += libdl.so
-namespace.neuralnetworks.link.default.shared_libs += liblog.so
-namespace.neuralnetworks.link.default.shared_libs += libm.so
-namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
-namespace.neuralnetworks.link.default.shared_libs += libsync.so
-namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
+namespace.neuralnetworks.links = system
+namespace.neuralnetworks.link.system.shared_libs = libc.so
+namespace.neuralnetworks.link.system.shared_libs += libcgrouprc.so
+namespace.neuralnetworks.link.system.shared_libs += libdl.so
+namespace.neuralnetworks.link.system.shared_libs += liblog.so
+namespace.neuralnetworks.link.system.shared_libs += libm.so
+namespace.neuralnetworks.link.system.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.system.shared_libs += libsync.so
+namespace.neuralnetworks.link.system.shared_libs += libvndksupport.so
###############################################################################
# Namespace config for native tests that need access to both system and vendor
@@ -795,5 +795,3 @@
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
-
-namespace.default.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 86d8042..4d34b67 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -121,6 +121,9 @@
mkdir /mnt/media_rw 0750 root media_rw
mkdir /mnt/user 0755 root root
mkdir /mnt/user/0 0755 root root
+ mkdir /mnt/user/0/self 0755 root root
+ mkdir /mnt/user/0/emulated 0755 root root
+ mkdir /mnt/user/0/emulated/0 0755 root root
mkdir /mnt/expand 0771 system system
mkdir /mnt/appfuse 0711 root root
@@ -367,9 +370,6 @@
# Once everything is setup, no need to modify /.
# The bind+remount combination allows this to work in containers.
mount rootfs rootfs / remount bind ro nodev
- # Mount default storage into root namespace
- mount none /mnt/runtime/default /storage bind rec
- mount none none /storage slave rec
# Make sure /sys/kernel/debug (if present) is labeled properly
# Note that tracefs may be mounted under debug, so we need to cross filesystems
@@ -642,6 +642,22 @@
chown root system /dev/fscklogs/log
chmod 0770 /dev/fscklogs/log
+# Switch between sdcardfs and FUSE depending on persist property
+# TODO: Move this to ro property before launch because FDE devices
+# interact with persistent properties differently during boot
+on zygote-start && property:persist.sys.fuse=true
+ # Mount default storage into root namespace
+ mount none /mnt/user/0 /storage bind rec
+ mount none none /storage slave rec
+on zygote-start && property:persist.sys.fuse=false
+ # Mount default storage into root namespace
+ mount none /mnt/runtime/default /storage bind rec
+ mount none none /storage slave rec
+on zygote-start && property:persist.sys.fuse=""
+ # Mount default storage into root namespace
+ mount none /mnt/runtime/default /storage bind rec
+ mount none none /storage slave rec
+
# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
on zygote-start && property:ro.crypto.state=unencrypted