Merge "Parse IfIndex for Netlink RTM_NEWADDR / RTM_DELADDR event"
diff --git a/.clang-format-4 b/.clang-format-4
index ae4a451..9127163 100644
--- a/.clang-format-4
+++ b/.clang-format-4
@@ -5,6 +5,7 @@
CommentPragmas: NOLINT:.*
DerivePointerAlignment: false
IndentWidth: 4
+ContinuationIndentWidth: 8
PointerAlignment: Left
TabWidth: 4
UseTab: Never
diff --git a/CleanSpec.mk b/CleanSpec.mk
index dc45959..0e43dae 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -74,3 +74,7 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/ld.config.txt)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/ld.config.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/)
diff --git a/OWNERS b/OWNERS
index 1d319af..682a067 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,5 +1 @@
enh@google.com
-per-file libsysutils/src/Netlink* = ek@google.com
-per-file libsysutils/src/Netlink* = lorenzo@google.com
-per-file libsysutils/include/sysutils/Netlink* = ek@google.com
-per-file libsysutils/include/sysutils/Netlink* = lorenzo@google.com
diff --git a/adb/Android.bp b/adb/Android.bp
index 553473f..c0e4c57 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -19,17 +19,13 @@
"-Wall",
"-Wextra",
"-Werror",
+ "-Wexit-time-destructors",
"-Wno-unused-parameter",
"-Wno-missing-field-initializers",
"-Wvla",
],
rtti: true,
- clang_cflags: [
- "-Wexit-time-destructors",
- "-Wthread-safety",
- ],
-
use_version_lib: true,
compile_multilib: "first",
@@ -85,6 +81,12 @@
"-luserenv",
],
},
+
+ not_windows: {
+ cflags: [
+ "-Wthread-safety",
+ ],
+ },
},
}
@@ -104,6 +106,7 @@
"socket_spec.cpp",
"sysdeps/errno.cpp",
"transport.cpp",
+ "transport_fd.cpp",
"transport_local.cpp",
"transport_usb.cpp",
]
@@ -302,8 +305,6 @@
name: "adbd",
defaults: ["adb_defaults"],
- // adbd must be static, as it is copied into the recovery image.
- static_executable: true,
recovery_available: true,
srcs: [
@@ -344,7 +345,6 @@
"libselinux",
"libsquashfs_utils",
"libqemu_pipe",
- "libdebuggerd_handler",
"libbase",
"libcutils",
@@ -371,6 +371,7 @@
"liblog",
"libusb",
"libmdnssd",
+ "libselinux",
],
test_suites: ["device-tests"],
}
diff --git a/adb/OVERVIEW.TXT b/adb/OVERVIEW.TXT
index 29a6992..f0b184c 100644
--- a/adb/OVERVIEW.TXT
+++ b/adb/OVERVIEW.TXT
@@ -103,9 +103,6 @@
4-byte hex length, followed by a string giving the reason
for failure.
- 3. As a special exception, for 'host:version', a 4-byte
- hex string corresponding to the server's internal version number
-
Note that the connection is still alive after an OKAY, which allows the
client to make other requests. But in certain cases, an OKAY will even
change the state of the connection.
diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT
index 30c21f7..3e18a54 100644
--- a/adb/SERVICES.TXT
+++ b/adb/SERVICES.TXT
@@ -7,10 +7,6 @@
host:version
Ask the ADB server for its internal version number.
- As a special exception, the server will respond with a 4-byte
- hex string corresponding to its internal version number, without
- any OKAY or FAIL.
-
host:kill
Ask the ADB server to quit immediately. This is used when the
ADB client detects that an obsolete server is running after an
diff --git a/adb/adb.h b/adb/adb.h
index ede55da..c884166 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -124,8 +124,6 @@
void print_packet(const char* label, apacket* p);
-// These use the system (v)fprintf, not the adb prefixed ones defined in sysdeps.h, so they
-// shouldn't be tagged with ADB_FORMAT_ARCHETYPE.
void fatal(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
void fatal_errno(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 39983ab..e07dba7 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -1136,8 +1136,7 @@
cmd += " " + escape_arg(*argv++);
}
- // No need for shell protocol with logcat, always disable for simplicity.
- return send_shell_command(cmd, true);
+ return send_shell_command(cmd);
}
static void write_zeros(int bytes, int fd) {
@@ -1796,6 +1795,11 @@
}
else if (!strcmp(argv[0], "track-devices")) {
return adb_connect_command("host:track-devices");
+ } else if (!strcmp(argv[0], "raw")) {
+ if (argc != 2) {
+ return syntax_error("adb raw SERVICE");
+ }
+ return adb_connect_command(argv[1]);
}
@@ -1861,7 +1865,7 @@
cmd += " " + escape_arg(*argv++);
}
- return send_shell_command(cmd, false);
+ return send_shell_command(cmd);
}
static int install_app(int argc, const char** argv) {
@@ -2049,7 +2053,7 @@
cmd += " " + escape_arg(*argv++);
}
- return send_shell_command(cmd, false);
+ return send_shell_command(cmd);
}
static int uninstall_app_legacy(int argc, const char** argv) {
@@ -2073,7 +2077,7 @@
static int delete_file(const std::string& filename) {
std::string cmd = "rm -f " + escape_arg(filename);
- return send_shell_command(cmd, false);
+ return send_shell_command(cmd);
}
static int install_app_legacy(int argc, const char** argv) {
diff --git a/adb/client/commandline.h b/adb/client/commandline.h
index 36cd798..3d10030 100644
--- a/adb/client/commandline.h
+++ b/adb/client/commandline.h
@@ -92,7 +92,7 @@
// resulting output.
// if |callback| is non-null, stdout/stderr output will be handled by it.
int send_shell_command(
- const std::string& command, bool disable_shell_protocol,
- StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
+ const std::string& command, bool disable_shell_protocol = false,
+ StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
#endif // COMMANDLINE_H
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index 26f8d83..1275641 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -510,8 +510,7 @@
return false;
}
-
- void Printf(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+ void Printf(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
std::string s;
va_list ap;
@@ -522,7 +521,7 @@
line_printer_.Print(s, LinePrinter::INFO);
}
- void Println(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+ void Println(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
std::string s;
va_list ap;
@@ -534,7 +533,7 @@
line_printer_.KeepInfoLine();
}
- void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+ void Error(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
std::string s = "adb: error: ";
va_list ap;
@@ -545,7 +544,7 @@
line_printer_.Print(s, LinePrinter::ERROR);
}
- void Warning(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+ void Warning(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
std::string s = "adb: warning: ";
va_list ap;
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index 9751ebf..f529e8f 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -75,7 +75,7 @@
static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
/// List of opened usb handles
-static std::vector<usb_handle*> handle_list;
+static std::vector<usb_handle*>& handle_list = *new std::vector<usb_handle*>();
/// Locker for the list of opened usb handles
static std::mutex& usb_lock = *new std::mutex();
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 232d9c5..c02cafa 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -38,7 +38,6 @@
#include <scoped_minijail.h>
#include <private/android_filesystem_config.h>
-#include "debuggerd/handler.h"
#include "selinux/android.h"
#include "adb.h"
@@ -274,7 +273,6 @@
close_stdin();
- debuggerd_init(nullptr);
adb_trace_init(argv);
D("Handling main()");
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 401c99c..56d647a 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -98,6 +98,7 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <private/android_logger.h>
+#include <selinux/android.h>
#include "adb.h"
#include "adb_io.h"
@@ -343,6 +344,24 @@
adb_write(oom_score_adj_fd, oom_score_adj_value, strlen(oom_score_adj_value)));
}
+#ifdef __ANDROID_RECOVERY__
+ // Special routine for recovery. Switch to shell domain when adbd is
+ // is running with dropped privileged (i.e. not running as root) and
+ // is built for the recovery mode. This is required because recovery
+ // rootfs is not labeled and everything is labeled just as rootfs.
+ char* con = nullptr;
+ if (getcon(&con) == 0) {
+ if (!strcmp(con, "u:r:adbd:s0")) {
+ if (selinux_android_setcon("u:r:shell:s0") < 0) {
+ LOG(FATAL) << "Could not set SELinux context for subprocess";
+ }
+ }
+ freecon(con);
+ } else {
+ LOG(FATAL) << "Failed to get SELinux context";
+ }
+#endif
+
if (command_.empty()) {
// Spawn a login shell if we don't have a command.
execle(_PATH_BSHELL, "-" _PATH_BSHELL, nullptr, cenv.data());
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 1b7758c..098a39d 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -21,6 +21,7 @@
#include "fdevent.h"
#include <fcntl.h>
+#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -75,6 +76,8 @@
static bool main_thread_valid;
static uint64_t main_thread_id;
+static uint64_t fdevent_id;
+
static bool run_needs_flush = false;
static auto& run_queue_notify_fd = *new unique_fd();
static auto& run_queue_mutex = *new std::mutex();
@@ -111,7 +114,8 @@
if (fde->state & FDE_ERROR) {
state += "E";
}
- return android::base::StringPrintf("(fdevent %d %s)", fde->fd.get(), state.c_str());
+ return android::base::StringPrintf("(fdevent %" PRIu64 ": fd %d %s)", fde->id, fde->fd.get(),
+ state.c_str());
}
void fdevent_install(fdevent* fde, int fd, fd_func func, void* arg) {
@@ -125,6 +129,7 @@
CHECK_GE(fd, 0);
fdevent* fde = new fdevent();
+ fde->id = fdevent_id++;
fde->state = FDE_ACTIVE;
fde->fd.reset(fd);
fde->func = func;
diff --git a/adb/fdevent.h b/adb/fdevent.h
index 69c4072..d501b86 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -32,8 +32,7 @@
typedef void (*fd_func)(int fd, unsigned events, void *userdata);
struct fdevent {
- fdevent* next = nullptr;
- fdevent* prev = nullptr;
+ uint64_t id;
unique_fd fd;
int force_eof = 0;
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 3be99f6..f1197d7 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -39,19 +39,6 @@
#include "sysdeps/network.h"
#include "sysdeps/stat.h"
-// Some printf-like functions are implemented in terms of
-// android::base::StringAppendV, so they should use the same attribute for
-// compile-time format string checking. On Windows, if the mingw version of
-// vsnprintf is used in StringAppendV, use `gnu_printf' which allows z in %zd
-// and PRIu64 (and related) to be recognized by the compile-time checking.
-#define ADB_FORMAT_ARCHETYPE __printf__
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-#undef ADB_FORMAT_ARCHETYPE
-#define ADB_FORMAT_ARCHETYPE gnu_printf
-#endif
-#endif
-
#ifdef _WIN32
// Clang-only nullability specifiers
@@ -212,14 +199,12 @@
extern int adb_utime(const char *, struct utimbuf *);
extern int adb_chmod(const char *, int);
-extern int adb_vfprintf(FILE *stream, const char *format, va_list ap)
- __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 0)));
-extern int adb_vprintf(const char *format, va_list ap)
- __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 0)));
-extern int adb_fprintf(FILE *stream, const char *format, ...)
- __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3)));
-extern int adb_printf(const char *format, ...)
- __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 2)));
+extern int adb_vfprintf(FILE* stream, const char* format, va_list ap)
+ __attribute__((__format__(__printf__, 2, 0)));
+extern int adb_vprintf(const char* format, va_list ap) __attribute__((__format__(__printf__, 1, 0)));
+extern int adb_fprintf(FILE* stream, const char* format, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+extern int adb_printf(const char* format, ...) __attribute__((__format__(__printf__, 1, 2)));
extern int adb_fputs(const char* buf, FILE* stream);
extern int adb_fputc(int ch, FILE* stream);
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index bfac342..52f586c 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -2455,9 +2455,8 @@
}
// Function prototype because attributes cannot be placed on func definitions.
-static int _console_vfprintf(const HANDLE console, FILE* stream,
- const char *format, va_list ap)
- __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 3, 0)));
+static int _console_vfprintf(const HANDLE console, FILE* stream, const char* format, va_list ap)
+ __attribute__((__format__(__printf__, 3, 0)));
// Internal function to format a UTF-8 string and write it to a Win32 console.
// Returns -1 on error.
diff --git a/adb/transport.h b/adb/transport.h
index ae9cc02..cb20615 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -92,6 +92,8 @@
std::string transport_name_;
ReadCallback read_callback_;
ErrorCallback error_callback_;
+
+ static std::unique_ptr<Connection> FromFd(unique_fd fd);
};
// Abstraction for a blocking packet transport.
diff --git a/adb/transport_benchmark.cpp b/adb/transport_benchmark.cpp
index da24aa7..022808f 100644
--- a/adb/transport_benchmark.cpp
+++ b/adb/transport_benchmark.cpp
@@ -24,13 +24,19 @@
#include "sysdeps.h"
#include "transport.h"
-#define ADB_CONNECTION_BENCHMARK(benchmark_name, ...) \
- BENCHMARK_TEMPLATE(benchmark_name, FdConnection, ##__VA_ARGS__) \
- ->Arg(1) \
- ->Arg(16384) \
- ->Arg(MAX_PAYLOAD) \
+#define ADB_CONNECTION_BENCHMARK(benchmark_name, ...) \
+ BENCHMARK_TEMPLATE(benchmark_name, FdConnection, ##__VA_ARGS__) \
+ ->Arg(1) \
+ ->Arg(16384) \
+ ->Arg(MAX_PAYLOAD) \
+ ->UseRealTime(); \
+ BENCHMARK_TEMPLATE(benchmark_name, NonblockingFdConnection, ##__VA_ARGS__) \
+ ->Arg(1) \
+ ->Arg(16384) \
+ ->Arg(MAX_PAYLOAD) \
->UseRealTime()
+struct NonblockingFdConnection;
template <typename ConnectionType>
std::unique_ptr<Connection> MakeConnection(unique_fd fd);
@@ -40,6 +46,11 @@
return std::make_unique<BlockingConnectionAdapter>(std::move(fd_connection));
}
+template <>
+std::unique_ptr<Connection> MakeConnection<NonblockingFdConnection>(unique_fd fd) {
+ return Connection::FromFd(std::move(fd));
+}
+
template <typename ConnectionType>
void BM_Connection_Unidirectional(benchmark::State& state) {
int fds[2];
diff --git a/adb/transport_fd.cpp b/adb/transport_fd.cpp
new file mode 100644
index 0000000..85f3c52
--- /dev/null
+++ b/adb/transport_fd.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <deque>
+#include <mutex>
+#include <string>
+#include <thread>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
+
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "sysdeps.h"
+#include "sysdeps/memory.h"
+#include "transport.h"
+#include "types.h"
+
+static void CreateWakeFds(unique_fd* read, unique_fd* write) {
+ // TODO: eventfd on linux?
+ int wake_fds[2];
+ int rc = adb_socketpair(wake_fds);
+ set_file_block_mode(wake_fds[0], false);
+ set_file_block_mode(wake_fds[1], false);
+ CHECK_EQ(0, rc);
+ *read = unique_fd(wake_fds[0]);
+ *write = unique_fd(wake_fds[1]);
+}
+
+struct NonblockingFdConnection : public Connection {
+ NonblockingFdConnection(unique_fd fd) : started_(false), fd_(std::move(fd)) {
+ set_file_block_mode(fd_.get(), false);
+ CreateWakeFds(&wake_fd_read_, &wake_fd_write_);
+ }
+
+ void SetRunning(bool value) {
+ std::lock_guard<std::mutex> lock(run_mutex_);
+ running_ = value;
+ }
+
+ bool IsRunning() {
+ std::lock_guard<std::mutex> lock(run_mutex_);
+ return running_;
+ }
+
+ void Run(std::string* error) {
+ SetRunning(true);
+ while (IsRunning()) {
+ adb_pollfd pfds[2] = {
+ {.fd = fd_.get(), .events = POLLIN},
+ {.fd = wake_fd_read_.get(), .events = POLLIN},
+ };
+
+ {
+ std::lock_guard<std::mutex> lock(this->write_mutex_);
+ if (!writable_) {
+ pfds[0].events |= POLLOUT;
+ }
+ }
+
+ int rc = adb_poll(pfds, 2, -1);
+ if (rc == -1) {
+ *error = android::base::StringPrintf("poll failed: %s", strerror(errno));
+ return;
+ } else if (rc == 0) {
+ LOG(FATAL) << "poll timed out with an infinite timeout?";
+ }
+
+ if (pfds[0].revents) {
+ if ((pfds[0].revents & POLLOUT)) {
+ std::lock_guard<std::mutex> lock(this->write_mutex_);
+ WriteResult result = DispatchWrites();
+ switch (result) {
+ case WriteResult::Error:
+ *error = "write failed";
+ return;
+
+ case WriteResult::Completed:
+ writable_ = true;
+ break;
+
+ case WriteResult::TryAgain:
+ break;
+ }
+ }
+
+ if (pfds[0].revents & POLLIN) {
+ // TODO: Should we be getting blocks from a free list?
+ auto block = std::make_unique<IOVector::block_type>(MAX_PAYLOAD);
+ rc = adb_read(fd_.get(), &(*block)[0], block->size());
+ if (rc == -1) {
+ *error = std::string("read failed: ") + strerror(errno);
+ return;
+ } else if (rc == 0) {
+ *error = "read failed: EOF";
+ return;
+ }
+ block->resize(rc);
+ read_buffer_.append(std::move(block));
+
+ if (!read_header_ && read_buffer_.size() >= sizeof(amessage)) {
+ auto header_buf = read_buffer_.take_front(sizeof(amessage)).coalesce();
+ CHECK_EQ(sizeof(amessage), header_buf.size());
+ read_header_ = std::make_unique<amessage>();
+ memcpy(read_header_.get(), header_buf.data(), sizeof(amessage));
+ }
+
+ if (read_header_ && read_buffer_.size() >= read_header_->data_length) {
+ auto data_chain = read_buffer_.take_front(read_header_->data_length);
+
+ // TODO: Make apacket carry around a IOVector instead of coalescing.
+ auto payload = data_chain.coalesce<apacket::payload_type>();
+ auto packet = std::make_unique<apacket>();
+ packet->msg = *read_header_;
+ packet->payload = std::move(payload);
+ read_header_ = nullptr;
+ read_callback_(this, std::move(packet));
+ }
+ }
+ }
+
+ if (pfds[1].revents) {
+ uint64_t buf;
+ rc = adb_read(wake_fd_read_.get(), &buf, sizeof(buf));
+ CHECK_EQ(static_cast<int>(sizeof(buf)), rc);
+
+ // We were woken up either to add POLLOUT to our events, or to exit.
+ // Do nothing.
+ }
+ }
+ }
+
+ void Start() override final {
+ if (started_.exchange(true)) {
+ LOG(FATAL) << "Connection started multiple times?";
+ }
+
+ thread_ = std::thread([this]() {
+ std::string error = "connection closed";
+ Run(&error);
+ this->error_callback_(this, error);
+ });
+ }
+
+ void Stop() override final {
+ SetRunning(false);
+ WakeThread();
+ thread_.join();
+ }
+
+ void WakeThread() {
+ uint64_t buf = 0;
+ if (TEMP_FAILURE_RETRY(adb_write(wake_fd_write_.get(), &buf, sizeof(buf))) != sizeof(buf)) {
+ LOG(FATAL) << "failed to wake up thread";
+ }
+ }
+
+ enum class WriteResult {
+ Error,
+ Completed,
+ TryAgain,
+ };
+
+ WriteResult DispatchWrites() REQUIRES(write_mutex_) {
+ CHECK(!write_buffer_.empty());
+ if (!writable_) {
+ return WriteResult::TryAgain;
+ }
+
+ auto iovs = write_buffer_.iovecs();
+ ssize_t rc = adb_writev(fd_.get(), iovs.data(), iovs.size());
+ if (rc == -1) {
+ return WriteResult::Error;
+ } else if (rc == 0) {
+ errno = 0;
+ return WriteResult::Error;
+ }
+
+ // TODO: Implement a more efficient drop_front?
+ write_buffer_.take_front(rc);
+ if (write_buffer_.empty()) {
+ return WriteResult::Completed;
+ }
+
+ // There's data left in the range, which means our write returned early.
+ return WriteResult::TryAgain;
+ }
+
+ bool Write(std::unique_ptr<apacket> packet) final {
+ std::lock_guard<std::mutex> lock(write_mutex_);
+ const char* header_begin = reinterpret_cast<const char*>(&packet->msg);
+ const char* header_end = header_begin + sizeof(packet->msg);
+ auto header_block = std::make_unique<IOVector::block_type>(header_begin, header_end);
+ write_buffer_.append(std::move(header_block));
+ if (!packet->payload.empty()) {
+ write_buffer_.append(std::make_unique<IOVector::block_type>(std::move(packet->payload)));
+ }
+ return DispatchWrites() != WriteResult::Error;
+ }
+
+ std::thread thread_;
+
+ std::atomic<bool> started_;
+ std::mutex run_mutex_;
+ bool running_ GUARDED_BY(run_mutex_);
+
+ std::unique_ptr<amessage> read_header_;
+ IOVector read_buffer_;
+
+ unique_fd fd_;
+ unique_fd wake_fd_read_;
+ unique_fd wake_fd_write_;
+
+ std::mutex write_mutex_;
+ bool writable_ GUARDED_BY(write_mutex_) = true;
+ IOVector write_buffer_ GUARDED_BY(write_mutex_);
+
+ IOVector incoming_queue_;
+};
+
+std::unique_ptr<Connection> Connection::FromFd(unique_fd fd) {
+ return std::make_unique<NonblockingFdConnection>(std::move(fd));
+}
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
index c3396ee..11fcf71 100644
--- a/base/include/android-base/chrono_utils.h
+++ b/base/include/android-base/chrono_utils.h
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_CHRONO_UTILS_H
-#define ANDROID_BASE_CHRONO_UTILS_H
+#pragma once
#include <chrono>
#include <sstream>
-#if __cplusplus > 201103L // C++14
+#if __cplusplus > 201103L && !defined(__WIN32) // C++14
using namespace std::chrono_literals;
#endif
@@ -52,5 +51,3 @@
} // namespace base
} // namespace android
-
-#endif // ANDROID_BASE_CHRONO_UTILS_H
diff --git a/base/include/android-base/endian.h b/base/include/android-base/endian.h
index 6eb677c..cbbd8c9 100644
--- a/base/include/android-base/endian.h
+++ b/base/include/android-base/endian.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_ENDIAN_H
-#define ANDROID_BASE_ENDIAN_H
+#pragma once
/* A cross-platform equivalent of bionic's <sys/endian.h>. */
@@ -86,5 +85,3 @@
#define le64toh(x) (x)
#endif
-
-#endif // ANDROID_BASE_ENDIAN_H
diff --git a/base/include/android-base/errors.h b/base/include/android-base/errors.h
index 04c299c..06f29fc 100644
--- a/base/include/android-base/errors.h
+++ b/base/include/android-base/errors.h
@@ -27,8 +27,7 @@
// special handling to get the error string. Refer to Microsoft documentation
// to determine which error code to check for each function.
-#ifndef ANDROID_BASE_ERRORS_H
-#define ANDROID_BASE_ERRORS_H
+#pragma once
#include <string>
@@ -42,5 +41,3 @@
} // namespace base
} // namespace android
-
-#endif // ANDROID_BASE_ERRORS_H
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 667d6fb..908690b 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_FILE_H
-#define ANDROID_BASE_FILE_H
+#pragma once
#include <sys/stat.h>
#include <sys/types.h>
@@ -78,5 +77,3 @@
} // namespace base
} // namespace android
-
-#endif // ANDROID_BASE_FILE_H
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index 05a12e7..7f0801f 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_LOGGING_H
-#define ANDROID_BASE_LOGGING_H
+#pragma once
//
// Google-style C++ logging.
@@ -506,5 +505,3 @@
#undef OSTREAM_STRING_POINTER_USAGE_WARNING
} // namespace std
-
-#endif // ANDROID_BASE_LOGGING_H
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index fd6efb2..0c8eac0 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_MACROS_H
-#define ANDROID_BASE_MACROS_H
+#pragma once
#include <stddef.h> // for size_t
#include <unistd.h> // for TEMP_FAILURE_RETRY
@@ -197,5 +196,3 @@
#elif defined(__mips__) && defined(__LP64__)
#define ABI_STRING "mips64"
#endif
-
-#endif // ANDROID_BASE_MACROS_H
diff --git a/base/include/android-base/memory.h b/base/include/android-base/memory.h
index 9971226..0277a03 100644
--- a/base/include/android-base/memory.h
+++ b/base/include/android-base/memory.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_MEMORY_H
-#define ANDROID_BASE_MEMORY_H
+#pragma once
namespace android {
namespace base {
@@ -37,5 +36,3 @@
} // namespace base
} // namespace android
-
-#endif // ANDROID_BASE_MEMORY_H
diff --git a/base/include/android-base/parsedouble.h b/base/include/android-base/parsedouble.h
index daa6902..c273c61 100644
--- a/base/include/android-base/parsedouble.h
+++ b/base/include/android-base/parsedouble.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_PARSEDOUBLE_H
-#define ANDROID_BASE_PARSEDOUBLE_H
+#pragma once
#include <errno.h>
#include <stdlib.h>
@@ -46,5 +45,3 @@
} // namespace base
} // namespace android
-
-#endif // ANDROID_BASE_PARSEDOUBLE_H
diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h
index 1b7cc5f..b0fc7c3 100644
--- a/base/include/android-base/parseint.h
+++ b/base/include/android-base/parseint.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_PARSEINT_H
-#define ANDROID_BASE_PARSEINT_H
+#pragma once
#include <errno.h>
#include <stdlib.h>
@@ -104,5 +103,3 @@
} // namespace base
} // namespace android
-
-#endif // ANDROID_BASE_PARSEINT_H
diff --git a/base/include/android-base/parsenetaddress.h b/base/include/android-base/parsenetaddress.h
index b4ac025..47f8b5f 100644
--- a/base/include/android-base/parsenetaddress.h
+++ b/base/include/android-base/parsenetaddress.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_PARSENETADDRESS_H
-#define ANDROID_BASE_PARSENETADDRESS_H
+#pragma once
#include <string>
@@ -34,5 +33,3 @@
} // namespace base
} // namespace android
-
-#endif // ANDROID_BASE_PARSENETADDRESS_H
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 3a05143..31e5273 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_PROPERTIES_H
-#define ANDROID_BASE_PROPERTIES_H
+#pragma once
#include <sys/cdefs.h>
@@ -73,5 +72,3 @@
} // namespace base
} // namespace android
-
-#endif // ANDROID_BASE_PROPERTIES_H
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
index c314e02..e6a9d10 100644
--- a/base/include/android-base/scopeguard.h
+++ b/base/include/android-base/scopeguard.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_SCOPEGUARD_H
-#define ANDROID_BASE_SCOPEGUARD_H
+#pragma once
#include <utility> // for std::move, std::forward
@@ -66,5 +65,3 @@
} // namespace base
} // namespace android
-
-#endif // ANDROID_BASE_SCOPEGUARD_H
diff --git a/base/include/android-base/stringprintf.h b/base/include/android-base/stringprintf.h
index 1fd6297..93c56af 100644
--- a/base/include/android-base/stringprintf.h
+++ b/base/include/android-base/stringprintf.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_STRINGPRINTF_H
-#define ANDROID_BASE_STRINGPRINTF_H
+#pragma once
#include <stdarg.h>
#include <string>
@@ -24,33 +23,18 @@
namespace base {
// These printf-like functions are implemented in terms of vsnprintf, so they
-// use the same attribute for compile-time format string checking. On Windows,
-// if the mingw version of vsnprintf is used, use `gnu_printf' which allows z
-// in %zd and PRIu64 (and related) to be recognized by the compile-time
-// checking.
-#define ANDROID_BASE_FORMAT_ARCHETYPE __printf__
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-#undef ANDROID_BASE_FORMAT_ARCHETYPE
-#define ANDROID_BASE_FORMAT_ARCHETYPE gnu_printf
-#endif
-#endif
+// use the same attribute for compile-time format string checking.
// Returns a string corresponding to printf-like formatting of the arguments.
-std::string StringPrintf(const char* fmt, ...)
- __attribute__((__format__(ANDROID_BASE_FORMAT_ARCHETYPE, 1, 2)));
+std::string StringPrintf(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
// Appends a printf-like formatting of the arguments to 'dst'.
void StringAppendF(std::string* dst, const char* fmt, ...)
- __attribute__((__format__(ANDROID_BASE_FORMAT_ARCHETYPE, 2, 3)));
+ __attribute__((__format__(__printf__, 2, 3)));
// Appends a printf-like formatting of the arguments to 'dst'.
void StringAppendV(std::string* dst, const char* format, va_list ap)
- __attribute__((__format__(ANDROID_BASE_FORMAT_ARCHETYPE, 2, 0)));
-
-#undef ANDROID_BASE_FORMAT_ARCHETYPE
+ __attribute__((__format__(__printf__, 2, 0)));
} // namespace base
} // namespace android
-
-#endif // ANDROID_BASE_STRINGPRINTF_H
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index 4d9fa34..9c35560 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_STRINGS_H
-#define ANDROID_BASE_STRINGS_H
+#pragma once
#include <sstream>
#include <string>
@@ -75,5 +74,3 @@
} // namespace base
} // namespace android
-
-#endif // ANDROID_BASE_STRINGS_H
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index b29676f..9e2ea97 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_TEST_UTILS_H
-#define ANDROID_BASE_TEST_UTILS_H
+#pragma once
#include <regex>
#include <string>
@@ -114,5 +113,3 @@
ADD_FAILURE() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << (str); \
} \
} while (0)
-
-#endif // ANDROID_BASE_TEST_UTILS_H
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
index 1307f0e..d56e935 100644
--- a/base/include/android-base/thread_annotations.h
+++ b/base/include/android-base/thread_annotations.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_THREAD_ANNOTATIONS_H
-#define ANDROID_BASE_THREAD_ANNOTATIONS_H
+#pragma once
#if defined(__SUPPORT_TS_ANNOTATION__) || defined(__clang__)
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
@@ -109,5 +108,3 @@
#define NO_THREAD_SAFETY_ANALYSIS \
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
-
-#endif // ANDROID_BASE_THREAD_ANNOTATIONS_H
diff --git a/base/include/android-base/threads.h b/base/include/android-base/threads.h
index 85e65ba..f4ba809 100644
--- a/base/include/android-base/threads.h
+++ b/base/include/android-base/threads.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_THREADS_H
-#define ANDROID_BASE_THREADS_H
+#pragma once
#include <stdint.h>
@@ -24,5 +23,3 @@
uint64_t GetThreadId();
}
} // namespace android
-
-#endif
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 5d89271..d334e30 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_UNIQUE_FD_H
-#define ANDROID_BASE_UNIQUE_FD_H
+#pragma once
#include <fcntl.h>
@@ -150,5 +149,3 @@
#endif
"close called on unique_fd"
)));
-
-#endif // ANDROID_BASE_UNIQUE_FD_H
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
index c9cc1ab..4b91623 100755
--- a/base/include/android-base/utf8.h
+++ b/base/include/android-base/utf8.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_BASE_UTF8_H
-#define ANDROID_BASE_UTF8_H
+#pragma once
#ifdef _WIN32
#include <string>
@@ -102,5 +101,3 @@
} // namespace utf8
} // namespace base
} // namespace android
-
-#endif // ANDROID_BASE_UTF8_H
diff --git a/base/logging.cpp b/base/logging.cpp
index 978d56d..35054ac 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -58,13 +58,15 @@
#include <android-base/strings.h>
#include <android-base/threads.h>
-namespace {
+namespace android {
+namespace base {
+
+// BSD-based systems like Android/macOS have getprogname(). Others need us to provide one.
+#if defined(__GLIBC__) || defined(_WIN32)
+static const char* getprogname() {
#if defined(__GLIBC__)
-const char* getprogname() {
return program_invocation_short_name;
-}
#elif defined(_WIN32)
-const char* getprogname() {
static bool first = true;
static char progname[MAX_PATH] = {};
@@ -81,11 +83,28 @@
}
return progname;
+#endif
}
#endif
+static const char* GetFileBasename(const char* file) {
+ // We can't use basename(3) even on Unix because the Mac doesn't
+ // have a non-modifying basename.
+ const char* last_slash = strrchr(file, '/');
+ if (last_slash != nullptr) {
+ return last_slash + 1;
+ }
+#if defined(_WIN32)
+ const char* last_backslash = strrchr(file, '\\');
+ if (last_backslash != nullptr) {
+ return last_backslash + 1;
+ }
+#endif
+ return file;
+}
+
#if defined(__linux__)
-int OpenKmsg() {
+static int OpenKmsg() {
#if defined(__ANDROID__)
// pick up 'file w /dev/kmsg' environment from daemon's init rc file
const auto val = getenv("ANDROID_FILE__dev_kmsg");
@@ -100,10 +119,6 @@
return TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC));
}
#endif
-} // namespace
-
-namespace android {
-namespace base {
static std::mutex& LoggingLock() {
static auto& logging_lock = *new std::mutex();
@@ -216,7 +231,7 @@
unsigned int /*line*/, const char* message) {
if (severity >= WARNING) {
fflush(stdout);
- fprintf(stderr, "%s: %s\n", getprogname(), message);
+ fprintf(stderr, "%s: %s\n", GetFileBasename(getprogname()), message);
} else {
fprintf(stdout, "%s\n", message);
}
@@ -336,22 +351,6 @@
Aborter() = std::move(aborter);
}
-static const char* GetFileBasename(const char* file) {
- // We can't use basename(3) even on Unix because the Mac doesn't
- // have a non-modifying basename.
- const char* last_slash = strrchr(file, '/');
- if (last_slash != nullptr) {
- return last_slash + 1;
- }
-#if defined(_WIN32)
- const char* last_backslash = strrchr(file, '\\');
- if (last_backslash != nullptr) {
- return last_backslash + 1;
- }
-#endif
- return file;
-}
-
// This indirection greatly reduces the stack impact of having lots of
// checks/logging in a function.
class LogMessageData {
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index dd9ba88..8fc2171 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -63,7 +63,6 @@
name: "bootstat",
defaults: ["bootstat_defaults"],
static_libs: ["libbootstat"],
- shared_libs: ["liblogcat"],
init_rc: ["bootstat.rc"],
product_variables: {
pdk: {
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 01b8948..ddd1caf 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -445,6 +445,10 @@
*hw_reset* ) var="hard,hw_reset" ;;
*usb* ) var="cold,charger" ;;
*rtc* ) var="cold,rtc" ;;
+ *2sec_reboot* ) var="cold,rtc,2sec" ;;
+ *wdt_by_pass_pwk* ) var="warm" ;;
+ wdt ) var="reboot" ;;
+ *tool_by_pass_pwk* ) var="reboot,tool" ;;
*bootloader* ) var="bootloader" ;;
* ) var="reboot" ;;
esac
@@ -476,23 +480,25 @@
- (wait until screen is up, boot has completed)
- adb shell getprop ro.boot.bootreason (bootloader reason)
- adb shell getprop persist.sys.boot.reason (last reason)
+- adb shell getprop sys.boot.reason.last (last last reason)
- adb shell getprop sys.boot.reason (system reason)
- NB: all should have a value that is compliant with our known set." ]
test_properties() {
duration_test 1
wait_for_screen
retval=0
- check_set="ro.boot.bootreason persist.sys.boot.reason sys.boot.reason"
+ check_set="ro.boot.bootreason sys.boot.reason.last sys.boot.reason"
bootloader=""
# NB: this test could fail if performed _after_ optional_factory_reset test
# and will report
# ERROR: expected "reboot" got ""
- # for Android property persist.sys.boot.reason
+ # for Android property sys.boot.reason.last
# following is mitigation for the persist.sys.boot.reason, skip it
if [ "reboot,factory_reset" = "`validate_property ro.boot_bootreason`" ]; then
check_set="ro.boot.bootreason sys.boot.reason"
bootloader="bootloader"
fi
+ EXPECT_PROPERTY persist.sys.boot.reason ""
for prop in ${check_set}; do
reason=`validate_property ${prop}`
EXPECT_PROPERTY ${prop} ${reason} || retval=${?}
@@ -550,7 +556,8 @@
popd >&2
wait_for_screen
EXPECT_PROPERTY sys.boot.reason "\(reboot,ota\|bootloader\)"
- EXPECT_PROPERTY persist.sys.boot.reason bootloader
+ EXPECT_PROPERTY sys.boot.reason.last bootloader
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs reboot,ota bootloader
}
@@ -564,7 +571,8 @@
adb reboot ota
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,ota
- EXPECT_PROPERTY persist.sys.boot.reason reboot,ota
+ EXPECT_PROPERTY sys.boot.reason.last reboot,ota
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs reboot,ota
}
@@ -589,9 +597,15 @@
wait_for_screen
bootloader_reason=`validate_property ro.boot.bootreason`
EXPECT_PROPERTY ro.boot.bootreason ${bootloader_reason}
- EXPECT_PROPERTY sys.boot.reason ${reason}
- EXPECT_PROPERTY persist.sys.boot.reason ${reason}
- report_bootstat_logs ${reason}
+ # to make sys.boot.reason report user friendly
+ reasons=${reason}
+ if [ "${bootloader_reason}" != "${reason}" -a -n "${bootloader_reason}" ]; then
+ reasons="\(${reason}\|${bootloader_reason}\)"
+ fi
+ EXPECT_PROPERTY sys.boot.reason ${reasons}
+ EXPECT_PROPERTY sys.boot.reason.last ${reason}
+ EXPECT_PROPERTY persist.sys.boot.reason ""
+ report_bootstat_logs ${reason} ${bootloader_reason}
}
[ "USAGE: test_cold
@@ -623,7 +637,8 @@
adb reboot >&2
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
- EXPECT_PROPERTY persist.sys.boot.reason "reboot,.*"
+ EXPECT_PROPERTY sys.boot.reason.last "reboot,.*"
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs reboot,factory_reset reboot, reboot,adb \
"-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \
"-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date"
@@ -654,6 +669,7 @@
wait_for_screen
( exit ${save_ret} ) # because one can not just do ?=${save_ret}
EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
+ EXPECT_PROPERTY sys.boot.reason.last ""
EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs reboot,factory_reset bootloader \
"-bootstat: Failed to read /data/misc/bootstat/last_boot_time_utc: No such file or directory" \
@@ -727,7 +743,8 @@
)
EXPECT_PROPERTY sys.boot.reason shutdown,battery
- EXPECT_PROPERTY persist.sys.boot.reason cold
+ EXPECT_PROPERTY sys.boot.reason.last cold
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs shutdown,battery "-bootstat: Battery level at shutdown 2%"
exitPstore
}
@@ -748,7 +765,8 @@
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
EXPECT_PROPERTY sys.boot.reason shutdown,battery
- EXPECT_PROPERTY persist.sys.boot.reason shutdown,battery
+ EXPECT_PROPERTY sys.boot.reason.last shutdown,battery
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs shutdown,battery
}
@@ -768,7 +786,8 @@
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
EXPECT_PROPERTY sys.boot.reason shutdown,thermal,battery
- EXPECT_PROPERTY persist.sys.boot.reason shutdown,thermal,battery
+ EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal,battery
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs shutdown,thermal,battery
}
@@ -804,7 +823,8 @@
echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
wait_for_screen
EXPECT_PROPERTY sys.boot.reason ${panic_msg}
- EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
+ EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs kernel_panic,sysrq
exitPstore
}
@@ -831,7 +851,8 @@
echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
wait_for_screen
EXPECT_PROPERTY sys.boot.reason ${panic_msg}
- EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
+ EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs kernel_panic,sysrq,test \
"-bootstat: Unknown boot reason: kernel_panic,sysrq,test"
exitPstore
@@ -861,7 +882,8 @@
adb reboot warm
wait_for_screen
EXPECT_PROPERTY sys.boot.reason ${panic_msg}
- EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
+ EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs kernel_panic,hung
exitPstore
}
@@ -893,7 +915,8 @@
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
EXPECT_PROPERTY sys.boot.reason shutdown,thermal
- EXPECT_PROPERTY persist.sys.boot.reason shutdown,thermal
+ EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs shutdown,thermal
}
@@ -913,7 +936,8 @@
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
EXPECT_PROPERTY sys.boot.reason shutdown,userrequested
- EXPECT_PROPERTY persist.sys.boot.reason shutdown,userrequested
+ EXPECT_PROPERTY sys.boot.reason.last shutdown,userrequested
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs shutdown,userrequested
}
@@ -929,7 +953,8 @@
adb shell reboot
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,shell
- EXPECT_PROPERTY persist.sys.boot.reason reboot,shell
+ EXPECT_PROPERTY sys.boot.reason.last reboot,shell
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs reboot,shell
}
@@ -945,7 +970,8 @@
adb reboot
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,adb
- EXPECT_PROPERTY persist.sys.boot.reason reboot,adb
+ EXPECT_PROPERTY sys.boot.reason.last reboot,adb
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs reboot,adb
}
@@ -980,23 +1006,8 @@
adb shell 'reboot "Its Just So Hard"'
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
- EXPECT_PROPERTY persist.sys.boot.reason "reboot,Its Just So Hard"
- # Do not leave this test with an illegal value in persist.sys.boot.reason
- save_ret=${?} # hold on to error code from above two lines
- if isDebuggable; then # can do this easy, or we can do this hard.
- adb shell su root setprop persist.sys.boot.reason reboot,its_just_so_hard
- ( exit ${save_ret} ) # because one can not just do ?=${save_ret}
- else
- report_bootstat_logs reboot,its_just_so_hard # report what we have so far
- # user build mitigation
- adb shell reboot its_just_so_hard
- wait_for_screen
- ( exit ${save_ret} ) # because one can not just do ?=${save_ret}
- EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
- fi
- # Ensure persist.sys.boot.reason now valid, failure here acts as a signal
- # that we could choke up following tests. For example test_properties.
- EXPECT_PROPERTY persist.sys.boot.reason reboot,its_just_so_hard ${flag}
+ EXPECT_PROPERTY sys.boot.reason.last reboot,its_just_so_hard
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs reboot,its_just_so_hard
}
@@ -1045,7 +1056,8 @@
# Check values
EXPECT_PROPERTY ro.boot.bootreason "${bootloader_expected}"
EXPECT_PROPERTY sys.boot.reason "${sys_expected}"
- EXPECT_PROPERTY persist.sys.boot.reason "${last_expected}"
+ EXPECT_PROPERTY sys.boot.reason.last "${last_expected}"
+ EXPECT_PROPERTY persist.sys.boot.reason ""
report_bootstat_logs "${sys_expected}"
}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 1b13b21..7ec57ec 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -43,7 +43,6 @@
#include <android/log.h>
#include <cutils/android_reboot.h>
#include <cutils/properties.h>
-#include <log/logcat.h>
#include <metricslogger/metrics_logger.h>
#include "boot_event_record_store.h"
@@ -201,7 +200,7 @@
{"cold", 56},
{"hard", 57},
{"warm", 58},
- // {"recovery", 59}, // Duplicate of enum 3 above. Immediate reuse possible.
+ {"reboot,kernel_power_off_charging__reboot_system", 59}, // Can not happen
{"thermal-shutdown", 60},
{"shutdown,thermal", 61},
{"shutdown,battery", 62},
@@ -228,7 +227,7 @@
{"2sec_reboot", 83},
{"reboot,by_key", 84},
{"reboot,longkey", 85},
- {"reboot,2sec", 86},
+ {"reboot,2sec", 86}, // Deprecate in two years, replaced with cold,rtc,2sec
{"shutdown,thermal,battery", 87},
{"reboot,its_just_so_hard", 88}, // produced by boot_reason_test
{"reboot,Its Just So Hard", 89}, // produced by boot_reason_test
@@ -306,6 +305,11 @@
{"kernel_panic,sysrq,livelock,alarm", 161}, // llkd
{"kernel_panic,sysrq,livelock,driver", 162}, // llkd
{"kernel_panic,sysrq,livelock,zombie", 163}, // llkd
+ {"kernel_panic,modem", 164},
+ {"kernel_panic,adsp", 165},
+ {"kernel_panic,dsps", 166},
+ {"kernel_panic,wcnss", 167},
+ {"kernel_panic,_sde_encoder_phys_cmd_handle_ppdone_timeout", 168},
};
// Converts a string value representing the reason the system booted to an
@@ -702,6 +706,10 @@
{"Corrupt kernel stack", "stack"},
{"low stack detected", "stack"},
{"corrupted stack end", "stack"},
+ {"subsys-restart: Resetting the SoC - modem crashed.", "modem"},
+ {"subsys-restart: Resetting the SoC - adsp crashed.", "adsp"},
+ {"subsys-restart: Resetting the SoC - dsps crashed.", "dsps"},
+ {"subsys-restart: Resetting the SoC - wcnss crashed.", "wcnss"},
};
ret = "kernel_panic";
@@ -726,6 +734,7 @@
const char system_reboot_reason_property[] = "sys.boot.reason";
const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;
+const char last_last_reboot_reason_property[] = "sys.boot.reason.last";
const char bootloader_reboot_reason_property[] = "ro.boot.bootreason";
// Scrub, Sanitize, Standardize and Enhance the boot reason string supplied.
@@ -782,7 +791,10 @@
{"hard,hw_reset", "hw_reset"},
{"cold,charger", "usb"},
{"cold,rtc", "rtc"},
- {"reboot,2sec", "2sec_reboot"},
+ {"cold,rtc,2sec", "2sec_reboot"},
+ {"!warm", "wdt_by_pass_pwk"}, // change flavour of blunt
+ {"!reboot", "^wdt$"}, // change flavour of blunt
+ {"reboot,tool", "tool_by_pass_pwk"},
{"bootloader", ""},
};
@@ -842,6 +854,10 @@
ret = "reboot," + subReason; // legitimize unknown reasons
}
}
+ // Some bootloaders shutdown results record in last kernel message.
+ if (!strcmp(ret.c_str(), "reboot,kernel_power_off_charging__reboot_system")) {
+ ret = "shutdown";
+ }
}
// Check for kernel panics, allowed to override reboot command.
@@ -853,103 +869,7 @@
}
}
- // The following battery test should migrate to a default system health HAL
-
- // Let us not worry if the reboot command was issued, for the cases of
- // reboot -p, reboot <no reason>, reboot cold, reboot warm and reboot hard.
- // Same for bootloader and ro.boot.bootreasons of this set, but a dead
- // battery could conceivably lead to these, so worthy of override.
- if (isBluntRebootReason(ret)) {
- // Heuristic to determine if shutdown possibly because of a dead battery?
- // Really a hail-mary pass to find it in last klog content ...
- static const int battery_dead_threshold = 2; // percent
- static const char battery[] = "healthd: battery l=";
- const pstoreConsole console(content);
- size_t pos = console.rfind(battery); // last one
- std::string digits;
- if (pos != std::string::npos) {
- digits = content.substr(pos + strlen(battery), strlen("100 "));
- // correct common errors
- correctForBitError(digits, "100 ");
- if (digits[0] == '!') digits[0] = '1';
- if (digits[1] == '!') digits[1] = '1';
- }
- const char* endptr = digits.c_str();
- unsigned level = 0;
- while (::isdigit(*endptr)) {
- level *= 10;
- level += *endptr++ - '0';
- // make sure no leading zeros, except zero itself, and range check.
- if ((level == 0) || (level > 100)) break;
- }
- // example bit error rate issues for 10%
- // 'l=10 ' no bits in error
- // 'l=00 ' single bit error (fails above)
- // 'l=1 ' single bit error
- // 'l=0 ' double bit error
- // There are others, not typically critical because of 2%
- // battery_dead_threshold. KISS check, make sure second
- // character after digit sequence is not a space.
- if ((level <= 100) && (endptr != digits.c_str()) && (endptr[0] == ' ') && (endptr[1] != ' ')) {
- LOG(INFO) << "Battery level at shutdown " << level << "%";
- if (level <= battery_dead_threshold) {
- ret = "shutdown,battery";
- }
- } else { // Most likely
- digits = ""; // reset digits
-
- // Content buffer no longer will have console data. Beware if more
- // checks added below, that depend on parsing console content.
- content = "";
-
- LOG(DEBUG) << "Can not find last low battery in last console messages";
- android_logcat_context ctx = create_android_logcat();
- FILE* fp = android_logcat_popen(&ctx, "logcat -b kernel -v brief -d");
- if (fp != nullptr) {
- android::base::ReadFdToString(fileno(fp), &content);
- }
- android_logcat_pclose(&ctx, fp);
- static const char logcat_battery[] = "W/healthd ( 0): battery l=";
- const char* match = logcat_battery;
-
- if (content == "") {
- // Service logd.klog not running, go to smaller buffer in the kernel.
- int rc = klogctl(KLOG_SIZE_BUFFER, nullptr, 0);
- if (rc > 0) {
- ssize_t len = rc + 1024; // 1K Margin should it grow between calls.
- std::unique_ptr<char[]> buf(new char[len]);
- rc = klogctl(KLOG_READ_ALL, buf.get(), len);
- if (rc < len) {
- len = rc + 1;
- }
- buf[--len] = '\0';
- content = buf.get();
- }
- match = battery;
- }
-
- pos = content.find(match); // The first one it finds.
- if (pos != std::string::npos) {
- digits = content.substr(pos + strlen(match), strlen("100 "));
- }
- endptr = digits.c_str();
- level = 0;
- while (::isdigit(*endptr)) {
- level *= 10;
- level += *endptr++ - '0';
- // make sure no leading zeros, except zero itself, and range check.
- if ((level == 0) || (level > 100)) break;
- }
- if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) {
- LOG(INFO) << "Battery level at startup " << level << "%";
- if (level <= battery_dead_threshold) {
- ret = "shutdown,battery";
- }
- } else {
- LOG(DEBUG) << "Can not find first battery level in dmesg or logcat";
- }
- }
- }
+ // TODO: use the HAL to get battery level (http://b/77725702).
// Is there a controlled shutdown hint in last_reboot_reason_property?
if (isBluntRebootReason(ret)) {
@@ -986,10 +906,6 @@
}
LOG(INFO) << "Canonical boot reason: " << ret;
- if (isKernelRebootReason(ret) && (GetProperty(last_reboot_reason_property) != "")) {
- // Rewrite as it must be old news, kernel reasons trump user space.
- SetProperty(last_reboot_reason_property, ret);
- }
return ret;
}
@@ -1114,6 +1030,22 @@
return android::base::boot_clock::now().time_since_epoch() - GetBootTimeOffset();
}
+void SetSystemBootReason() {
+ const std::string bootloader_boot_reason(GetProperty(bootloader_reboot_reason_property));
+ const std::string system_boot_reason(BootReasonStrToReason(bootloader_boot_reason));
+ // Record the scrubbed system_boot_reason to the property
+ SetProperty(system_reboot_reason_property, system_boot_reason);
+ // Shift last_reboot_reason_property to last_last_reboot_reason_property
+ std::string last_boot_reason(GetProperty(last_reboot_reason_property));
+ if (last_boot_reason.empty() || isKernelRebootReason(system_boot_reason)) {
+ last_boot_reason = system_boot_reason;
+ } else {
+ transformReason(last_boot_reason);
+ }
+ SetProperty(last_last_reboot_reason_property, last_boot_reason);
+ SetProperty(last_reboot_reason_property, "");
+}
+
// Records several metrics related to the time it takes to boot the device,
// including disambiguating boot time on encrypted or non-encrypted devices.
void RecordBootComplete() {
@@ -1193,12 +1125,10 @@
boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
// Log the scrubbed system_boot_reason.
- const std::string system_reason(BootReasonStrToReason(reason));
+ const std::string system_reason(GetProperty(system_reboot_reason_property));
int32_t system_boot_reason = BootReasonStrToEnum(system_reason);
boot_event_store.AddBootEventWithValue("system_boot_reason", system_boot_reason);
- // Record the scrubbed system_boot_reason to the property
- SetProperty(system_reboot_reason_property, system_reason);
if (reason == "") {
SetProperty(bootloader_reboot_reason_property, system_reason);
}
@@ -1264,20 +1194,22 @@
int option_index = 0;
static const char value_str[] = "value";
+ static const char system_boot_reason_str[] = "set_system_boot_reason";
static const char boot_complete_str[] = "record_boot_complete";
static const char boot_reason_str[] = "record_boot_reason";
static const char factory_reset_str[] = "record_time_since_factory_reset";
static const struct option long_options[] = {
// clang-format off
- { "help", no_argument, NULL, 'h' },
- { "log", no_argument, NULL, 'l' },
- { "print", no_argument, NULL, 'p' },
- { "record", required_argument, NULL, 'r' },
- { value_str, required_argument, NULL, 0 },
- { boot_complete_str, no_argument, NULL, 0 },
- { boot_reason_str, no_argument, NULL, 0 },
- { factory_reset_str, no_argument, NULL, 0 },
- { NULL, 0, NULL, 0 }
+ { "help", no_argument, NULL, 'h' },
+ { "log", no_argument, NULL, 'l' },
+ { "print", no_argument, NULL, 'p' },
+ { "record", required_argument, NULL, 'r' },
+ { value_str, required_argument, NULL, 0 },
+ { system_boot_reason_str, no_argument, NULL, 0 },
+ { boot_complete_str, no_argument, NULL, 0 },
+ { boot_reason_str, no_argument, NULL, 0 },
+ { factory_reset_str, no_argument, NULL, 0 },
+ { NULL, 0, NULL, 0 }
// clang-format on
};
@@ -1293,6 +1225,8 @@
// |optarg| is an external variable set by getopt representing
// the option argument.
value = optarg;
+ } else if (option_name == system_boot_reason_str) {
+ SetSystemBootReason();
} else if (option_name == boot_complete_str) {
RecordBootComplete();
} else if (option_name == boot_reason_str) {
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index f06a38f..1300a27 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -68,8 +68,9 @@
# Record boot complete metrics.
on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
+ # Converts bootloader boot reason to system boot reason
# Record boot_complete and related stats (decryption, etc).
# Record the boot reason.
# Record time since factory reset.
# Log all boot events.
- exec_background - system log -- /system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
+ exec_background - system log -- /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 0b13662..1b20157 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -47,6 +47,7 @@
cc_library_static {
name: "libtombstoned_client_static",
defaults: ["debuggerd_defaults"],
+ recovery_available: true,
srcs: [
"tombstoned/tombstoned_client.cpp",
"util.cpp",
@@ -90,7 +91,6 @@
cc_library_static {
name: "libdebuggerd_handler",
defaults: ["debuggerd_defaults"],
- recovery_available: true,
srcs: ["handler/debuggerd_fallback_nop.cpp"],
whole_static_libs: [
@@ -104,6 +104,7 @@
cc_library_static {
name: "libdebuggerd_handler_fallback",
defaults: ["debuggerd_defaults"],
+ recovery_available: true,
srcs: [
"handler/debuggerd_fallback.cpp",
],
@@ -120,6 +121,12 @@
"liblzma",
"libcutils",
],
+ target: {
+ recovery: {
+ cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+ exclude_static_libs: ["libdexfile"],
+ },
+ },
export_include_dirs: ["include"],
}
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index b016e23..360ea95 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -20,6 +20,7 @@
#include <string.h>
#include <limits>
+#include <string_view>
#include <thread>
#include <android-base/file.h>
@@ -33,9 +34,10 @@
using android::base::unique_fd;
static void usage(int exit_code) {
- fprintf(stderr, "usage: debuggerd [-b] PID\n");
+ fprintf(stderr, "usage: debuggerd [-bj] PID\n");
fprintf(stderr, "\n");
fprintf(stderr, "-b, --backtrace just a backtrace rather than a full tombstone\n");
+ fprintf(stderr, "-j collect java traces\n");
_exit(exit_code);
}
@@ -58,8 +60,19 @@
int main(int argc, char* argv[]) {
if (argc <= 1) usage(0);
if (argc > 3) usage(1);
- if (argc == 3 && strcmp(argv[1], "-b") != 0 && strcmp(argv[1], "--backtrace") != 0) usage(1);
- bool backtrace_only = argc == 3;
+
+ DebuggerdDumpType dump_type = kDebuggerdTombstone;
+
+ if (argc == 3) {
+ std::string_view flag = argv[1];
+ if (flag == "-b" || flag == "--backtrace") {
+ dump_type = kDebuggerdNativeBacktrace;
+ } else if (flag == "-j") {
+ dump_type = kDebuggerdJavaBacktrace;
+ } else {
+ usage(1);
+ }
+ }
pid_t pid;
if (!android::base::ParseInt(argv[argc - 1], &pid, 1, std::numeric_limits<pid_t>::max())) {
@@ -90,8 +103,7 @@
}
std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
- if (!debuggerd_trigger_dump(pid, backtrace_only ? kDebuggerdNativeBacktrace : kDebuggerdTombstone,
- 0, std::move(pipewrite))) {
+ if (!debuggerd_trigger_dump(pid, dump_type, 0, std::move(pipewrite))) {
redirect_thread.join();
errx(1, "failed to dump process %d", pid);
}
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 2935eb5..50c70f3 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -78,21 +78,10 @@
void set_verbose();
// These printf-like functions are implemented in terms of vsnprintf, so they
-// use the same attribute for compile-time format string checking. On Windows,
-// if the mingw version of vsnprintf is used, use `gnu_printf' which allows z
-// in %zd and PRIu64 (and related) to be recognized by the compile-time
-// checking.
-#define FASTBOOT_FORMAT_ARCHETYPE __printf__
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-#undef FASTBOOT_FORMAT_ARCHETYPE
-#define FASTBOOT_FORMAT_ARCHETYPE gnu_printf
-#endif
-#endif
+// use the same attribute for compile-time format string checking.
void die(const char* fmt, ...) __attribute__((__noreturn__))
-__attribute__((__format__(FASTBOOT_FORMAT_ARCHETYPE, 1, 2)));
-void verbose(const char* fmt, ...) __attribute__((__format__(FASTBOOT_FORMAT_ARCHETYPE, 1, 2)));
-#undef FASTBOOT_FORMAT_ARCHETYPE
+__attribute__((__format__(__printf__, 1, 2)));
+void verbose(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
/* Current product */
extern char cur_product[FB_RESPONSE_SZ + 1];
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index bc3b04b..b0b4839 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -56,12 +56,18 @@
"libselinux",
"libavb",
"libfstab",
+ "libdm",
+ "liblp",
],
export_static_lib_headers: [
"libfstab",
+ "libdm",
+ "liblp",
],
whole_static_libs: [
+ "liblp",
"liblogwrap",
+ "libdm",
"libfstab",
],
cppflags: [
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 6e9ffba..9856126 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1190,27 +1190,6 @@
return 0;
}
-int fs_mgr_unmount_all(struct fstab *fstab)
-{
- int i = 0;
- int ret = 0;
-
- if (!fstab) {
- return -1;
- }
-
- while (fstab->recs[i].blk_device) {
- if (umount(fstab->recs[i].mount_point)) {
- LERROR << "Cannot unmount filesystem at "
- << fstab->recs[i].mount_point;
- ret = -1;
- }
- i++;
- }
-
- return ret;
-}
-
/* This must be called after mount_all, because the mkswap command needs to be
* available.
*/
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 5a9cb65..7c6093e 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -33,11 +33,11 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <libavb/libavb.h>
+#include <libdm/dm.h>
#include "fs_mgr.h"
#include "fs_mgr_priv.h"
#include "fs_mgr_priv_avb_ops.h"
-#include "fs_mgr_priv_dm_ioctl.h"
#include "fs_mgr_priv_sha.h"
static inline bool nibble_value(const char& c, uint8_t* value) {
@@ -139,38 +139,23 @@
};
std::unique_ptr<FsManagerAvbVerifier> FsManagerAvbVerifier::Create() {
- std::string cmdline;
- if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
- PERROR << "Failed to read /proc/cmdline";
- return nullptr;
- }
-
std::unique_ptr<FsManagerAvbVerifier> avb_verifier(new FsManagerAvbVerifier());
if (!avb_verifier) {
LERROR << "Failed to create unique_ptr<FsManagerAvbVerifier>";
return nullptr;
}
- std::string digest;
- std::string hash_alg;
- for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
- std::vector<std::string> pieces = android::base::Split(entry, "=");
- const std::string& key = pieces[0];
- const std::string& value = pieces[1];
-
- if (key == "androidboot.vbmeta.hash_alg") {
- hash_alg = value;
- } else if (key == "androidboot.vbmeta.size") {
- if (!android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
- return nullptr;
- }
- } else if (key == "androidboot.vbmeta.digest") {
- digest = value;
- }
+ std::string value;
+ if (!fs_mgr_get_boot_config("vbmeta.size", &value) ||
+ !android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
+ LERROR << "Invalid hash size: " << value.c_str();
+ return nullptr;
}
// Reads hash algorithm.
size_t expected_digest_size = 0;
+ std::string hash_alg;
+ fs_mgr_get_boot_config("vbmeta.hash_alg", &hash_alg);
if (hash_alg == "sha256") {
expected_digest_size = SHA256_DIGEST_LENGTH * 2;
avb_verifier->hash_alg_ = kSHA256;
@@ -183,6 +168,8 @@
}
// Reads digest.
+ std::string digest;
+ fs_mgr_get_boot_config("vbmeta.digest", &digest);
if (digest.size() != expected_digest_size) {
LERROR << "Unexpected digest size: " << digest.size()
<< " (expected: " << expected_digest_size << ")";
@@ -231,9 +218,9 @@
// Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
// See the following link for more details:
// https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
-static std::string construct_verity_table(const AvbHashtreeDescriptor& hashtree_desc,
- const std::string& salt, const std::string& root_digest,
- const std::string& blk_device) {
+static bool construct_verity_table(const AvbHashtreeDescriptor& hashtree_desc,
+ const std::string& salt, const std::string& root_digest,
+ const std::string& blk_device, android::dm::DmTable* table) {
// Loads androidboot.veritymode from kernel cmdline.
std::string verity_mode;
if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
@@ -248,145 +235,56 @@
dm_verity_mode = "ignore_corruption";
} else if (verity_mode != "eio") { // Default dm_verity_mode is eio.
LERROR << "Unknown androidboot.veritymode: " << verity_mode;
- return "";
+ return false;
}
- // dm-verity construction parameters:
- // <version> <dev> <hash_dev>
- // <data_block_size> <hash_block_size>
- // <num_data_blocks> <hash_start_block>
- // <algorithm> <digest> <salt>
- // [<#opt_params> <opt_params>]
- std::ostringstream verity_table;
- verity_table << hashtree_desc.dm_verity_version << " " << blk_device << " " << blk_device << " "
- << hashtree_desc.data_block_size << " " << hashtree_desc.hash_block_size << " "
- << hashtree_desc.image_size / hashtree_desc.data_block_size << " "
- << hashtree_desc.tree_offset / hashtree_desc.hash_block_size << " "
- << hashtree_desc.hash_algorithm << " " << root_digest << " " << salt;
+ std::ostringstream hash_algorithm;
+ hash_algorithm << hashtree_desc.hash_algorithm;
- // Continued from the above optional parameters:
- // [<#opt_params> <opt_params>]
- int optional_argc = 0;
- std::ostringstream optional_args;
-
- // dm-verity optional parameters for FEC (forward error correction):
- // use_fec_from_device <fec_dev>
- // fec_roots <num>
- // fec_blocks <num>
- // fec_start <offset>
+ android::dm::DmTargetVerity target(0, hashtree_desc.image_size / 512,
+ hashtree_desc.dm_verity_version, blk_device, blk_device,
+ hashtree_desc.data_block_size, hashtree_desc.hash_block_size,
+ hashtree_desc.image_size / hashtree_desc.data_block_size,
+ hashtree_desc.tree_offset / hashtree_desc.hash_block_size,
+ hash_algorithm.str(), root_digest, salt);
if (hashtree_desc.fec_size > 0) {
- // Note that fec_blocks is the size that FEC covers, *NOT* the
- // size of the FEC data. Since we use FEC for everything up until
- // the FEC data, it's the same as the offset (fec_start).
- optional_argc += 8;
- // clang-format off
- optional_args << "use_fec_from_device " << blk_device
- << " fec_roots " << hashtree_desc.fec_num_roots
- << " fec_blocks " << hashtree_desc.fec_offset / hashtree_desc.data_block_size
- << " fec_start " << hashtree_desc.fec_offset / hashtree_desc.data_block_size
- << " ";
- // clang-format on
+ target.UseFec(blk_device, hashtree_desc.fec_num_roots,
+ hashtree_desc.fec_offset / hashtree_desc.data_block_size,
+ hashtree_desc.fec_offset / hashtree_desc.data_block_size);
}
-
if (!dm_verity_mode.empty()) {
- optional_argc += 1;
- optional_args << dm_verity_mode << " ";
+ target.SetVerityMode(dm_verity_mode);
}
-
// Always use ignore_zero_blocks.
- optional_argc += 1;
- optional_args << "ignore_zero_blocks";
+ target.IgnoreZeroBlocks();
- verity_table << " " << optional_argc << " " << optional_args.str();
- return verity_table.str();
-}
+ LINFO << "Built verity table: '" << target.GetParameterString() << "'";
-static bool load_verity_table(struct dm_ioctl* io, const std::string& dm_device_name, int fd,
- uint64_t image_size, const std::string& verity_table) {
- fs_mgr_dm_ioctl_init(io, DM_BUF_SIZE, dm_device_name);
-
- // The buffer consists of [dm_ioctl][dm_target_spec][verity_params].
- char* buffer = (char*)io;
-
- // Builds the dm_target_spec arguments.
- struct dm_target_spec* dm_target = (struct dm_target_spec*)&buffer[sizeof(struct dm_ioctl)];
- io->flags = DM_READONLY_FLAG;
- io->target_count = 1;
- dm_target->status = 0;
- dm_target->sector_start = 0;
- dm_target->length = image_size / 512;
- strcpy(dm_target->target_type, "verity");
-
- // Builds the verity params.
- char* verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
- size_t bufsize = DM_BUF_SIZE - (verity_params - buffer);
-
- LINFO << "Loading verity table: '" << verity_table << "'";
-
- // Copies verity_table to verity_params (including the terminating null byte).
- if (verity_table.size() > bufsize - 1) {
- LERROR << "Verity table size too large: " << verity_table.size()
- << " (max allowable size: " << bufsize - 1 << ")";
- return false;
- }
- memcpy(verity_params, verity_table.c_str(), verity_table.size() + 1);
-
- // Sets ext target boundary.
- verity_params += verity_table.size() + 1;
- verity_params = (char*)(((unsigned long)verity_params + 7) & ~7);
- dm_target->next = verity_params - buffer;
-
- // Sends the ioctl to load the verity table.
- if (ioctl(fd, DM_TABLE_LOAD, io)) {
- PERROR << "Error loading verity table";
- return false;
- }
-
- return true;
+ return table->AddTarget(std::make_unique<android::dm::DmTargetVerity>(target));
}
static bool hashtree_dm_verity_setup(struct fstab_rec* fstab_entry,
const AvbHashtreeDescriptor& hashtree_desc,
const std::string& salt, const std::string& root_digest,
bool wait_for_verity_dev) {
- // Gets the device mapper fd.
- android::base::unique_fd fd(open("/dev/device-mapper", O_RDWR));
- if (fd < 0) {
- PERROR << "Error opening device mapper";
+ android::dm::DmTable table;
+ if (!construct_verity_table(hashtree_desc, salt, root_digest, fstab_entry->blk_device, &table) ||
+ !table.valid()) {
+ LERROR << "Failed to construct verity table.";
return false;
}
+ table.set_readonly(true);
- // Creates the device.
- alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
- struct dm_ioctl* io = (struct dm_ioctl*)buffer;
const std::string mount_point(basename(fstab_entry->mount_point));
- if (!fs_mgr_dm_create_device(io, mount_point, fd)) {
+ android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
+ if (!dm.CreateDevice(mount_point, table)) {
LERROR << "Couldn't create verity device!";
return false;
}
- // Gets the name of the device file.
- std::string verity_blk_name;
- if (!fs_mgr_dm_get_device_name(io, mount_point, fd, &verity_blk_name)) {
- LERROR << "Couldn't get verity device number!";
- return false;
- }
-
- std::string verity_table =
- construct_verity_table(hashtree_desc, salt, root_digest, fstab_entry->blk_device);
- if (verity_table.empty()) {
- LERROR << "Failed to construct verity table.";
- return false;
- }
-
- // Loads the verity mapping table.
- if (!load_verity_table(io, mount_point, fd, hashtree_desc.image_size, verity_table)) {
- LERROR << "Couldn't load verity table!";
- return false;
- }
-
- // Activates the device.
- if (!fs_mgr_dm_resume_table(io, mount_point, fd)) {
+ std::string dev_path;
+ if (!dm.GetDmDevicePathByName(mount_point, &dev_path)) {
+ LERROR << "Couldn't get verity device path!";
return false;
}
@@ -395,10 +293,10 @@
// Updates fstab_rec->blk_device to verity device name.
free(fstab_entry->blk_device);
- fstab_entry->blk_device = strdup(verity_blk_name.c_str());
+ fstab_entry->blk_device = strdup(dev_path.c_str());
// Makes sure we've set everything up properly.
- if (wait_for_verity_dev && !fs_mgr_wait_for_file(verity_blk_name, 1s)) {
+ if (wait_for_verity_dev && !fs_mgr_wait_for_file(dev_path, 1s)) {
return false;
}
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index 9c5d3f3..733ad55 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -14,7 +14,10 @@
* limitations under the License.
*/
+#include <algorithm>
+#include <iterator>
#include <string>
+#include <vector>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
@@ -23,46 +26,71 @@
#include "fs_mgr_priv.h"
-// Tries to get the given boot config value from kernel cmdline.
-// Returns true if successfully found, false otherwise.
-bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
+std::vector<std::pair<std::string, std::string>> fs_mgr_parse_boot_config(const std::string& cmdline) {
+ static constexpr char quote = '"';
+
+ std::vector<std::pair<std::string, std::string>> result;
+ size_t base = 0;
+ while (true) {
+ // skip quoted spans
+ auto found = base;
+ while (((found = cmdline.find_first_of(" \"", found)) != cmdline.npos) &&
+ (cmdline[found] == quote)) {
+ // unbalanced quote is ok
+ if ((found = cmdline.find(quote, found + 1)) == cmdline.npos) break;
+ ++found;
+ }
+ std::string piece;
+ auto source = cmdline.substr(base, found - base);
+ std::remove_copy(source.begin(), source.end(),
+ std::back_insert_iterator<std::string>(piece), quote);
+ auto equal_sign = piece.find('=');
+ if (equal_sign == piece.npos) {
+ if (!piece.empty()) {
+ // no difference between <key> and <key>=
+ result.emplace_back(std::move(piece), "");
+ }
+ } else {
+ result.emplace_back(piece.substr(0, equal_sign), piece.substr(equal_sign + 1));
+ }
+ if (found == cmdline.npos) break;
+ base = found + 1;
+ }
+
+ return result;
+}
+
+bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& android_key,
+ std::string* out_val) {
FS_MGR_CHECK(out_val != nullptr);
- std::string cmdline;
- std::string cmdline_key("androidboot." + key);
- if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
- for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
- std::vector<std::string> pieces = android::base::Split(entry, "=");
- if (pieces.size() == 2) {
- if (pieces[0] == cmdline_key) {
- *out_val = pieces[1];
- return true;
- }
- }
+ const std::string cmdline_key("androidboot." + android_key);
+ for (const auto& [key, value] : fs_mgr_parse_boot_config(cmdline)) {
+ if (key == cmdline_key) {
+ *out_val = value;
+ return true;
}
}
+ *out_val = "";
return false;
}
-// Tries to get the boot config value in properties, kernel cmdline and
-// device tree (in that order). returns 'true' if successfully found, 'false'
-// otherwise
+// Tries to get the given boot config value from kernel cmdline.
+// Returns true if successfully found, false otherwise.
+bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
+ std::string cmdline;
+ if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) return false;
+ return fs_mgr_get_boot_config_from_kernel(cmdline, key, out_val);
+}
+
+// Tries to get the boot config value in device tree, properties and
+// kernel cmdline (in that order). Returns 'true' if successfully
+// found, 'false' otherwise.
bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
FS_MGR_CHECK(out_val != nullptr);
- // first check if we have "ro.boot" property already
- *out_val = android::base::GetProperty("ro.boot." + key, "");
- if (!out_val->empty()) {
- return true;
- }
-
- // fallback to kernel cmdline, properties may not be ready yet
- if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
- return true;
- }
-
- // lastly, check the device tree
+ // firstly, check the device tree
if (is_dt_compatible()) {
std::string file_name = get_android_dt_dir() + "/" + key;
if (android::base::ReadFileToString(file_name, out_val)) {
@@ -73,5 +101,16 @@
}
}
+ // next, check if we have "ro.boot" property already
+ *out_val = android::base::GetProperty("ro.boot." + key, "");
+ if (!out_val->empty()) {
+ return true;
+ }
+
+ // finally, fallback to kernel cmdline, properties may not be ready yet
+ if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
+ return true;
+ }
+
return false;
}
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index b2f3a68..5159b4c 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -37,95 +37,35 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <liblp/reader.h>
#include "fs_mgr_priv.h"
-#include "fs_mgr_priv_dm_ioctl.h"
namespace android {
namespace fs_mgr {
-std::string LogicalPartitionExtent::Serialize() const {
- // Note: we need to include an explicit null-terminator.
- std::string argv =
- android::base::StringPrintf("%s %" PRIu64, block_device_.c_str(), first_sector_);
- argv.push_back(0);
+using DeviceMapper = android::dm::DeviceMapper;
+using DmTable = android::dm::DmTable;
+using DmTarget = android::dm::DmTarget;
+using DmTargetZero = android::dm::DmTargetZero;
+using DmTargetLinear = android::dm::DmTargetLinear;
- // The kernel expects each target to be aligned.
- size_t spec_bytes = sizeof(struct dm_target_spec) + argv.size();
- size_t padding = ((spec_bytes + 7) & ~7) - spec_bytes;
- for (size_t i = 0; i < padding; i++) {
- argv.push_back(0);
- }
-
- struct dm_target_spec spec;
- spec.sector_start = logical_sector_;
- spec.length = num_sectors_;
- spec.status = 0;
- strcpy(spec.target_type, "linear");
- spec.next = sizeof(struct dm_target_spec) + argv.size();
-
- return std::string((char*)&spec, sizeof(spec)) + argv;
-}
-
-static bool LoadDmTable(int dm_fd, const LogicalPartition& partition) {
- // Combine all dm_target_spec buffers together.
- std::string target_string;
+static bool CreateDmDeviceForPartition(DeviceMapper& dm, const LogicalPartition& partition) {
+ DmTable table;
for (const auto& extent : partition.extents) {
- target_string += extent.Serialize();
+ table.AddTarget(std::make_unique<DmTargetLinear>(extent));
}
-
- // Allocate the ioctl buffer.
- size_t buffer_size = sizeof(struct dm_ioctl) + target_string.size();
- std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(buffer_size);
-
- // Initialize the ioctl buffer header, then copy our target specs in.
- struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
- fs_mgr_dm_ioctl_init(io, buffer_size, partition.name);
- io->target_count = partition.extents.size();
- if (partition.attributes & kPartitionReadonly) {
- io->flags |= DM_READONLY_FLAG;
- }
- memcpy(io + 1, target_string.c_str(), target_string.size());
-
- if (ioctl(dm_fd, DM_TABLE_LOAD, io)) {
- PERROR << "Failed ioctl() on DM_TABLE_LOAD, partition " << partition.name;
+ if (!dm.CreateDevice(partition.name, table)) {
return false;
}
- return true;
-}
-
-static bool LoadTablesAndActivate(int dm_fd, const LogicalPartition& partition) {
- if (!LoadDmTable(dm_fd, partition)) {
- return false;
- }
-
- struct dm_ioctl io;
- return fs_mgr_dm_resume_table(&io, partition.name, dm_fd);
-}
-
-static bool CreateDmDeviceForPartition(int dm_fd, const LogicalPartition& partition) {
- struct dm_ioctl io;
- if (!fs_mgr_dm_create_device(&io, partition.name, dm_fd)) {
- return false;
- }
- if (!LoadTablesAndActivate(dm_fd, partition)) {
- // Remove the device rather than leave it in an inactive state.
- fs_mgr_dm_destroy_device(&io, partition.name, dm_fd);
- return false;
- }
-
LINFO << "Created device-mapper device: " << partition.name;
return true;
}
bool CreateLogicalPartitions(const LogicalPartitionTable& table) {
- android::base::unique_fd dm_fd(open("/dev/device-mapper", O_RDWR));
- if (dm_fd < 0) {
- PLOG(ERROR) << "failed to open /dev/device-mapper";
- return false;
- }
+ DeviceMapper& dm = DeviceMapper::Instance();
for (const auto& partition : table.partitions) {
- if (!CreateDmDeviceForPartition(dm_fd, partition)) {
+ if (!CreateDmDeviceForPartition(dm, partition)) {
LOG(ERROR) << "could not create dm-linear device for partition: " << partition.name;
return false;
}
@@ -137,5 +77,59 @@
return nullptr;
}
+static bool CreateDmTable(const std::string& block_device, const LpMetadata& metadata,
+ const LpMetadataPartition& partition, DmTable* table) {
+ uint64_t sector = 0;
+ for (size_t i = 0; i < partition.num_extents; i++) {
+ const auto& extent = metadata.extents[partition.first_extent_index + i];
+ std::unique_ptr<DmTarget> target;
+ switch (extent.target_type) {
+ case LP_TARGET_TYPE_ZERO:
+ target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);
+ break;
+ case LP_TARGET_TYPE_LINEAR:
+ target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, block_device,
+ extent.target_data);
+ break;
+ default:
+ LOG(ERROR) << "Unknown target type in metadata: " << extent.target_type;
+ return false;
+ }
+ if (!table->AddTarget(std::move(target))) {
+ return false;
+ }
+ sector += extent.num_sectors;
+ }
+ if (partition.attributes & LP_PARTITION_ATTR_READONLY) {
+ table->set_readonly(true);
+ }
+ 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);
+ if (!metadata) {
+ LOG(ERROR) << "Could not read partition table.";
+ return true;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ for (const auto& partition : metadata->partitions) {
+ DmTable table;
+ if (!CreateDmTable(block_device, *metadata.get(), partition, &table)) {
+ return false;
+ }
+ std::string name = GetPartitionName(partition);
+ if (!dm.CreateDevice(name, table)) {
+ return false;
+ }
+ std::string path;
+ dm.GetDmDevicePathByName(partition.name, &path);
+ LINFO << "Created logical partition " << name << " on device " << path;
+ }
+ return true;
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index af4d6c1..a14dba3 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -679,7 +679,6 @@
// We can't call fs_mgr_free_fstab because a->recs still references the
// memory allocated by strdup.
free(b->recs);
- free(b->fstab_filename);
free(b);
a->num_entries = total_entries;
@@ -741,9 +740,7 @@
}
fstab = fs_mgr_read_fstab_file(fstab_file);
- if (fstab) {
- fstab->fstab_filename = strdup(fstab_path);
- } else {
+ if (!fstab) {
LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << fstab_path << "'";
}
@@ -854,9 +851,6 @@
/* Free the fstab_recs array created by calloc(3) */
free(fstab->recs);
- /* Free the fstab filename */
- free(fstab->fstab_filename);
-
/* Free fstab */
free(fstab);
}
diff --git a/fs_mgr/fs_mgr_priv_boot_config.h b/fs_mgr/fs_mgr_priv_boot_config.h
index d98dc02..417fb38 100644
--- a/fs_mgr/fs_mgr_priv_boot_config.h
+++ b/fs_mgr/fs_mgr_priv_boot_config.h
@@ -19,7 +19,13 @@
#include <sys/cdefs.h>
#include <string>
+#include <utility>
+#include <vector>
+std::vector<std::pair<std::string, std::string>> fs_mgr_parse_boot_config(const std::string& cmdline);
+
+bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& key,
+ std::string* out_val);
bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val);
bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index fe41f8a..5fb4ebb 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -35,6 +35,7 @@
#include <android-base/unique_fd.h>
#include <crypto_utils/android_pubkey.h>
#include <cutils/properties.h>
+#include <libdm/dm.h>
#include <logwrap/logwrap.h>
#include <openssl/obj_mac.h>
#include <openssl/rsa.h>
@@ -44,7 +45,6 @@
#include "fs_mgr.h"
#include "fs_mgr_priv.h"
-#include "fs_mgr_priv_dm_ioctl.h"
#define VERITY_TABLE_RSA_KEY "/verity_key"
#define VERITY_TABLE_HASH_IDX 8
@@ -250,48 +250,27 @@
return true;
}
-static int load_verity_table(struct dm_ioctl *io, const std::string &name,
- uint64_t device_size, int fd,
- const struct verity_table_params *params, format_verity_table_func format)
-{
- char *verity_params;
- char *buffer = (char*) io;
- size_t bufsize;
+static int load_verity_table(android::dm::DeviceMapper& dm, const std::string& name,
+ uint64_t device_size, const struct verity_table_params* params,
+ format_verity_table_func format) {
+ android::dm::DmTable table;
+ table.set_readonly(true);
- fs_mgr_dm_ioctl_init(io, DM_BUF_SIZE, name);
-
- struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
-
- // set tgt arguments
- io->target_count = 1;
- io->flags = DM_READONLY_FLAG;
- tgt->status = 0;
- tgt->sector_start = 0;
- tgt->length = device_size / 512;
- strcpy(tgt->target_type, "verity");
-
- // build the verity params
- verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
- bufsize = DM_BUF_SIZE - (verity_params - buffer);
-
- if (!format(verity_params, bufsize, params)) {
+ char buffer[DM_BUF_SIZE];
+ if (!format(buffer, sizeof(buffer), params)) {
LERROR << "Failed to format verity parameters";
return -1;
}
- LINFO << "loading verity table: '" << verity_params << "'";
-
- // set next target boundary
- verity_params += strlen(verity_params) + 1;
- verity_params = (char*)(((uintptr_t)verity_params + 7) & ~7);
- tgt->next = verity_params - buffer;
-
- // send the ioctl to load the verity table
- if (ioctl(fd, DM_TABLE_LOAD, io)) {
- PERROR << "Error loading verity table";
+ android::dm::DmTargetVerityString target(0, device_size / 512, buffer);
+ if (!table.AddTarget(std::make_unique<decltype(target)>(target))) {
+ LERROR << "Failed to add verity target";
return -1;
}
-
+ if (!dm.CreateDevice(name, table)) {
+ LERROR << "Failed to create verity device \"" << name << "\"";
+ return -1;
+ }
return 0;
}
@@ -761,11 +740,11 @@
struct fec_verity_metadata verity;
struct verity_table_params params = { .table = NULL };
- alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
- struct dm_ioctl *io = (struct dm_ioctl *) buffer;
const std::string mount_point(basename(fstab->mount_point));
bool verified_at_boot = false;
+ android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
+
if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
FEC_DEFAULT_ROOTS) < 0) {
PERROR << "Failed to open '" << fstab->blk_device << "'";
@@ -798,24 +777,6 @@
params.ecc_dev = fstab->blk_device;
- // get the device mapper fd
- if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
- PERROR << "Error opening device mapper";
- goto out;
- }
-
- // create the device
- if (!fs_mgr_dm_create_device(io, mount_point, fd)) {
- LERROR << "Couldn't create verity device!";
- goto out;
- }
-
- // get the name of the device file
- if (!fs_mgr_dm_get_device_name(io, mount_point, fd, &verity_blk_name)) {
- LERROR << "Couldn't get verity device number!";
- goto out;
- }
-
if (load_verity_state(fstab, ¶ms.mode) < 0) {
/* if accessing or updating the state failed, switch to the default
* safe mode. This makes sure the device won't end up in an endless
@@ -861,8 +822,7 @@
fstab->fs_mgr_flags & MF_SLOTSELECT);
// load the verity mapping table
- if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
- format_verity_table) == 0) {
+ if (load_verity_table(dm, mount_point, verity.data_size, ¶ms, format_verity_table) == 0) {
goto loaded;
}
@@ -871,15 +831,14 @@
LINFO << "Disabling error correction for " << mount_point.c_str();
params.ecc.valid = false;
- if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
- format_verity_table) == 0) {
+ if (load_verity_table(dm, mount_point, verity.data_size, ¶ms, format_verity_table) == 0) {
goto loaded;
}
}
// try the legacy format for backwards compatibility
- if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
- format_legacy_verity_table) == 0) {
+ if (load_verity_table(dm, mount_point, verity.data_size, ¶ms, format_legacy_verity_table) ==
+ 0) {
goto loaded;
}
@@ -888,8 +847,8 @@
LINFO << "Falling back to EIO mode for " << mount_point.c_str();
params.mode = VERITY_MODE_EIO;
- if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
- format_legacy_verity_table) == 0) {
+ if (load_verity_table(dm, mount_point, verity.data_size, ¶ms,
+ format_legacy_verity_table) == 0) {
goto loaded;
}
}
@@ -898,9 +857,8 @@
goto out;
loaded:
-
- // activate the device
- if (!fs_mgr_dm_resume_table(io, mount_point, fd)) {
+ if (!dm.GetDmDevicePathByName(mount_point, &verity_blk_name)) {
+ LERROR << "Couldn't get verity device number!";
goto out;
}
@@ -923,7 +881,7 @@
if (!verified_at_boot) {
free(fstab->blk_device);
fstab->blk_device = strdup(verity_blk_name.c_str());
- } else if (!fs_mgr_dm_destroy_device(io, mount_point, fd)) {
+ } else if (!dm.DeleteDevice(mount_point)) {
LERROR << "Failed to remove verity device " << mount_point.c_str();
goto out;
}
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 72f019e..c1b2ed9 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -70,7 +70,6 @@
char *tmp_mount_point);
int fs_mgr_do_mount_one(struct fstab_rec *rec);
int fs_mgr_do_tmpfs_mount(const char *n_name);
-int fs_mgr_unmount_all(struct fstab *fstab);
struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab);
void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size);
bool fs_mgr_load_verity_state(int* mode);
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index 7f928e5..3b0c791 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -26,53 +26,20 @@
#define __CORE_FS_MGR_DM_LINEAR_H
#include <stdint.h>
+
#include <memory>
#include <string>
#include <vector>
+#include <libdm/dm.h>
+#include <liblp/metadata_format.h>
+
namespace android {
namespace fs_mgr {
-static const uint32_t kPartitionReadonly = 0x1;
-
-class LogicalPartitionExtent {
- public:
- LogicalPartitionExtent() : logical_sector_(0), first_sector_(0), num_sectors_(0) {}
- LogicalPartitionExtent(uint64_t logical_sector, uint64_t first_sector, uint64_t num_sectors,
- const std::string& block_device)
- : logical_sector_(logical_sector),
- first_sector_(first_sector),
- num_sectors_(num_sectors),
- block_device_(block_device) {}
-
- // Return a string containing the dm_target_spec buffer needed to use this
- // extent in a device-mapper table.
- std::string Serialize() const;
-
- const std::string& block_device() const { return block_device_; }
-
- private:
- // Logical sector this extent represents in the presented block device.
- // This is equal to the previous extent's logical sector plus the number
- // of sectors in that extent. The first extent always starts at 0.
- uint64_t logical_sector_;
- // First 512-byte sector of this extent, on the source block device.
- uint64_t first_sector_;
- // Number of 512-byte sectors.
- uint64_t num_sectors_;
- // Target block device.
- std::string block_device_;
-};
-
struct LogicalPartition {
- LogicalPartition() : attributes(0), num_sectors(0) {}
-
std::string name;
- uint32_t attributes;
- // Number of 512-byte sectors total.
- uint64_t num_sectors;
- // List of extents.
- std::vector<LogicalPartitionExtent> extents;
+ std::vector<android::dm::DmTargetLinear> extents;
};
struct LogicalPartitionTable {
@@ -92,6 +59,7 @@
// /dev/block/dm-<name> where |name| is the partition name.
//
bool CreateLogicalPartitions(const LogicalPartitionTable& table);
+bool CreateLogicalPartitions(const std::string& block_device);
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index d232cca..b1ee328 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -33,7 +33,6 @@
struct fstab {
int num_entries;
struct fstab_rec* recs;
- char* fstab_filename;
};
struct fstab_rec {
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
new file mode 100644
index 0000000..22af123
--- /dev/null
+++ b/fs_mgr/libdm/Android.bp
@@ -0,0 +1,54 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_static {
+ name: "libdm",
+ defaults: ["fs_mgr_defaults"],
+ recovery_available: true,
+
+ export_include_dirs: ["include"],
+ cflags: [
+ // TODO(b/110035986): Allows us to create a skeleton of required classes
+ "-Wno-unused-private-field",
+ ],
+
+ srcs: [
+ "dm_table.cpp",
+ "dm_target.cpp",
+ "dm.cpp",
+ "loop_control.cpp",
+ ],
+
+ header_libs: [
+ "libbase_headers",
+ "liblog_headers",
+ ],
+}
+
+cc_test {
+ name: "libdm_test",
+ defaults: ["fs_mgr_defaults"],
+ static_libs: [
+ "libdm",
+ "libbase",
+ "liblog",
+ ],
+ srcs: [
+ "dm_test.cpp",
+ "loop_control_test.cpp",
+ "test_util.cpp",
+ ]
+}
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
new file mode 100644
index 0000000..b96f4c1
--- /dev/null
+++ b/fs_mgr/libdm/dm.cpp
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libdm/dm.h"
+
+#include <sys/ioctl.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace dm {
+
+DeviceMapper& DeviceMapper::Instance() {
+ static DeviceMapper instance;
+ return instance;
+}
+// Creates a new device mapper device
+bool DeviceMapper::CreateDevice(const std::string& name) {
+ if (name.empty()) {
+ LOG(ERROR) << "Unnamed device mapper device creation is not supported";
+ return false;
+ }
+
+ if (name.size() >= DM_NAME_LEN) {
+ LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
+ return false;
+ }
+
+ struct dm_ioctl io;
+ InitIo(&io, name);
+
+ if (ioctl(fd_, DM_DEV_CREATE, &io)) {
+ PLOG(ERROR) << "DM_DEV_CREATE failed for [" << name << "]";
+ return false;
+ }
+
+ // Check to make sure the newly created device doesn't already have targets
+ // added or opened by someone
+ CHECK(io.target_count == 0) << "Unexpected targets for newly created [" << name << "] device";
+ CHECK(io.open_count == 0) << "Unexpected opens for newly created [" << name << "] device";
+
+ // Creates a new device mapper device with the name passed in
+ return true;
+}
+
+bool DeviceMapper::DeleteDevice(const std::string& name) {
+ if (name.empty()) {
+ LOG(ERROR) << "Unnamed device mapper device creation is not supported";
+ return false;
+ }
+
+ if (name.size() >= DM_NAME_LEN) {
+ LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
+ return false;
+ }
+
+ struct dm_ioctl io;
+ InitIo(&io, name);
+
+ if (ioctl(fd_, DM_DEV_REMOVE, &io)) {
+ PLOG(ERROR) << "DM_DEV_REMOVE failed for [" << name << "]";
+ return false;
+ }
+
+ // Check to make sure appropriate uevent is generated so ueventd will
+ // do the right thing and remove the corresponding device node and symlinks.
+ CHECK(io.flags & DM_UEVENT_GENERATED_FLAG)
+ << "Didn't generate uevent for [" << name << "] removal";
+
+ return true;
+}
+
+const std::unique_ptr<DmTable> DeviceMapper::table(const std::string& /* name */) const {
+ // TODO(b/110035986): Return the table, as read from the kernel instead
+ return nullptr;
+}
+
+DmDeviceState DeviceMapper::state(const std::string& /* name */) const {
+ // TODO(b/110035986): Return the state, as read from the kernel instead
+ return DmDeviceState::INVALID;
+}
+
+bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
+ if (!CreateDevice(name)) {
+ return false;
+ }
+ if (!LoadTableAndActivate(name, table)) {
+ DeleteDevice(name);
+ return false;
+ }
+ return true;
+}
+
+bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& table) {
+ std::string ioctl_buffer(sizeof(struct dm_ioctl), 0);
+ ioctl_buffer += table.Serialize();
+
+ struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(&ioctl_buffer[0]);
+ InitIo(io, name);
+ io->data_size = ioctl_buffer.size();
+ io->data_start = sizeof(struct dm_ioctl);
+ io->target_count = static_cast<uint32_t>(table.num_targets());
+ if (table.readonly()) {
+ io->flags |= DM_READONLY_FLAG;
+ }
+ if (ioctl(fd_, DM_TABLE_LOAD, io)) {
+ PLOG(ERROR) << "DM_TABLE_LOAD failed";
+ return false;
+ }
+
+ InitIo(io, name);
+ if (ioctl(fd_, DM_DEV_SUSPEND, io)) {
+ PLOG(ERROR) << "DM_TABLE_SUSPEND resume failed";
+ return false;
+ }
+ return true;
+}
+
+// Reads all the available device mapper targets and their corresponding
+// versions from the kernel and returns in a vector
+bool DeviceMapper::GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets) {
+ targets->clear();
+
+ // calculate the space needed to read a maximum of kMaxPossibleDmTargets
+ uint32_t payload_size = sizeof(struct dm_target_versions);
+ payload_size += DM_MAX_TYPE_NAME;
+ // device mapper wants every target spec to be aligned at 8-byte boundary
+ payload_size = DM_ALIGN(payload_size);
+ payload_size *= kMaxPossibleDmTargets;
+
+ uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
+ auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
+ if (buffer == nullptr) {
+ LOG(ERROR) << "failed to allocate memory";
+ return false;
+ }
+
+ // Sets appropriate data size and data_start to make sure we tell kernel
+ // about the total size of the buffer we are passing and where to start
+ // writing the list of targets.
+ struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
+ InitIo(io);
+ io->data_size = data_size;
+ io->data_start = sizeof(*io);
+
+ if (ioctl(fd_, DM_LIST_VERSIONS, io)) {
+ PLOG(ERROR) << "DM_LIST_VERSIONS failed";
+ return false;
+ }
+
+ // If the provided buffer wasn't enough to list all targets, note that
+ // any data beyond sizeof(*io) must not be read in this case
+ if (io->flags & DM_BUFFER_FULL_FLAG) {
+ LOG(INFO) << data_size << " is not enough memory to list all dm targets";
+ return false;
+ }
+
+ // if there are no targets registered, return success with empty vector
+ if (io->data_size == sizeof(*io)) {
+ return true;
+ }
+
+ // Parse each target and list the name and version
+ // TODO(b/110035986): Templatize this
+ uint32_t next = sizeof(*io);
+ data_size = io->data_size - next;
+ struct dm_target_versions* vers =
+ reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
+ while (next && data_size) {
+ targets->emplace_back(vers);
+ if (vers->next == 0) {
+ break;
+ }
+ next += vers->next;
+ data_size -= vers->next;
+ vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
+ }
+
+ return true;
+}
+
+bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
+ devices->clear();
+
+ // calculate the space needed to read a maximum of 256 targets, each with
+ // name with maximum length of 16 bytes
+ uint32_t payload_size = sizeof(struct dm_name_list);
+ // 128-bytes for the name
+ payload_size += DM_NAME_LEN;
+ // dm wants every device spec to be aligned at 8-byte boundary
+ payload_size = DM_ALIGN(payload_size);
+ payload_size *= kMaxPossibleDmDevices;
+ uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
+ auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
+ if (buffer == nullptr) {
+ LOG(ERROR) << "failed to allocate memory";
+ return false;
+ }
+
+ // Sets appropriate data size and data_start to make sure we tell kernel
+ // about the total size of the buffer we are passing and where to start
+ // writing the list of targets.
+ struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
+ InitIo(io);
+ io->data_size = data_size;
+ io->data_start = sizeof(*io);
+
+ if (ioctl(fd_, DM_LIST_DEVICES, io)) {
+ PLOG(ERROR) << "DM_LIST_DEVICES failed";
+ return false;
+ }
+
+ // If the provided buffer wasn't enough to list all devices any data
+ // beyond sizeof(*io) must not be read.
+ if (io->flags & DM_BUFFER_FULL_FLAG) {
+ LOG(INFO) << data_size << " is not enough memory to list all dm devices";
+ return false;
+ }
+
+ // if there are no devices created yet, return success with empty vector
+ if (io->data_size == sizeof(*io)) {
+ return true;
+ }
+
+ // Parse each device and add a new DmBlockDevice to the vector
+ // created from the kernel data.
+ uint32_t next = sizeof(*io);
+ data_size = io->data_size - next;
+ struct dm_name_list* dm_dev =
+ reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
+
+ while (next && data_size) {
+ devices->emplace_back((dm_dev));
+ if (dm_dev->next == 0) {
+ break;
+ }
+ next += dm_dev->next;
+ data_size -= dm_dev->next;
+ dm_dev = reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
+ }
+
+ return true;
+}
+
+// Accepts a device mapper device name (like system_a, vendor_b etc) and
+// returns the path to it's device node (or symlink to the device node)
+bool DeviceMapper::GetDmDevicePathByName(const std::string& name, std::string* path) {
+ struct dm_ioctl io;
+ InitIo(&io, name);
+ if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+ PLOG(ERROR) << "DM_DEV_STATUS failed for " << name;
+ return false;
+ }
+
+ uint32_t dev_num = minor(io.dev);
+ *path = "/dev/block/dm-" + std::to_string(dev_num);
+ return true;
+}
+
+// private methods of DeviceMapper
+void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {
+ CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization";
+ memset(io, 0, sizeof(*io));
+
+ io->version[0] = DM_VERSION0;
+ io->version[1] = DM_VERSION1;
+ io->version[2] = DM_VERSION2;
+ io->data_size = sizeof(*io);
+ io->data_start = 0;
+ if (!name.empty()) {
+ strlcpy(io->name, name.c_str(), sizeof(io->name));
+ }
+}
+
+} // namespace dm
+} // namespace android
diff --git a/fs_mgr/libdm/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp
new file mode 100644
index 0000000..15c7ce1
--- /dev/null
+++ b/fs_mgr/libdm/dm_table.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libdm/dm_table.h"
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+
+namespace android {
+namespace dm {
+
+bool DmTable::AddTarget(std::unique_ptr<DmTarget>&& target) {
+ if (!target->Valid()) {
+ return false;
+ }
+ targets_.push_back(std::move(target));
+ return true;
+}
+
+bool DmTable::RemoveTarget(std::unique_ptr<DmTarget>&& /* target */) {
+ return true;
+}
+
+bool DmTable::valid() const {
+ if (targets_.empty()) {
+ LOG(ERROR) << "Device-mapper table must have at least one target.";
+ return "";
+ }
+ if (targets_[0]->start() != 0) {
+ LOG(ERROR) << "Device-mapper table must start at logical sector 0.";
+ return "";
+ }
+ return true;
+}
+
+uint64_t DmTable::num_sectors() const {
+ return valid() ? num_sectors_ : 0;
+}
+
+// Returns a string representation of the table that is ready to be passed
+// down to the kernel for loading.
+//
+// Implementation must verify there are no gaps in the table, table starts
+// with sector == 0, and iterate over each target to get its table
+// serialized.
+std::string DmTable::Serialize() const {
+ if (!valid()) {
+ return "";
+ }
+
+ std::string table;
+ for (const auto& target : targets_) {
+ table += target->Serialize();
+ }
+ return table;
+}
+
+} // namespace dm
+} // namespace android
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
new file mode 100644
index 0000000..20b26df
--- /dev/null
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libdm/dm_target.h"
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+
+#include <libdm/dm.h>
+
+namespace android {
+namespace dm {
+
+std::string DmTarget::Serialize() const {
+ // Create a string containing a dm_target_spec, parameter data, and an
+ // explicit null terminator.
+ std::string data(sizeof(dm_target_spec), '\0');
+ data += GetParameterString();
+ data.push_back('\0');
+
+ // The kernel expects each target to be 8-byte aligned.
+ size_t padding = DM_ALIGN(data.size()) - data.size();
+ for (size_t i = 0; i < padding; i++) {
+ data.push_back('\0');
+ }
+
+ // Finally fill in the dm_target_spec.
+ struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&data[0]);
+ spec->sector_start = start();
+ spec->length = size();
+ strlcpy(spec->target_type, name().c_str(), sizeof(spec->target_type));
+ spec->next = (uint32_t)data.size();
+ return data;
+}
+
+std::string DmTargetZero::GetParameterString() const {
+ // The zero target type has no additional parameters.
+ return "";
+}
+
+std::string DmTargetLinear::GetParameterString() const {
+ return block_device_ + " " + std::to_string(physical_sector_);
+}
+
+DmTargetVerity::DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,
+ const std::string& block_device, const std::string& hash_device,
+ uint32_t data_block_size, uint32_t hash_block_size,
+ uint32_t num_data_blocks, uint32_t hash_start_block,
+ const std::string& hash_algorithm, const std::string& root_digest,
+ const std::string& salt)
+ : DmTarget(start, length), valid_(true) {
+ base_args_ = {
+ std::to_string(version),
+ block_device,
+ hash_device,
+ std::to_string(data_block_size),
+ std::to_string(hash_block_size),
+ std::to_string(num_data_blocks),
+ std::to_string(hash_start_block),
+ hash_algorithm,
+ root_digest,
+ salt,
+ };
+}
+
+void DmTargetVerity::UseFec(const std::string& device, uint32_t num_roots, uint32_t num_blocks,
+ uint32_t start) {
+ optional_args_.emplace_back("use_fec_from_device");
+ optional_args_.emplace_back(device);
+ optional_args_.emplace_back("fec_roots");
+ optional_args_.emplace_back(std::to_string(num_roots));
+ optional_args_.emplace_back("fec_blocks");
+ optional_args_.emplace_back(std::to_string(num_blocks));
+ optional_args_.emplace_back("fec_start");
+ optional_args_.emplace_back(std::to_string(start));
+}
+
+void DmTargetVerity::SetVerityMode(const std::string& mode) {
+ if (mode != "restart_on_corruption" && mode != "ignore_corruption") {
+ LOG(ERROR) << "Unknown verity mode: " << mode;
+ valid_ = false;
+ return;
+ }
+ optional_args_.emplace_back(mode);
+}
+
+void DmTargetVerity::IgnoreZeroBlocks() {
+ optional_args_.emplace_back("ignore_zero_blocks");
+}
+
+std::string DmTargetVerity::GetParameterString() const {
+ std::string base = android::base::Join(base_args_, " ");
+ if (optional_args_.empty()) {
+ return base;
+ }
+ std::string optional = android::base::Join(optional_args_, " ");
+ return base + " " + std::to_string(optional_args_.size()) + " " + optional;
+}
+
+} // namespace dm
+} // namespace android
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
new file mode 100644
index 0000000..85f8e4a
--- /dev/null
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <ctime>
+#include <map>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <libdm/dm.h>
+#include <libdm/loop_control.h>
+#include "test_util.h"
+
+using namespace std;
+using namespace android::dm;
+using unique_fd = android::base::unique_fd;
+
+TEST(libdm, HasMinimumTargets) {
+ DeviceMapper& dm = DeviceMapper::Instance();
+ vector<DmTargetTypeInfo> targets;
+ ASSERT_TRUE(dm.GetAvailableTargets(&targets));
+
+ map<string, DmTargetTypeInfo> by_name;
+ for (const auto& target : targets) {
+ by_name[target.name()] = target;
+ }
+
+ auto iter = by_name.find("linear");
+ EXPECT_NE(iter, by_name.end());
+}
+
+// Helper to ensure that device mapper devices are released.
+class TempDevice {
+ public:
+ TempDevice(const std::string& name, const DmTable& table)
+ : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
+ valid_ = dm_.CreateDevice(name, table);
+ }
+ TempDevice(TempDevice&& other) : dm_(other.dm_), name_(other.name_), valid_(other.valid_) {
+ other.valid_ = false;
+ }
+ ~TempDevice() {
+ if (valid_) {
+ dm_.DeleteDevice(name_);
+ }
+ }
+ bool Destroy() {
+ if (!valid_) {
+ return false;
+ }
+ valid_ = false;
+ return dm_.DeleteDevice(name_);
+ }
+ bool WaitForUdev() const {
+ auto start_time = std::chrono::steady_clock::now();
+ while (true) {
+ if (!access(path().c_str(), F_OK)) {
+ return true;
+ }
+ if (errno != ENOENT) {
+ return false;
+ }
+ std::this_thread::sleep_for(50ms);
+ std::chrono::duration elapsed = std::chrono::steady_clock::now() - start_time;
+ if (elapsed >= 5s) {
+ return false;
+ }
+ }
+ }
+ std::string path() const {
+ std::string device_path;
+ if (!dm_.GetDmDevicePathByName(name_, &device_path)) {
+ return "";
+ }
+ return device_path;
+ }
+ const std::string& name() const { return name_; }
+ bool valid() const { return valid_; }
+
+ TempDevice(const TempDevice&) = delete;
+ TempDevice& operator=(const TempDevice&) = delete;
+
+ TempDevice& operator=(TempDevice&& other) {
+ name_ = other.name_;
+ valid_ = other.valid_;
+ other.valid_ = false;
+ return *this;
+ }
+
+ private:
+ DeviceMapper& dm_;
+ std::string name_;
+ bool valid_;
+};
+
+TEST(libdm, DmLinear) {
+ unique_fd tmp1(CreateTempFile("file_1", 4096));
+ ASSERT_GE(tmp1, 0);
+ unique_fd tmp2(CreateTempFile("file_2", 4096));
+ ASSERT_GE(tmp2, 0);
+
+ // Create two different files. These will back two separate loop devices.
+ const char message1[] = "Hello! This is sector 1.";
+ const char message2[] = "Goodbye. This is sector 2.";
+ ASSERT_TRUE(android::base::WriteFully(tmp1, message1, sizeof(message1)));
+ ASSERT_TRUE(android::base::WriteFully(tmp2, message2, sizeof(message2)));
+
+ LoopDevice loop_a(tmp1);
+ ASSERT_TRUE(loop_a.valid());
+ LoopDevice loop_b(tmp2);
+ ASSERT_TRUE(loop_b.valid());
+
+ // Define a 2-sector device, with each sector mapping to the first sector
+ // of one of our loop devices.
+ DmTable table;
+ ASSERT_TRUE(table.AddTarget(make_unique<DmTargetLinear>(0, 1, loop_a.device(), 0)));
+ ASSERT_TRUE(table.AddTarget(make_unique<DmTargetLinear>(1, 1, loop_b.device(), 0)));
+ ASSERT_TRUE(table.valid());
+
+ TempDevice dev("libdm-test-dm-linear", table);
+ ASSERT_TRUE(dev.valid());
+ ASSERT_FALSE(dev.path().empty());
+ ASSERT_TRUE(dev.WaitForUdev());
+
+ // Note: a scope is needed to ensure that there are no open descriptors
+ // when we go to close the device.
+ {
+ unique_fd dev_fd(open(dev.path().c_str(), O_RDWR));
+ ASSERT_GE(dev_fd, 0);
+
+ // Test that each sector of our device is correctly mapped to each loop
+ // device.
+ char sector[512];
+ ASSERT_TRUE(android::base::ReadFully(dev_fd, sector, sizeof(sector)));
+ ASSERT_EQ(strncmp(sector, message1, sizeof(message1)), 0);
+ ASSERT_TRUE(android::base::ReadFully(dev_fd, sector, sizeof(sector)));
+ ASSERT_EQ(strncmp(sector, message2, sizeof(message2)), 0);
+ }
+
+ // Normally the TestDevice destructor would delete this, but at least one
+ // test should ensure that device deletion works.
+ ASSERT_TRUE(dev.Destroy());
+}
+
+TEST(libdm, DmVerityArgsAvb2) {
+ std::string device = "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a";
+ std::string algorithm = "sha1";
+ std::string digest = "4be7e823b8c40f7bd5c8ccd5123f0722c5baca21";
+ std::string salt = "cc99f81ecb9484220a003b0719ee59dcf9be7e5d";
+
+ DmTargetVerity target(0, 10000, 1, device, device, 4096, 4096, 125961, 125961, algorithm,
+ digest, salt);
+ target.UseFec(device, 2, 126955, 126955);
+ target.SetVerityMode("restart_on_corruption");
+ target.IgnoreZeroBlocks();
+
+ // Verity table from a walleye build.
+ std::string expected =
+ "1 /dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a "
+ "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a 4096 4096 125961 125961 sha1 "
+ "4be7e823b8c40f7bd5c8ccd5123f0722c5baca21 cc99f81ecb9484220a003b0719ee59dcf9be7e5d 10 "
+ "use_fec_from_device /dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a fec_roots "
+ "2 fec_blocks 126955 fec_start 126955 restart_on_corruption ignore_zero_blocks";
+ EXPECT_EQ(target.GetParameterString(), expected);
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
new file mode 100644
index 0000000..60bceed
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2018 Google, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBDM_DM_H_
+#define _LIBDM_DM_H_
+
+#include <fcntl.h>
+#include <linux/dm-ioctl.h>
+#include <linux/kdev_t.h>
+#include <stdint.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+
+#include "dm_table.h"
+
+// The minimum expected device mapper major.minor version
+#define DM_VERSION0 (4)
+#define DM_VERSION1 (0)
+#define DM_VERSION2 (0)
+
+#define DM_ALIGN_MASK (7)
+#define DM_ALIGN(x) ((x + DM_ALIGN_MASK) & ~DM_ALIGN_MASK)
+
+namespace android {
+namespace dm {
+
+enum class DmDeviceState { INVALID, SUSPENDED, ACTIVE };
+
+class DeviceMapper final {
+ public:
+ class DmBlockDevice final {
+ public:
+ // only allow creating this with dm_name_list
+ DmBlockDevice() = delete;
+
+ explicit DmBlockDevice(struct dm_name_list* d) : name_(d->name), dev_(d->dev){};
+
+ // Returs device mapper name associated with the block device
+ const std::string& name() const { return name_; }
+
+ // Return major number for the block device
+ uint32_t Major() const { return major(dev_); }
+
+ // Return minor number for the block device
+ uint32_t Minor() const { return minor(dev_); }
+ ~DmBlockDevice() = default;
+
+ private:
+ std::string name_;
+ uint64_t dev_;
+ };
+
+ // Removes a device mapper device with the given name.
+ // Returns 'true' on success, false otherwise.
+ bool DeleteDevice(const std::string& name);
+
+ // Reads the device mapper table from the device with given anme and
+ // returns it in a DmTable object.
+ const std::unique_ptr<DmTable> table(const std::string& name) const;
+
+ // Returns the current state of the underlying device mapper device
+ // with given name.
+ // One of INVALID, SUSPENDED or ACTIVE.
+ DmDeviceState state(const std::string& name) const;
+
+ // Creates a device, loads the given table, and activates it. If the device
+ // is not able to be activated, it is destroyed, and false is returned.
+ bool CreateDevice(const std::string& name, const DmTable& table);
+
+ // Loads the device mapper table from parameter into the underlying device
+ // mapper device with given name and activate / resumes the device in the
+ // process. A device with the given name must already exist.
+ //
+ // Returns 'true' on success, false otherwise.
+ bool LoadTableAndActivate(const std::string& name, const DmTable& table);
+
+ // Returns true if a list of available device mapper targets registered in the kernel was
+ // successfully read and stored in 'targets'. Returns 'false' otherwise.
+ bool GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets);
+
+ // Return 'true' if it can successfully read the list of device mapper block devices
+ // currently created. 'devices' will be empty if the kernel interactions
+ // were successful and there are no block devices at the moment. Returns
+ // 'false' in case of any failure along the way.
+ bool GetAvailableDevices(std::vector<DmBlockDevice>* devices);
+
+ // Returns the path to the device mapper device node in '/dev' corresponding to
+ // 'name'. If the device does not exist, false is returned, and the path
+ // parameter is not set.
+ bool GetDmDevicePathByName(const std::string& name, std::string* path);
+
+ // The only way to create a DeviceMapper object.
+ static DeviceMapper& Instance();
+
+ ~DeviceMapper() {
+ if (fd_ != -1) {
+ ::close(fd_);
+ }
+ }
+
+ private:
+ // Maximum possible device mapper targets registered in the kernel.
+ // This is only used to read the list of targets from kernel so we allocate
+ // a finite amount of memory. This limit is in no way enforced by the kernel.
+ static constexpr uint32_t kMaxPossibleDmTargets = 256;
+
+ // Maximum possible device mapper created block devices. Note that this is restricted by
+ // the minor numbers (that used to be 8 bits) that can be range from 0 to 2^20-1 in newer
+ // kernels. In Android systems however, we never expect these to grow beyond the artificial
+ // limit we are imposing here of 256.
+ static constexpr uint32_t kMaxPossibleDmDevices = 256;
+
+ void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const;
+
+ DeviceMapper() : fd_(-1) {
+ fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
+ if (fd_ < 0) {
+ PLOG(ERROR) << "Failed to open device-mapper";
+ }
+ }
+
+ // Creates a device mapper device with given name.
+ // Return 'true' on success and 'false' on failure to
+ // create OR if a device mapper device with the same name already
+ // exists.
+ bool CreateDevice(const std::string& name);
+
+ int fd_;
+ // Non-copyable & Non-movable
+ DeviceMapper(const DeviceMapper&) = delete;
+ DeviceMapper& operator=(const DeviceMapper&) = delete;
+ DeviceMapper& operator=(DeviceMapper&&) = delete;
+ DeviceMapper(DeviceMapper&&) = delete;
+};
+
+} // namespace dm
+} // namespace android
+
+#endif /* _LIBDM_DM_H_ */
diff --git a/fs_mgr/libdm/include/libdm/dm_table.h b/fs_mgr/libdm/include/libdm/dm_table.h
new file mode 100644
index 0000000..5c639be
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/dm_table.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2018 Google, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBDM_DMTABLE_H_
+#define _LIBDM_DMTABLE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "dm_target.h"
+
+namespace android {
+namespace dm {
+
+class DmTable {
+ public:
+ DmTable() : num_sectors_(0), readonly_(false) {}
+
+ // Adds a target to the device mapper table for a range specified in the target object.
+ // The function will return 'true' if the target was successfully added and doesn't overlap with
+ // any of the existing targets in the table. Gaps are allowed. The final check, including
+ // overlaps and gaps are done before loading the table. Returns 'false' on failure.
+ bool AddTarget(std::unique_ptr<DmTarget>&& target);
+
+ // Removes a target from the table for the range specified in the target object. Returns 'false'
+ // if the target name doesn't match with the one in the table. Returns 'true' if target is
+ // successfully removed.
+ bool RemoveTarget(std::unique_ptr<DmTarget>&& target);
+
+ // Checks the table to make sure it is valid. i.e. Checks for range overlaps, range gaps
+ // and returns 'true' if the table is ready to be loaded into kernel. Returns 'false' if the
+ // table is malformed.
+ bool valid() const;
+
+ // Returns the toatl number of targets.
+ size_t num_targets() const { return targets_.size(); }
+
+ // Returns the total size represented by the table in terms of number of 512-byte sectors.
+ // NOTE: This function will overlook if there are any gaps in the targets added in the table.
+ uint64_t num_sectors() const;
+
+ // Returns the string represntation of the table that is ready to be passed into the kernel
+ // as part of the DM_TABLE_LOAD ioctl.
+ std::string Serialize() const;
+
+ void set_readonly(bool readonly) { readonly_ = readonly; }
+ bool readonly() const { return readonly_; }
+
+ ~DmTable() = default;
+
+ private:
+ // list of targets defined in this table sorted by
+ // their start and end sectors.
+ // Note: Overlapping targets MUST never be added in this list.
+ std::vector<std::unique_ptr<DmTarget>> targets_;
+
+ // Total size in terms of # of sectors, as calculated by looking at the last and the first
+ // target in 'target_'.
+ uint64_t num_sectors_;
+
+ // True if the device should be read-only; false otherwise.
+ bool readonly_;
+};
+
+} // namespace dm
+} // namespace android
+
+#endif /* _LIBDM_DMTABLE_H_ */
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
new file mode 100644
index 0000000..d5974f4
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2018 Google, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBDM_DMTARGET_H_
+#define _LIBDM_DMTARGET_H_
+
+#include <linux/dm-ioctl.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace dm {
+
+class DmTargetTypeInfo {
+ public:
+ DmTargetTypeInfo() : major_(0), minor_(0), patch_(0) {}
+ DmTargetTypeInfo(const struct dm_target_versions* info)
+ : name_(info->name),
+ major_(info->version[0]),
+ minor_(info->version[1]),
+ patch_(info->version[2]) {}
+
+ const std::string& name() const { return name_; }
+ std::string version() const {
+ return std::to_string(major_) + "." + std::to_string(minor_) + "." + std::to_string(patch_);
+ }
+
+ private:
+ std::string name_;
+ uint32_t major_;
+ uint32_t minor_;
+ uint32_t patch_;
+};
+
+class DmTarget {
+ public:
+ DmTarget(uint64_t start, uint64_t length) : start_(start), length_(length) {}
+
+ virtual ~DmTarget() = default;
+
+ // Returns name of the target.
+ virtual std::string name() const = 0;
+
+ // Return the first logical sector represented by this target.
+ uint64_t start() const { return start_; }
+
+ // Returns size in number of sectors when this target is part of
+ // a DmTable, return 0 otherwise.
+ uint64_t size() const { return length_; }
+
+ // Function that converts this object to a string of arguments that can
+ // be passed to the kernel for adding this target in a table. Each target (e.g. verity, linear)
+ // must implement this, for it to be used on a device.
+ std::string Serialize() const;
+
+ virtual bool Valid() const { return true; }
+
+ protected:
+ // Get the parameter string that is passed to the end of the dm_target_spec
+ // for this target type.
+ virtual std::string GetParameterString() const = 0;
+
+ private:
+ // logical sector number start and total length (in terms of 512-byte sectors) represented
+ // by this target within a DmTable.
+ uint64_t start_, length_;
+};
+
+class DmTargetZero final : public DmTarget {
+ public:
+ DmTargetZero(uint64_t start, uint64_t length) : DmTarget(start, length) {}
+
+ std::string name() const override { return "zero"; }
+ std::string GetParameterString() const override;
+};
+
+class DmTargetLinear final : public DmTarget {
+ public:
+ DmTargetLinear(uint64_t start, uint64_t length, const std::string& block_device,
+ uint64_t physical_sector)
+ : DmTarget(start, length), block_device_(block_device), physical_sector_(physical_sector) {}
+
+ std::string name() const override { return "linear"; }
+ std::string GetParameterString() const override;
+ const std::string& block_device() const { return block_device_; }
+
+ private:
+ std::string block_device_;
+ uint64_t physical_sector_;
+};
+
+class DmTargetVerity final : public DmTarget {
+ public:
+ DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,
+ const std::string& block_device, const std::string& hash_device,
+ uint32_t data_block_size, uint32_t hash_block_size, uint32_t num_data_blocks,
+ uint32_t hash_start_block, const std::string& hash_algorithm,
+ const std::string& root_digest, const std::string& salt);
+
+ void UseFec(const std::string& device, uint32_t num_roots, uint32_t num_blocks, uint32_t start);
+ void SetVerityMode(const std::string& mode);
+ void IgnoreZeroBlocks();
+
+ std::string name() const override { return "verity"; }
+ std::string GetParameterString() const override;
+ bool Valid() const override { return valid_; }
+
+ private:
+ std::vector<std::string> base_args_;
+ std::vector<std::string> optional_args_;
+ bool valid_;
+};
+
+// This is the same as DmTargetVerity, but the table may be specified as a raw
+// string. This code exists only for fs_mgr_verity and should be avoided. Use
+// DmTargetVerity for new code instead.
+class DmTargetVerityString final : public DmTarget {
+ public:
+ DmTargetVerityString(uint64_t start, uint64_t length, const std::string& target_string)
+ : DmTarget(start, length), target_string_(target_string) {}
+
+ std::string name() const override { return "verity"; }
+ std::string GetParameterString() const override { return target_string_; }
+ bool Valid() const override { return true; }
+
+ private:
+ std::string target_string_;
+};
+
+} // namespace dm
+} // namespace android
+
+#endif /* _LIBDM_DMTARGET_H_ */
diff --git a/fs_mgr/libdm/include/libdm/loop_control.h b/fs_mgr/libdm/include/libdm/loop_control.h
new file mode 100644
index 0000000..e6e83f4
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/loop_control.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2018 Google, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBDM_LOOP_CONTROL_H_
+#define _LIBDM_LOOP_CONTROL_H_
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace dm {
+
+class LoopControl final {
+ public:
+ LoopControl();
+
+ // Attaches the file specified by 'file_fd' to the loop device specified
+ // by 'loopdev'
+ bool Attach(int file_fd, std::string* loopdev) const;
+
+ // Detach the loop device given by 'loopdev' from the attached backing file.
+ bool Detach(const std::string& loopdev) const;
+
+ LoopControl(const LoopControl&) = delete;
+ LoopControl& operator=(const LoopControl&) = delete;
+ LoopControl& operator=(LoopControl&&) = default;
+ LoopControl(LoopControl&&) = default;
+
+ private:
+ bool FindFreeLoopDevice(std::string* loopdev) const;
+
+ static constexpr const char* kLoopControlDevice = "/dev/loop-control";
+
+ android::base::unique_fd control_fd_;
+};
+
+// Create a temporary loop device around a file descriptor or path.
+class LoopDevice {
+ public:
+ // Create a loop device for the given file descriptor. It is closed when
+ // LoopDevice is destroyed only if auto_close is true.
+ LoopDevice(int fd, bool auto_close = false);
+ // Create a loop device for the given file path. It will be opened for
+ // reading and writing and closed when the loop device is detached.
+ explicit LoopDevice(const std::string& path);
+ ~LoopDevice();
+
+ bool valid() const { return fd_ != -1 && !device_.empty(); }
+ const std::string& device() const { return device_; }
+
+ LoopDevice(const LoopDevice&) = delete;
+ LoopDevice& operator=(const LoopDevice&) = delete;
+ LoopDevice& operator=(LoopDevice&&) = default;
+ LoopDevice(LoopDevice&&) = default;
+
+ private:
+ void Init();
+
+ android::base::unique_fd fd_;
+ bool owns_fd_;
+ std::string device_;
+ LoopControl control_;
+};
+
+} // namespace dm
+} // namespace android
+
+#endif /* _LIBDM_LOOP_CONTROL_H_ */
diff --git a/fs_mgr/libdm/loop_control.cpp b/fs_mgr/libdm/loop_control.cpp
new file mode 100644
index 0000000..0beb1a6
--- /dev/null
+++ b/fs_mgr/libdm/loop_control.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libdm/loop_control.h"
+
+#include <fcntl.h>
+#include <linux/loop.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace dm {
+
+LoopControl::LoopControl() : control_fd_(-1) {
+ control_fd_.reset(TEMP_FAILURE_RETRY(open(kLoopControlDevice, O_RDWR | O_CLOEXEC)));
+ if (control_fd_ < 0) {
+ PLOG(ERROR) << "Failed to open loop-control";
+ }
+}
+
+bool LoopControl::Attach(int file_fd, std::string* loopdev) const {
+ if (!FindFreeLoopDevice(loopdev)) {
+ LOG(ERROR) << "Failed to attach, no free loop devices";
+ return false;
+ }
+
+ android::base::unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loopdev->c_str(), O_RDWR | O_CLOEXEC)));
+ if (loop_fd < 0) {
+ PLOG(ERROR) << "Failed to open: " << *loopdev;
+ return false;
+ }
+
+ int rc = ioctl(loop_fd, LOOP_SET_FD, file_fd);
+ if (rc < 0) {
+ PLOG(ERROR) << "Failed LOOP_SET_FD";
+ return false;
+ }
+ return true;
+}
+
+bool LoopControl::Detach(const std::string& loopdev) const {
+ if (loopdev.empty()) {
+ LOG(ERROR) << "Must provide a loop device";
+ return false;
+ }
+
+ android::base::unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loopdev.c_str(), O_RDWR | O_CLOEXEC)));
+ if (loop_fd < 0) {
+ PLOG(ERROR) << "Failed to open: " << loopdev;
+ return false;
+ }
+
+ int rc = ioctl(loop_fd, LOOP_CLR_FD, 0);
+ if (rc) {
+ PLOG(ERROR) << "Failed LOOP_CLR_FD for '" << loopdev << "'";
+ return false;
+ }
+ return true;
+}
+
+bool LoopControl::FindFreeLoopDevice(std::string* loopdev) const {
+ int rc = ioctl(control_fd_, LOOP_CTL_GET_FREE);
+ if (rc < 0) {
+ PLOG(ERROR) << "Failed to get free loop device";
+ return false;
+ }
+
+ // Ueventd on android creates all loop devices as /dev/block/loopX
+ // The total number of available devices is determined by 'loop.max_part'
+ // kernel command line argument.
+ *loopdev = ::android::base::StringPrintf("/dev/block/loop%d", rc);
+ return true;
+}
+
+LoopDevice::LoopDevice(int fd, bool auto_close) : fd_(fd), owns_fd_(auto_close) {
+ Init();
+}
+
+LoopDevice::LoopDevice(const std::string& path) : fd_(-1), owns_fd_(true) {
+ fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC));
+ if (fd_ < -1) {
+ PLOG(ERROR) << "open failed for " << path;
+ return;
+ }
+ Init();
+}
+
+LoopDevice::~LoopDevice() {
+ if (valid()) {
+ control_.Detach(device_);
+ }
+ if (!owns_fd_) {
+ (void)fd_.release();
+ }
+}
+
+void LoopDevice::Init() {
+ control_.Attach(fd_, &device_);
+}
+
+} // namespace dm
+} // namespace android
diff --git a/fs_mgr/libdm/loop_control_test.cpp b/fs_mgr/libdm/loop_control_test.cpp
new file mode 100644
index 0000000..08bdc00
--- /dev/null
+++ b/fs_mgr/libdm/loop_control_test.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libdm/loop_control.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include "test_util.h"
+
+using namespace std;
+using namespace android::dm;
+using unique_fd = android::base::unique_fd;
+
+static unique_fd TempFile() {
+ // A loop device needs to be at least one sector to actually work, so fill
+ // up the file with a message.
+ unique_fd fd(CreateTempFile("temp", 0));
+ if (fd < 0) {
+ return {};
+ }
+ char buffer[] = "Hello";
+ for (size_t i = 0; i < 1000; i++) {
+ if (!android::base::WriteFully(fd, buffer, sizeof(buffer))) {
+ perror("write");
+ return {};
+ }
+ }
+ return fd;
+}
+
+TEST(libdm, LoopControl) {
+ unique_fd fd = TempFile();
+ ASSERT_GE(fd, 0);
+
+ LoopDevice loop(fd);
+ ASSERT_TRUE(loop.valid());
+
+ char buffer[6];
+ unique_fd loop_fd(open(loop.device().c_str(), O_RDWR));
+ ASSERT_GE(loop_fd, 0);
+ ASSERT_TRUE(android::base::ReadFully(loop_fd, buffer, sizeof(buffer)));
+ ASSERT_EQ(memcmp(buffer, "Hello", 6), 0);
+}
diff --git a/fs_mgr/libdm/test_util.cpp b/fs_mgr/libdm/test_util.cpp
new file mode 100644
index 0000000..307251c
--- /dev/null
+++ b/fs_mgr/libdm/test_util.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <linux/memfd.h>
+#include <stdio.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "test_util.h"
+
+namespace android {
+namespace dm {
+
+using unique_fd = android::base::unique_fd;
+
+// Create a temporary in-memory file. If size is non-zero, the file will be
+// created with a fixed size.
+unique_fd CreateTempFile(const std::string& name, size_t size) {
+ unique_fd fd(syscall(__NR_memfd_create, name.c_str(), MFD_ALLOW_SEALING));
+ if (fd < 0) {
+ return {};
+ }
+ if (size) {
+ if (ftruncate(fd, size) < 0) {
+ perror("ftruncate");
+ return {};
+ }
+ if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
+ perror("fcntl");
+ return {};
+ }
+ }
+ return fd;
+}
+
+} // namespace dm
+} // namespace android
diff --git a/fs_mgr/libdm/test_util.h b/fs_mgr/libdm/test_util.h
new file mode 100644
index 0000000..96b051c
--- /dev/null
+++ b/fs_mgr/libdm/test_util.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBDM_TEST_UTILS_H_
+#define _LIBDM_TEST_UTILS_H_
+
+#include <android-base/unique_fd.h>
+#include <stddef.h>
+
+#include <string>
+
+namespace android {
+namespace dm {
+
+// Create a temporary in-memory file. If size is non-zero, the file will be
+// created with a fixed size.
+android::base::unique_fd CreateTempFile(const std::string& name, size_t size);
+
+} // namespace dm
+} // namespace android
+
+#endif // _LIBDM_TEST_UTILS_H_
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
new file mode 100644
index 0000000..f7086a8
--- /dev/null
+++ b/fs_mgr/liblp/Android.bp
@@ -0,0 +1,61 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_static {
+ name: "liblp",
+ host_supported: true,
+ recovery_available: true,
+ defaults: ["fs_mgr_defaults"],
+ cppflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ ],
+ srcs: [
+ "builder.cpp",
+ "reader.cpp",
+ "utility.cpp",
+ "writer.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "liblog",
+ "libcrypto",
+ "libcrypto_utils",
+ ],
+ whole_static_libs: [
+ "libext2_uuid",
+ "libext4_utils",
+ "libsparse",
+ "libz",
+ ],
+ export_include_dirs: ["include"],
+}
+
+cc_test {
+ name: "liblp_test",
+ defaults: ["fs_mgr_defaults"],
+ static_libs: [
+ "libbase",
+ "liblog",
+ "libcrypto",
+ "libcrypto_utils",
+ "liblp",
+ ],
+ srcs: [
+ "builder_test.cpp",
+ "io_test.cpp",
+ "utility_test.cpp",
+ ],
+}
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
new file mode 100644
index 0000000..0e4838c
--- /dev/null
+++ b/fs_mgr/liblp/builder.cpp
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "liblp/builder.h"
+
+#include <string.h>
+
+#include <algorithm>
+
+#include <uuid/uuid.h>
+
+#include "liblp/metadata_format.h"
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Align a byte count up to the nearest 512-byte sector.
+template <typename T>
+static inline T AlignToSector(T value) {
+ return (value + (LP_SECTOR_SIZE - 1)) & ~T(LP_SECTOR_SIZE - 1);
+}
+
+void LinearExtent::AddTo(LpMetadata* out) const {
+ out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_});
+}
+
+void ZeroExtent::AddTo(LpMetadata* out) const {
+ out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0});
+}
+
+Partition::Partition(const std::string& name, const std::string& guid, uint32_t attributes)
+ : name_(name), guid_(guid), attributes_(attributes), size_(0) {}
+
+void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
+ size_ += extent->num_sectors() * LP_SECTOR_SIZE;
+ extents_.push_back(std::move(extent));
+}
+
+void Partition::RemoveExtents() {
+ size_ = 0;
+ extents_.clear();
+}
+
+void Partition::ShrinkTo(uint64_t requested_size) {
+ uint64_t aligned_size = AlignToSector(requested_size);
+ if (size_ <= aligned_size) {
+ return;
+ }
+ if (aligned_size == 0) {
+ RemoveExtents();
+ return;
+ }
+
+ // Remove or shrink extents of any kind until the total partition size is
+ // equal to the requested size.
+ uint64_t sectors_to_remove = (size_ - aligned_size) / LP_SECTOR_SIZE;
+ while (sectors_to_remove) {
+ Extent* extent = extents_.back().get();
+ if (extent->num_sectors() > sectors_to_remove) {
+ size_ -= sectors_to_remove * LP_SECTOR_SIZE;
+ extent->set_num_sectors(extent->num_sectors() - sectors_to_remove);
+ break;
+ }
+ size_ -= (extent->num_sectors() * LP_SECTOR_SIZE);
+ sectors_to_remove -= extent->num_sectors();
+ extents_.pop_back();
+ }
+ DCHECK(size_ == requested_size);
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(uint64_t blockdevice_size,
+ uint32_t metadata_max_size,
+ uint32_t metadata_slot_count) {
+ std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
+ if (!builder->Init(blockdevice_size, metadata_max_size, metadata_slot_count)) {
+ return nullptr;
+ }
+ return builder;
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata) {
+ std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
+ if (!builder->Init(metadata)) {
+ return nullptr;
+ }
+ return builder;
+}
+
+MetadataBuilder::MetadataBuilder() {
+ memset(&geometry_, 0, sizeof(geometry_));
+ geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
+ geometry_.struct_size = sizeof(geometry_);
+
+ memset(&header_, 0, sizeof(header_));
+ header_.magic = LP_METADATA_HEADER_MAGIC;
+ header_.major_version = LP_METADATA_MAJOR_VERSION;
+ header_.minor_version = LP_METADATA_MINOR_VERSION;
+ header_.header_size = sizeof(header_);
+ header_.partitions.entry_size = sizeof(LpMetadataPartition);
+ header_.extents.entry_size = sizeof(LpMetadataExtent);
+}
+
+bool MetadataBuilder::Init(const LpMetadata& metadata) {
+ geometry_ = metadata.geometry;
+
+ for (const auto& partition : metadata.partitions) {
+ Partition* builder = AddPartition(GetPartitionName(partition), GetPartitionGuid(partition),
+ partition.attributes);
+ if (!builder) {
+ return false;
+ }
+
+ for (size_t i = 0; i < partition.num_extents; i++) {
+ const LpMetadataExtent& extent = metadata.extents[partition.first_extent_index + i];
+ if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+ auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_data);
+ builder->AddExtent(std::move(copy));
+ } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
+ auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
+ builder->AddExtent(std::move(copy));
+ }
+ }
+ }
+ return true;
+}
+
+bool MetadataBuilder::Init(uint64_t blockdevice_size, uint32_t metadata_max_size,
+ uint32_t metadata_slot_count) {
+ if (metadata_max_size < sizeof(LpMetadataHeader)) {
+ LERROR << "Invalid metadata maximum size.";
+ return false;
+ }
+ if (metadata_slot_count == 0) {
+ LERROR << "Invalid metadata slot count.";
+ return false;
+ }
+
+ // Align the metadata size up to the nearest sector.
+ metadata_max_size = AlignToSector(metadata_max_size);
+
+ // We reserve a geometry block (4KB) plus space for each copy of the
+ // maximum size of a metadata blob. Then, we double that space since
+ // we store a backup copy of everything.
+ uint64_t reserved =
+ LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count);
+ uint64_t total_reserved = reserved * 2;
+
+ if (blockdevice_size < total_reserved || blockdevice_size - total_reserved < LP_SECTOR_SIZE) {
+ LERROR << "Attempting to create metadata on a block device that is too small.";
+ return false;
+ }
+
+ // The last sector is inclusive. We subtract one to make sure that logical
+ // partitions won't overlap with the same sector as the backup metadata,
+ // which could happen if the block device was not aligned to LP_SECTOR_SIZE.
+ geometry_.first_logical_sector = reserved / LP_SECTOR_SIZE;
+ geometry_.last_logical_sector = ((blockdevice_size - reserved) / LP_SECTOR_SIZE) - 1;
+ geometry_.metadata_max_size = metadata_max_size;
+ geometry_.metadata_slot_count = metadata_slot_count;
+ DCHECK(geometry_.last_logical_sector >= geometry_.first_logical_sector);
+ return true;
+}
+
+Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& guid,
+ uint32_t attributes) {
+ if (name.empty()) {
+ LERROR << "Partition must have a non-empty name.";
+ return nullptr;
+ }
+ if (FindPartition(name)) {
+ LERROR << "Attempting to create duplication partition with name: " << name;
+ return nullptr;
+ }
+ partitions_.push_back(std::make_unique<Partition>(name, guid, attributes));
+ return partitions_.back().get();
+}
+
+Partition* MetadataBuilder::FindPartition(const std::string& name) {
+ for (const auto& partition : partitions_) {
+ if (partition->name() == name) {
+ return partition.get();
+ }
+ }
+ return nullptr;
+}
+
+void MetadataBuilder::RemovePartition(const std::string& name) {
+ for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {
+ if ((*iter)->name() == name) {
+ partitions_.erase(iter);
+ return;
+ }
+ }
+}
+
+bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t requested_size) {
+ // Align the space needed up to the nearest sector.
+ uint64_t aligned_size = AlignToSector(requested_size);
+ if (partition->size() >= aligned_size) {
+ return true;
+ }
+
+ // Figure out how much we need to allocate.
+ uint64_t space_needed = aligned_size - partition->size();
+ uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
+ DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
+
+ struct Interval {
+ uint64_t start;
+ uint64_t end;
+
+ Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
+ bool operator<(const Interval& other) const { return start < other.start; }
+ };
+ std::vector<Interval> intervals;
+
+ // Collect all extents in the partition table.
+ for (const auto& partition : partitions_) {
+ for (const auto& extent : partition->extents()) {
+ LinearExtent* linear = extent->AsLinearExtent();
+ if (!linear) {
+ continue;
+ }
+ intervals.emplace_back(linear->physical_sector(),
+ linear->physical_sector() + extent->num_sectors());
+ }
+ }
+
+ // Sort extents by starting sector.
+ std::sort(intervals.begin(), intervals.end());
+
+ // Find gaps that we can use for new extents. Note we store new extents in a
+ // temporary vector, and only commit them if we are guaranteed enough free
+ // space.
+ std::vector<std::unique_ptr<LinearExtent>> new_extents;
+ for (size_t i = 1; i < intervals.size(); i++) {
+ const Interval& previous = intervals[i - 1];
+ const Interval& current = intervals[i];
+
+ if (previous.end >= current.start) {
+ // There is no gap between these two extents, try the next one. Note that
+ // extents may never overlap, but just for safety, we ignore them if they
+ // do.
+ DCHECK(previous.end == current.start);
+ continue;
+ }
+
+ // This gap is enough to hold the remainder of the space requested, so we
+ // can allocate what we need and return.
+ if (current.start - previous.end >= sectors_needed) {
+ auto extent = std::make_unique<LinearExtent>(sectors_needed, previous.end);
+ sectors_needed -= extent->num_sectors();
+ new_extents.push_back(std::move(extent));
+ break;
+ }
+
+ // This gap is not big enough to fit the remainder of the space requested,
+ // so consume the whole thing and keep looking for more.
+ auto extent = std::make_unique<LinearExtent>(current.start - previous.end, previous.end);
+ sectors_needed -= extent->num_sectors();
+ new_extents.push_back(std::move(extent));
+ }
+
+ // If we still have more to allocate, take it from the remaining free space
+ // in the allocatable region.
+ if (sectors_needed) {
+ uint64_t first_sector;
+ if (intervals.empty()) {
+ first_sector = geometry_.first_logical_sector;
+ } else {
+ first_sector = intervals.back().end;
+ }
+ DCHECK(first_sector <= geometry_.last_logical_sector);
+
+ // Note: the last usable sector is inclusive.
+ if (geometry_.last_logical_sector + 1 - first_sector < sectors_needed) {
+ LERROR << "Not enough free space to expand partition: " << partition->name();
+ return false;
+ }
+ auto extent = std::make_unique<LinearExtent>(sectors_needed, first_sector);
+ new_extents.push_back(std::move(extent));
+ }
+
+ for (auto& extent : new_extents) {
+ partition->AddExtent(std::move(extent));
+ }
+ return true;
+}
+
+void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t requested_size) {
+ partition->ShrinkTo(requested_size);
+}
+
+std::unique_ptr<LpMetadata> MetadataBuilder::Export() {
+ std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
+ metadata->header = header_;
+ metadata->geometry = geometry_;
+
+ // Flatten the partition and extent structures into an LpMetadata, which
+ // makes it very easy to validate, serialize, or pass on to device-mapper.
+ for (const auto& partition : partitions_) {
+ LpMetadataPartition part;
+ memset(&part, 0, sizeof(part));
+
+ if (partition->name().size() > sizeof(part.name)) {
+ LERROR << "Partition name is too long: " << partition->name();
+ return nullptr;
+ }
+ if (partition->attributes() & ~(LP_PARTITION_ATTRIBUTE_MASK)) {
+ LERROR << "Partition " << partition->name() << " has unsupported attribute.";
+ return nullptr;
+ }
+
+ strncpy(part.name, partition->name().c_str(), sizeof(part.name));
+ if (uuid_parse(partition->guid().c_str(), part.guid) != 0) {
+ LERROR << "Could not parse guid " << partition->guid() << " for partition "
+ << partition->name();
+ return nullptr;
+ }
+
+ part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
+ part.num_extents = static_cast<uint32_t>(partition->extents().size());
+ part.attributes = partition->attributes();
+
+ for (const auto& extent : partition->extents()) {
+ extent->AddTo(metadata.get());
+ }
+ metadata->partitions.push_back(part);
+ }
+
+ metadata->header.partitions.num_entries = static_cast<uint32_t>(metadata->partitions.size());
+ metadata->header.extents.num_entries = static_cast<uint32_t>(metadata->extents.size());
+ return metadata;
+}
+
+uint64_t MetadataBuilder::AllocatableSpace() const {
+ return (geometry_.last_logical_sector - geometry_.first_logical_sector + 1) * LP_SECTOR_SIZE;
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
new file mode 100644
index 0000000..2983f0f
--- /dev/null
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+
+using namespace std;
+using namespace android::fs_mgr;
+
+static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
+static const char* TEST_GUID2 = "A799D1D6-669F-41D8-A3F0-EBB7572D8303";
+
+TEST(liblp, BuildBasic) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ EXPECT_EQ(partition->name(), "system");
+ EXPECT_EQ(partition->guid(), TEST_GUID);
+ EXPECT_EQ(partition->attributes(), LP_PARTITION_ATTR_READONLY);
+ EXPECT_EQ(partition->size(), 0);
+ EXPECT_EQ(builder->FindPartition("system"), partition);
+
+ builder->RemovePartition("system");
+ EXPECT_EQ(builder->FindPartition("system"), nullptr);
+}
+
+TEST(liblp, ResizePartition) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+ EXPECT_EQ(system->size(), 65536);
+ ASSERT_EQ(system->extents().size(), 1);
+
+ LinearExtent* extent = system->extents()[0]->AsLinearExtent();
+ ASSERT_NE(extent, nullptr);
+ EXPECT_EQ(extent->num_sectors(), 65536 / LP_SECTOR_SIZE);
+ // The first logical sector will be (4096+1024*2)/512 = 12.
+ EXPECT_EQ(extent->physical_sector(), 12);
+
+ // Test growing to the same size.
+ EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+ EXPECT_EQ(system->size(), 65536);
+ EXPECT_EQ(system->extents().size(), 1);
+ EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
+ // Test growing to a smaller size.
+ EXPECT_EQ(builder->GrowPartition(system, 0), true);
+ EXPECT_EQ(system->size(), 65536);
+ EXPECT_EQ(system->extents().size(), 1);
+ EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
+ // Test shrinking to a greater size.
+ builder->ShrinkPartition(system, 131072);
+ EXPECT_EQ(system->size(), 65536);
+ EXPECT_EQ(system->extents().size(), 1);
+ EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
+
+ // Test shrinking within the same extent.
+ builder->ShrinkPartition(system, 32768);
+ EXPECT_EQ(system->size(), 32768);
+ EXPECT_EQ(system->extents().size(), 1);
+ extent = system->extents()[0]->AsLinearExtent();
+ ASSERT_NE(extent, nullptr);
+ EXPECT_EQ(extent->num_sectors(), 32768 / LP_SECTOR_SIZE);
+ EXPECT_EQ(extent->physical_sector(), 12);
+
+ // Test shrinking to 0.
+ builder->ShrinkPartition(system, 0);
+ EXPECT_EQ(system->size(), 0);
+ EXPECT_EQ(system->extents().size(), 0);
+}
+
+TEST(liblp, PartitionAlignment) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ // Test that we align up to one sector.
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 10000), true);
+ EXPECT_EQ(system->size(), 10240);
+ EXPECT_EQ(system->extents().size(), 1);
+
+ builder->ShrinkPartition(system, 9000);
+ EXPECT_EQ(system->size(), 9216);
+ EXPECT_EQ(system->extents().size(), 1);
+}
+
+TEST(liblp, DiskAlignment) {
+ static const uint64_t kDiskSize = 1000000;
+ static const uint32_t kMetadataSize = 1024;
+ static const uint32_t kMetadataSlots = 2;
+
+ // If the disk size is not aligned to 512 bytes, make sure it still leaves
+ // space at the end for backup metadata, and that it doesn't overlap with
+ // the space for logical partitions.
+ unique_ptr<MetadataBuilder> builder =
+ MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ static const size_t kMetadataSpace =
+ (kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE;
+ uint64_t space_at_end =
+ kDiskSize - (exported->geometry.last_logical_sector + 1) * LP_SECTOR_SIZE;
+ EXPECT_GE(space_at_end, kMetadataSpace);
+}
+
+TEST(liblp, MetadataAlignment) {
+ // Make sure metadata sizes get aligned up.
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1000, 2);
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ EXPECT_EQ(exported->geometry.metadata_max_size, 1024);
+}
+
+TEST(liblp, UseAllDiskSpace) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ EXPECT_EQ(builder->AllocatableSpace(), 1036288);
+
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 1036288), true);
+ EXPECT_EQ(system->size(), 1036288);
+ EXPECT_EQ(builder->GrowPartition(system, 1036289), false);
+}
+
+TEST(liblp, BuildComplex) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ ASSERT_NE(vendor, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+ EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
+ EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+ EXPECT_EQ(system->size(), 98304);
+ EXPECT_EQ(vendor->size(), 32768);
+
+ // We now expect to have 3 extents total: 2 for system, 1 for vendor, since
+ // our allocation strategy is greedy/first-fit.
+ ASSERT_EQ(system->extents().size(), 2);
+ ASSERT_EQ(vendor->extents().size(), 1);
+
+ LinearExtent* system1 = system->extents()[0]->AsLinearExtent();
+ LinearExtent* system2 = system->extents()[1]->AsLinearExtent();
+ LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent();
+ ASSERT_NE(system1, nullptr);
+ ASSERT_NE(system2, nullptr);
+ ASSERT_NE(vendor1, nullptr);
+ EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
+ EXPECT_EQ(system1->physical_sector(), 12);
+ EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
+ EXPECT_EQ(system2->physical_sector(), 204);
+ EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
+ EXPECT_EQ(vendor1->physical_sector(), 140);
+ EXPECT_EQ(system1->physical_sector() + system1->num_sectors(), vendor1->physical_sector());
+ EXPECT_EQ(vendor1->physical_sector() + vendor1->num_sectors(), system2->physical_sector());
+}
+
+TEST(liblp, AddInvalidPartition) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+
+ // Duplicate name.
+ partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ EXPECT_EQ(partition, nullptr);
+
+ // Empty name.
+ partition = builder->AddPartition("", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ EXPECT_EQ(partition, nullptr);
+}
+
+TEST(liblp, BuilderExport) {
+ static const uint64_t kDiskSize = 1024 * 1024;
+ static const uint32_t kMetadataSize = 1024;
+ static const uint32_t kMetadataSlots = 2;
+ unique_ptr<MetadataBuilder> builder =
+ MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
+
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ ASSERT_NE(vendor, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+ EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
+ EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ EXPECT_NE(exported, nullptr);
+
+ // Verify geometry. Some details of this may change if we change the
+ // metadata structures. So in addition to checking the exact values, we
+ // also check that they are internally consistent after.
+ const LpMetadataGeometry& geometry = exported->geometry;
+ EXPECT_EQ(geometry.magic, LP_METADATA_GEOMETRY_MAGIC);
+ EXPECT_EQ(geometry.struct_size, sizeof(geometry));
+ EXPECT_EQ(geometry.metadata_max_size, 1024);
+ EXPECT_EQ(geometry.metadata_slot_count, 2);
+ EXPECT_EQ(geometry.first_logical_sector, 12);
+ EXPECT_EQ(geometry.last_logical_sector, 2035);
+
+ static const size_t kMetadataSpace =
+ (kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE;
+ uint64_t space_at_end = kDiskSize - (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE;
+ EXPECT_GE(space_at_end, kMetadataSpace);
+ EXPECT_GE(geometry.first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace);
+
+ // Verify header.
+ const LpMetadataHeader& header = exported->header;
+ EXPECT_EQ(header.magic, LP_METADATA_HEADER_MAGIC);
+ EXPECT_EQ(header.major_version, LP_METADATA_MAJOR_VERSION);
+ EXPECT_EQ(header.minor_version, LP_METADATA_MINOR_VERSION);
+
+ ASSERT_EQ(exported->partitions.size(), 2);
+ ASSERT_EQ(exported->extents.size(), 3);
+
+ for (const auto& partition : exported->partitions) {
+ Partition* original = builder->FindPartition(GetPartitionName(partition));
+ ASSERT_NE(original, nullptr);
+ EXPECT_EQ(original->guid(), GetPartitionGuid(partition));
+ for (size_t i = 0; i < partition.num_extents; i++) {
+ const auto& extent = exported->extents[partition.first_extent_index + i];
+ LinearExtent* original_extent = original->extents()[i]->AsLinearExtent();
+ EXPECT_EQ(extent.num_sectors, original_extent->num_sectors());
+ EXPECT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
+ EXPECT_EQ(extent.target_data, original_extent->physical_sector());
+ }
+ EXPECT_EQ(partition.attributes, original->attributes());
+ }
+}
+
+TEST(liblp, BuilderImport) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ ASSERT_NE(vendor, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+ EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
+ EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ builder = MetadataBuilder::New(*exported.get());
+ ASSERT_NE(builder, nullptr);
+ system = builder->FindPartition("system");
+ ASSERT_NE(system, nullptr);
+ vendor = builder->FindPartition("vendor");
+ ASSERT_NE(vendor, nullptr);
+
+ EXPECT_EQ(system->size(), 98304);
+ ASSERT_EQ(system->extents().size(), 2);
+ EXPECT_EQ(system->guid(), TEST_GUID);
+ EXPECT_EQ(system->attributes(), LP_PARTITION_ATTR_READONLY);
+ EXPECT_EQ(vendor->size(), 32768);
+ ASSERT_EQ(vendor->extents().size(), 1);
+ EXPECT_EQ(vendor->guid(), TEST_GUID2);
+ EXPECT_EQ(vendor->attributes(), LP_PARTITION_ATTR_READONLY);
+
+ LinearExtent* system1 = system->extents()[0]->AsLinearExtent();
+ LinearExtent* system2 = system->extents()[1]->AsLinearExtent();
+ LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent();
+ EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
+ EXPECT_EQ(system1->physical_sector(), 12);
+ EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
+ EXPECT_EQ(system2->physical_sector(), 204);
+ EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
+}
+
+TEST(liblp, ExportNameTooLong) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ std::string name = "abcdefghijklmnopqrstuvwxyz0123456789";
+ Partition* system = builder->AddPartition(name + name, TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ EXPECT_NE(system, nullptr);
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ EXPECT_EQ(exported, nullptr);
+}
+
+TEST(liblp, ExportInvalidGuid) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* system = builder->AddPartition("system", "bad", LP_PARTITION_ATTR_READONLY);
+ EXPECT_NE(system, nullptr);
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ EXPECT_EQ(exported, nullptr);
+}
+
+TEST(liblp, MetadataTooLarge) {
+ static const size_t kDiskSize = 128 * 1024;
+ static const size_t kMetadataSize = 64 * 1024;
+
+ // No space to store metadata + geometry.
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize, kMetadataSize, 1);
+ EXPECT_EQ(builder, nullptr);
+
+ // No space to store metadata + geometry + one free sector.
+ builder = MetadataBuilder::New(kDiskSize + LP_METADATA_GEOMETRY_SIZE * 2, kMetadataSize, 1);
+ EXPECT_EQ(builder, nullptr);
+
+ // Space for metadata + geometry + one free sector.
+ builder = MetadataBuilder::New(kDiskSize + LP_METADATA_GEOMETRY_SIZE * 2 + LP_SECTOR_SIZE,
+ kMetadataSize, 1);
+ EXPECT_NE(builder, nullptr);
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
new file mode 100644
index 0000000..671a3bd
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -0,0 +1,172 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef LIBLP_METADATA_BUILDER_H
+#define LIBLP_METADATA_BUILDER_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+
+#include "metadata_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+class LinearExtent;
+
+// Abstraction around dm-targets that can be encoded into logical partition tables.
+class Extent {
+ public:
+ explicit Extent(uint64_t num_sectors) : num_sectors_(num_sectors) {}
+ virtual ~Extent() {}
+
+ virtual void AddTo(LpMetadata* out) const = 0;
+ virtual LinearExtent* AsLinearExtent() { return nullptr; }
+
+ uint64_t num_sectors() const { return num_sectors_; }
+ void set_num_sectors(uint64_t num_sectors) { num_sectors_ = num_sectors; }
+
+ protected:
+ uint64_t num_sectors_;
+};
+
+// This corresponds to a dm-linear target.
+class LinearExtent final : public Extent {
+ public:
+ LinearExtent(uint64_t num_sectors, uint64_t physical_sector)
+ : Extent(num_sectors), physical_sector_(physical_sector) {}
+
+ void AddTo(LpMetadata* metadata) const override;
+ LinearExtent* AsLinearExtent() override { return this; }
+
+ uint64_t physical_sector() const { return physical_sector_; }
+
+ private:
+ uint64_t physical_sector_;
+};
+
+// This corresponds to a dm-zero target.
+class ZeroExtent final : public Extent {
+ public:
+ explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}
+
+ void AddTo(LpMetadata* out) const override;
+};
+
+class Partition final {
+ public:
+ Partition(const std::string& name, const std::string& guid, uint32_t attributes);
+
+ // Add a raw extent.
+ void AddExtent(std::unique_ptr<Extent>&& extent);
+
+ // Remove all extents from this partition.
+ void RemoveExtents();
+
+ // Remove and/or shrink extents until the partition is the requested size.
+ // See MetadataBuilder::ShrinkPartition for more information.
+ void ShrinkTo(uint64_t requested_size);
+
+ const std::string& name() const { return name_; }
+ uint32_t attributes() const { return attributes_; }
+ const std::string& guid() const { return guid_; }
+ const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
+ uint64_t size() const { return size_; }
+
+ private:
+ std::string name_;
+ std::string guid_;
+ std::vector<std::unique_ptr<Extent>> extents_;
+ uint32_t attributes_;
+ uint64_t size_;
+};
+
+class MetadataBuilder {
+ public:
+ // Construct an empty logical partition table builder. The block device size
+ // and maximum metadata size must be specified, as this will determine which
+ // areas of the physical partition can be flashed for metadata vs for logical
+ // partitions.
+ //
+ // If the parameters would yield invalid metadata, nullptr is returned. This
+ // could happen if the block device size is too small to store the metadata
+ // and backup copies.
+ static std::unique_ptr<MetadataBuilder> New(uint64_t blockdevice_size,
+ uint32_t metadata_max_size,
+ uint32_t metadata_slot_count);
+
+ // Import an existing table for modification. If the table is not valid, for
+ // example it contains duplicate partition names, then nullptr is returned.
+ static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
+
+ // Export metadata so it can be serialized to an image, to disk, or mounted
+ // via device-mapper.
+ std::unique_ptr<LpMetadata> Export();
+
+ // Add a partition, returning a handle so it can be sized as needed. If a
+ // partition with the given name already exists, nullptr is returned.
+ Partition* AddPartition(const std::string& name, const std::string& guid, uint32_t attributes);
+
+ // Delete a partition by name if it exists.
+ void RemovePartition(const std::string& name);
+
+ // Find a partition by name. If no partition is found, nullptr is returned.
+ Partition* FindPartition(const std::string& name);
+
+ // Grow a partition to the requested size. If the partition's size is already
+ // greater or equal to the requested size, this will return true and the
+ // partition table will not be changed. Otherwise, a greedy algorithm is
+ // used to find free gaps in the partition table and allocate them for this
+ // partition. If not enough space can be allocated, false is returned, and
+ // the parition table will not be modified.
+ //
+ // The size will be rounded UP to the nearest sector.
+ //
+ // Note, this is an in-memory operation, and it does not alter the
+ // underlying filesystem or contents of the partition on disk.
+ bool GrowPartition(Partition* partition, uint64_t requested_size);
+
+ // Shrink a partition to the requested size. If the partition is already
+ // smaller than the given size, this will return and the partition table
+ // will not be changed. Otherwise, extents will be removed and/or shrunk
+ // from the end of the partition until it is the requested size.
+ //
+ // The size will be rounded UP to the nearest sector.
+ //
+ // Note, this is an in-memory operation, and it does not alter the
+ // underlying filesystem or contents of the partition on disk.
+ void ShrinkPartition(Partition* partition, uint64_t requested_size);
+
+ // Amount of space that can be allocated to logical partitions.
+ uint64_t AllocatableSpace() const;
+
+ private:
+ MetadataBuilder();
+ bool Init(uint64_t blockdevice_size, uint32_t metadata_max_size, uint32_t metadata_slot_count);
+ bool Init(const LpMetadata& metadata);
+
+ LpMetadataGeometry geometry_;
+ LpMetadataHeader header_;
+ std::vector<std::unique_ptr<Partition>> partitions_;
+};
+
+} // namespace fs_mgr
+} // namespace android
+
+#endif /* LIBLP_METADATA_BUILDER_H */
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
new file mode 100644
index 0000000..8522435
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LOGICAL_PARTITION_METADATA_FORMAT_H_
+#define LOGICAL_PARTITION_METADATA_FORMAT_H_
+
+#ifdef __cplusplus
+#include <string>
+#include <vector>
+#endif
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Magic signature for LpMetadataGeometry. */
+#define LP_METADATA_GEOMETRY_MAGIC 0x616c4467
+
+/* Space reserved for geometry information. */
+#define LP_METADATA_GEOMETRY_SIZE 4096
+
+/* Magic signature for LpMetadataHeader. */
+#define LP_METADATA_HEADER_MAGIC 0x414C5030
+
+/* Current metadata version. */
+#define LP_METADATA_MAJOR_VERSION 1
+#define LP_METADATA_MINOR_VERSION 0
+
+/* Attributes for the LpMetadataPartition::attributes field.
+ *
+ * READONLY - The partition should not be considered writable. When used with
+ * device mapper, the block device will be created as read-only.
+ */
+#define LP_PARTITION_ATTR_NONE 0x0
+#define LP_PARTITION_ATTR_READONLY 0x1
+
+/* Mask that defines all valid attributes. */
+#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY)
+
+/* Default name of the physical partition that holds logical partition entries.
+ * The layout of this partition will look like:
+ *
+ * +--------------------+
+ * | Disk Geometry |
+ * +--------------------+
+ * | Metadata |
+ * +--------------------+
+ * | Logical Partitions |
+ * +--------------------+
+ * | Backup Metadata |
+ * +--------------------+
+ * | Geometry Backup |
+ * +--------------------+
+ */
+#define LP_METADATA_PARTITION_NAME "android"
+
+/* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
+#define LP_SECTOR_SIZE 512
+
+/* This structure is stored at sector 0 in the first 4096 bytes of the
+ * partition, and again in the very last 4096 bytes. It is never modified and
+ * describes how logical partition information can be located.
+ */
+typedef struct LpMetadataGeometry {
+ /* 0: Magic signature (LP_METADATA_GEOMETRY_MAGIC). */
+ uint32_t magic;
+
+ /* 4: Size of the LpMetadataGeometry struct. */
+ uint32_t struct_size;
+
+ /* 8: SHA256 checksum of this struct, with this field set to 0. */
+ uint8_t checksum[32];
+
+ /* 40: Maximum amount of space a single copy of the metadata can use. */
+ uint32_t metadata_max_size;
+
+ /* 44: Number of copies of the metadata to keep. For A/B devices, this
+ * will be 2. For an A/B/C device, it would be 3, et cetera. For Non-A/B
+ * it will be 1. A backup copy of each slot is kept, so if this is "2",
+ * there will be four copies total.
+ */
+ uint32_t metadata_slot_count;
+
+ /* 48: First usable sector for allocating logical partitions. this will be
+ * the first sector after the initial 4096 geometry block, followed by the
+ * space consumed by metadata_max_size*metadata_slot_count.
+ */
+ uint64_t first_logical_sector;
+
+ /* 56: Last usable sector, inclusive, for allocating logical partitions.
+ * At the end of this sector will follow backup metadata slots and the
+ * backup geometry block at the very end.
+ */
+ uint64_t last_logical_sector;
+} __attribute__((packed)) LpMetadataGeometry;
+
+/* The logical partition metadata has a number of tables; they are described
+ * in the header via the following structure.
+ *
+ * The size of the table can be computed by multiplying entry_size by
+ * num_entries, and the result must not overflow a 32-bit signed integer.
+ */
+typedef struct LpMetadataTableDescriptor {
+ /* 0: Location of the table, relative to the metadata header. */
+ uint32_t offset;
+ /* 4: Number of entries in the table. */
+ uint32_t num_entries;
+ /* 8: Size of each entry in the table, in bytes. */
+ uint32_t entry_size;
+} __attribute__((packed)) LpMetadataTableDescriptor;
+
+/* Binary format for the header of the logical partition metadata format.
+ *
+ * The format has three sections. The header must occur first, and the
+ * proceeding tables may be placed in any order after.
+ *
+ * +-----------------------------------------+
+ * | Header data - fixed size |
+ * +-----------------------------------------+
+ * | Partition table - variable size |
+ * +-----------------------------------------+
+ * | Partition table extents - variable size |
+ * +-----------------------------------------+
+ *
+ * The "Header" portion is described by LpMetadataHeader. It will always
+ * precede the other three blocks.
+ *
+ * All fields are stored in little-endian byte order when serialized.
+ *
+ * This struct is versioned; see the |major_version| and |minor_version|
+ * fields.
+ */
+typedef struct LpMetadataHeader {
+ /* 0: Four bytes equal to LP_METADATA_HEADER_MAGIC. */
+ uint32_t magic;
+
+ /* 4: Version number required to read this metadata. If the version is not
+ * equal to the library version, the metadata should be considered
+ * incompatible.
+ */
+ uint16_t major_version;
+
+ /* 6: Minor version. A library supporting newer features should be able to
+ * read metadata with an older minor version. However, an older library
+ * should not support reading metadata if its minor version is higher.
+ */
+ uint16_t minor_version;
+
+ /* 8: The size of this header struct. */
+ uint32_t header_size;
+
+ /* 12: SHA256 checksum of the header, up to |header_size| bytes, computed as
+ * if this field were set to 0.
+ */
+ uint8_t header_checksum[32];
+
+ /* 44: The total size of all tables. This size is contiguous; tables may not
+ * have gaps in between, and they immediately follow the header.
+ */
+ uint32_t tables_size;
+
+ /* 48: SHA256 checksum of all table contents. */
+ uint8_t tables_checksum[32];
+
+ /* 80: Partition table descriptor. */
+ LpMetadataTableDescriptor partitions;
+ /* 92: Extent table descriptor. */
+ LpMetadataTableDescriptor extents;
+} __attribute__((packed)) LpMetadataHeader;
+
+/* This struct defines a logical partition entry, similar to what would be
+ * present in a GUID Partition Table.
+ */
+typedef struct LpMetadataPartition {
+ /* 0: Name of this partition in ASCII characters. Any unused characters in
+ * the buffer must be set to 0. Characters may only be alphanumeric or _.
+ * The name must include at least one ASCII character, and it must be unique
+ * across all partition names. The length (36) is the same as the maximum
+ * length of a GPT partition name.
+ */
+ char name[36];
+
+ /* 36: Globally unique identifier (GUID) of this partition. */
+ uint8_t guid[16];
+
+ /* 52: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
+ uint32_t attributes;
+
+ /* 56: Index of the first extent owned by this partition. The extent will
+ * start at logical sector 0. Gaps between extents are not allowed.
+ */
+ uint32_t first_extent_index;
+
+ /* 60: Number of extents in the partition. Every partition must have at
+ * least one extent.
+ */
+ uint32_t num_extents;
+} __attribute__((packed)) LpMetadataPartition;
+
+/* This extent is a dm-linear target, and the index is an index into the
+ * LinearExtent table.
+ */
+#define LP_TARGET_TYPE_LINEAR 0
+
+/* This extent is a dm-zero target. The index is ignored and must be 0. */
+#define LP_TARGET_TYPE_ZERO 1
+
+/* This struct defines an extent entry in the extent table block. */
+typedef struct LpMetadataExtent {
+ /* 0: Length of this extent, in 512-byte sectors. */
+ uint64_t num_sectors;
+
+ /* 8: Target type for device-mapper (see LP_TARGET_TYPE_* values). */
+ uint32_t target_type;
+
+ /* 12: Contents depends on target_type.
+ *
+ * LINEAR: The sector on the physical partition that this extent maps onto.
+ * ZERO: This field must be 0.
+ */
+ uint64_t target_data;
+} __attribute__((packed)) LpMetadataExtent;
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#ifdef __cplusplus
+namespace android {
+namespace fs_mgr {
+
+// Helper structure for easily interpreting deserialized metadata, or
+// re-serializing metadata.
+struct LpMetadata {
+ LpMetadataGeometry geometry;
+ LpMetadataHeader header;
+ std::vector<LpMetadataPartition> partitions;
+ std::vector<LpMetadataExtent> extents;
+};
+
+// Helper to extract safe C++ strings from partition info.
+std::string GetPartitionName(const LpMetadataPartition& partition);
+std::string GetPartitionGuid(const LpMetadataPartition& partition);
+
+// Helper to return a slot number for a slot suffix.
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
+
+} // namespace fs_mgr
+} // namespace android
+#endif
+
+#endif /* LOGICAL_PARTITION_METADATA_FORMAT_H_ */
diff --git a/fs_mgr/liblp/include/liblp/reader.h b/fs_mgr/liblp/include/liblp/reader.h
new file mode 100644
index 0000000..982fe65
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/reader.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBLP_READER_H_
+#define LIBLP_READER_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "metadata_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Read logical partition metadata from its predetermined location on a block
+// device. If readback fails, we also attempt to load from a backup copy.
+std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number);
+std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
+
+// Read and validate the logical partition geometry from a block device.
+bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry);
+bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
+
+// Read logical partition metadata from an image file that was created with
+// WriteToImageFile().
+std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
+std::unique_ptr<LpMetadata> ReadFromImageFile(int fd);
+
+} // namespace fs_mgr
+} // namespace android
+
+#endif /* LIBLP_READER_H_ */
diff --git a/fs_mgr/liblp/include/liblp/writer.h b/fs_mgr/liblp/include/liblp/writer.h
new file mode 100644
index 0000000..efa409d
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/writer.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBLP_WRITER_H
+#define LIBLP_WRITER_H
+
+#include "metadata_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+// When flashing the initial logical partition layout, we also write geometry
+// information at the start and end of the big physical partition. This helps
+// locate metadata and backup metadata in the case of corruption or a failed
+// update. For normal changes to the metadata, we never modify the geometry.
+enum class SyncMode {
+ // Write geometry information.
+ Flash,
+ // Normal update of a single slot.
+ Update
+};
+
+// Write the given partition table to the given block device, writing only
+// copies according to the given sync mode.
+//
+// This will perform some verification, such that the device has enough space
+// to store the metadata as well as all of its extents.
+//
+// The slot number indicates which metadata slot to use.
+bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
+ uint32_t slot_number);
+bool WritePartitionTable(int fd, const LpMetadata& metadata, SyncMode sync_mode,
+ uint32_t slot_number);
+
+// Helper function to serialize geometry and metadata to a normal file, for
+// flashing or debugging.
+bool WriteToImageFile(const char* file, const LpMetadata& metadata);
+bool WriteToImageFile(int fd, const LpMetadata& metadata);
+
+} // namespace fs_mgr
+} // namespace android
+
+#endif /* LIBLP_WRITER_H */
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
new file mode 100644
index 0000000..2595654
--- /dev/null
+++ b/fs_mgr/liblp/io_test.cpp
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <linux/memfd.h>
+#include <stdio.h>
+#include <sys/syscall.h>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+#include <liblp/reader.h>
+#include <liblp/writer.h>
+
+#include "utility.h"
+
+using namespace std;
+using namespace android::fs_mgr;
+using unique_fd = android::base::unique_fd;
+
+// Our tests assume a 128KiB disk with two 512 byte metadata slots.
+static const size_t kDiskSize = 131072;
+static const size_t kMetadataSize = 512;
+static const size_t kMetadataSlots = 2;
+static const char* TEST_GUID_BASE = "A799D1D6-669F-41D8-A3F0-EBB7572D830";
+static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
+
+// Helper function for creating an in-memory file descriptor. This lets us
+// simulate read/writing logical partition metadata as if we had a block device
+// for a physical partition.
+static unique_fd CreateFakeDisk(off_t size) {
+ unique_fd fd(syscall(__NR_memfd_create, "fake_disk", MFD_ALLOW_SEALING));
+ if (fd < 0) {
+ perror("memfd_create");
+ return {};
+ }
+ if (ftruncate(fd, size) < 0) {
+ perror("ftruncate");
+ return {};
+ }
+ // Prevent anything from accidentally growing/shrinking the file, as it
+ // would not be allowed on an actual partition.
+ if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
+ perror("fcntl");
+ return {};
+ }
+ // Write garbage to the "disk" so we can tell what has been zeroed or not.
+ unique_ptr<uint8_t[]> buffer = make_unique<uint8_t[]>(size);
+ memset(buffer.get(), 0xcc, size);
+ if (!android::base::WriteFully(fd, buffer.get(), size)) {
+ return {};
+ }
+ return fd;
+}
+
+// Create a disk of the default size.
+static unique_fd CreateFakeDisk() {
+ return CreateFakeDisk(kDiskSize);
+}
+
+// Create a MetadataBuilder around some default sizes.
+static unique_ptr<MetadataBuilder> CreateDefaultBuilder() {
+ unique_ptr<MetadataBuilder> builder =
+ MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
+ return builder;
+}
+
+static bool AddDefaultPartitions(MetadataBuilder* builder) {
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_NONE);
+ if (!system) {
+ return false;
+ }
+ return builder->GrowPartition(system, 24 * 1024);
+}
+
+// Create a temporary disk and flash it with the default partition setup.
+static unique_fd CreateFlashedDisk() {
+ unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+ if (!builder || !AddDefaultPartitions(builder.get())) {
+ return {};
+ }
+ unique_fd fd = CreateFakeDisk();
+ if (fd < 0) {
+ return {};
+ }
+ // Export and flash.
+ unique_ptr<LpMetadata> exported = builder->Export();
+ if (!exported) {
+ return {};
+ }
+ if (!WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0)) {
+ return {};
+ }
+ return fd;
+}
+
+// Test that our CreateFakeDisk() function works.
+TEST(liblp, CreateFakeDisk) {
+ unique_fd fd = CreateFakeDisk();
+ ASSERT_GE(fd, 0);
+
+ uint64_t size;
+ ASSERT_TRUE(GetDescriptorSize(fd, &size));
+ ASSERT_EQ(size, kDiskSize);
+}
+
+// Flashing metadata should not work if the metadata was created for a larger
+// disk than the destination disk.
+TEST(liblp, ExportDiskTooSmall) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 1024, 512, 2);
+ ASSERT_NE(builder, nullptr);
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ // A larger geometry should fail to flash, since there won't be enough
+ // space to store the logical partition range that was specified.
+ unique_fd fd = CreateFakeDisk();
+ ASSERT_GE(fd, 0);
+
+ EXPECT_FALSE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
+}
+
+// Test the basics of flashing a partition and reading it back.
+TEST(liblp, FlashAndReadback) {
+ unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+ ASSERT_NE(builder, nullptr);
+ ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+
+ unique_fd fd = CreateFakeDisk();
+ ASSERT_GE(fd, 0);
+
+ // Export and flash.
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
+
+ // Read back. Note that some fields are only filled in during
+ // serialization, so exported and imported will not be identical. For
+ // example, table sizes and checksums are computed in WritePartitionTable.
+ // Therefore we check on a field-by-field basis.
+ unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+
+ // Check geometry and header.
+ EXPECT_EQ(exported->geometry.metadata_max_size, imported->geometry.metadata_max_size);
+ EXPECT_EQ(exported->geometry.metadata_slot_count, imported->geometry.metadata_slot_count);
+ EXPECT_EQ(exported->geometry.first_logical_sector, imported->geometry.first_logical_sector);
+ EXPECT_EQ(exported->geometry.last_logical_sector, imported->geometry.last_logical_sector);
+ EXPECT_EQ(exported->header.major_version, imported->header.major_version);
+ EXPECT_EQ(exported->header.minor_version, imported->header.minor_version);
+ EXPECT_EQ(exported->header.header_size, imported->header.header_size);
+
+ // Check partition tables.
+ ASSERT_EQ(exported->partitions.size(), imported->partitions.size());
+ EXPECT_EQ(GetPartitionName(exported->partitions[0]), GetPartitionName(imported->partitions[0]));
+ EXPECT_EQ(GetPartitionGuid(exported->partitions[0]), GetPartitionGuid(imported->partitions[0]));
+ EXPECT_EQ(exported->partitions[0].attributes, imported->partitions[0].attributes);
+ EXPECT_EQ(exported->partitions[0].first_extent_index,
+ imported->partitions[0].first_extent_index);
+ EXPECT_EQ(exported->partitions[0].num_extents, imported->partitions[0].num_extents);
+
+ // Check extent tables.
+ ASSERT_EQ(exported->extents.size(), imported->extents.size());
+ EXPECT_EQ(exported->extents[0].num_sectors, imported->extents[0].num_sectors);
+ EXPECT_EQ(exported->extents[0].target_type, imported->extents[0].target_type);
+ EXPECT_EQ(exported->extents[0].target_data, imported->extents[0].target_data);
+}
+
+// Test that we can update metadata slots without disturbing others.
+TEST(liblp, UpdateAnyMetadataSlot) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ ASSERT_EQ(imported->partitions.size(), 1);
+ EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
+
+ // Verify that we can't read unwritten metadata.
+ ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
+
+ // Change the name before writing to the next slot.
+ strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
+ ASSERT_TRUE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+
+ // Read back the original slot, make sure it hasn't changed.
+ imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ ASSERT_EQ(imported->partitions.size(), 1);
+ EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
+
+ // Now read back the new slot, and verify that it has a different name.
+ imported = ReadMetadata(fd, 1);
+ ASSERT_NE(imported, nullptr);
+ ASSERT_EQ(imported->partitions.size(), 1);
+ EXPECT_EQ(GetPartitionName(imported->partitions[0]), "vendor");
+
+ // Verify that we didn't overwrite anything in the logical paritition area.
+ // We expect the disk to be filled with 0xcc on creation so we can read
+ // this back and compare it.
+ char expected[LP_SECTOR_SIZE];
+ memset(expected, 0xcc, sizeof(expected));
+ for (uint64_t i = imported->geometry.first_logical_sector;
+ i <= imported->geometry.last_logical_sector; i++) {
+ char buffer[LP_SECTOR_SIZE];
+ ASSERT_GE(lseek(fd, i * LP_SECTOR_SIZE, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
+ ASSERT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
+ }
+}
+
+TEST(liblp, InvalidMetadataSlot) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ // Make sure all slots are filled.
+ unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+ ASSERT_NE(metadata, nullptr);
+ for (uint32_t i = 1; i < kMetadataSlots; i++) {
+ ASSERT_TRUE(WritePartitionTable(fd, *metadata.get(), SyncMode::Update, i));
+ }
+
+ // Verify that we can't read unavailable slots.
+ EXPECT_EQ(ReadMetadata(fd, kMetadataSlots), nullptr);
+}
+
+// Test that updating a metadata slot does not allow it to be computed based
+// on mismatching geometry.
+TEST(liblp, NoChangingGeometry) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ ASSERT_TRUE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+
+ imported->geometry.metadata_max_size += LP_SECTOR_SIZE;
+ ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+
+ imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ imported->geometry.metadata_slot_count++;
+ ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+
+ imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ imported->geometry.first_logical_sector++;
+ ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+
+ imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ imported->geometry.last_logical_sector--;
+ ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+}
+
+// Test that changing one bit of metadata is enough to break the checksum.
+TEST(liblp, BitFlipGeometry) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ LpMetadataGeometry geometry;
+ ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::ReadFully(fd, &geometry, sizeof(geometry)));
+
+ LpMetadataGeometry bad_geometry = geometry;
+ bad_geometry.metadata_slot_count++;
+ ASSERT_TRUE(android::base::WriteFully(fd, &bad_geometry, sizeof(bad_geometry)));
+
+ unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+ ASSERT_NE(metadata, nullptr);
+ EXPECT_EQ(metadata->geometry.metadata_slot_count, 2);
+}
+
+TEST(liblp, ReadBackupGeometry) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ char corruption[LP_METADATA_GEOMETRY_SIZE];
+ memset(corruption, 0xff, sizeof(corruption));
+
+ // Corrupt the first 4096 bytes of the disk.
+ ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+ EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+
+ // Corrupt the last 4096 bytes too.
+ ASSERT_GE(lseek(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END), 0);
+ ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+ EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+}
+
+TEST(liblp, ReadBackupMetadata) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+
+ char corruption[kMetadataSize];
+ memset(corruption, 0xff, sizeof(corruption));
+
+ ASSERT_GE(lseek(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+ EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+
+ off_t offset = LP_METADATA_GEOMETRY_SIZE + kMetadataSize * 2;
+
+ // Corrupt the backup metadata.
+ ASSERT_GE(lseek(fd, -offset, SEEK_END), 0);
+ ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+ EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+}
+
+// Test that we don't attempt to write metadata if it would overflow its
+// reserved space.
+TEST(liblp, TooManyPartitions) {
+ unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+ ASSERT_NE(builder, nullptr);
+
+ // Compute the maximum number of partitions we can fit in 1024 bytes of metadata.
+ size_t max_partitions = (kMetadataSize - sizeof(LpMetadataHeader)) / sizeof(LpMetadataPartition);
+ EXPECT_LT(max_partitions, 10);
+
+ // Add this number of partitions.
+ Partition* partition = nullptr;
+ for (size_t i = 0; i < max_partitions; i++) {
+ std::string guid = std::string(TEST_GUID) + to_string(i);
+ partition = builder->AddPartition(to_string(i), TEST_GUID, LP_PARTITION_ATTR_NONE);
+ ASSERT_NE(partition, nullptr);
+ }
+ ASSERT_NE(partition, nullptr);
+ // Add one extent to any partition to fill up more space - we're at 508
+ // bytes after this, out of 512.
+ ASSERT_TRUE(builder->GrowPartition(partition, 1024));
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ unique_fd fd = CreateFakeDisk();
+ ASSERT_GE(fd, 0);
+
+ // Check that we are able to write our table.
+ ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
+ ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Update, 1));
+
+ // Check that adding one more partition overflows the metadata allotment.
+ partition = builder->AddPartition("final", TEST_GUID, LP_PARTITION_ATTR_NONE);
+ EXPECT_NE(partition, nullptr);
+
+ exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ // The new table should be too large to be written.
+ ASSERT_FALSE(WritePartitionTable(fd, *exported.get(), SyncMode::Update, 1));
+
+ // Check that the first and last logical sectors weren't touched when we
+ // wrote this almost-full metadata.
+ char expected[LP_SECTOR_SIZE];
+ memset(expected, 0xcc, sizeof(expected));
+ char buffer[LP_SECTOR_SIZE];
+ ASSERT_GE(lseek(fd, exported->geometry.first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
+ EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
+ ASSERT_GE(lseek(fd, exported->geometry.last_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
+ EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
+}
+
+// Test that we can read and write image files.
+TEST(liblp, ImageFiles) {
+ unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+ ASSERT_NE(builder, nullptr);
+ ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+ unique_ptr<LpMetadata> exported = builder->Export();
+
+ unique_fd fd(syscall(__NR_memfd_create, "image_file", 0));
+ ASSERT_GE(fd, 0);
+ ASSERT_TRUE(WriteToImageFile(fd, *exported.get()));
+
+ unique_ptr<LpMetadata> imported = ReadFromImageFile(fd);
+ ASSERT_NE(imported, nullptr);
+}
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
new file mode 100644
index 0000000..7938186
--- /dev/null
+++ b/fs_mgr/liblp/reader.cpp
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "liblp/reader.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <functional>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Parse an LpMetadataGeometry from a buffer. The buffer must be at least
+// LP_METADATA_GEOMETRY_SIZE bytes in size.
+static bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {
+ static_assert(sizeof(*geometry) <= LP_METADATA_GEOMETRY_SIZE);
+ memcpy(geometry, buffer, sizeof(*geometry));
+
+ // Check the magic signature.
+ if (geometry->magic != LP_METADATA_GEOMETRY_MAGIC) {
+ LERROR << "Logical partition metadata has invalid geometry magic signature.";
+ return false;
+ }
+ // Recompute and check the CRC32.
+ {
+ LpMetadataGeometry temp = *geometry;
+ memset(&temp.checksum, 0, sizeof(temp.checksum));
+ SHA256(&temp, sizeof(temp), temp.checksum);
+ if (memcmp(temp.checksum, geometry->checksum, sizeof(temp.checksum)) != 0) {
+ LERROR << "Logical partition metadata has invalid geometry checksum.";
+ return false;
+ }
+ }
+ // Check that the struct size is equal (this will have to change if we ever
+ // change the struct size in a release).
+ if (geometry->struct_size != sizeof(LpMetadataGeometry)) {
+ LERROR << "Logical partition metadata has invalid struct size.";
+ return false;
+ }
+ if (geometry->metadata_slot_count == 0) {
+ LERROR << "Logical partition metadata has invalid slot count.";
+ return false;
+ }
+
+ // Check that the metadata area and logical partition areas don't overlap.
+ int64_t end_of_metadata =
+ GetPrimaryMetadataOffset(*geometry, geometry->metadata_slot_count - 1) +
+ geometry->metadata_max_size;
+ if (uint64_t(end_of_metadata) > geometry->first_logical_sector * LP_SECTOR_SIZE) {
+ LERROR << "Logical partition metadata overlaps with logical partition contents.";
+ return false;
+ }
+ return true;
+}
+
+// Read and validate geometry information from a block device that holds
+// logical partitions. If the information is corrupted, this will attempt
+// to read it from a secondary backup location.
+bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
+ // Read the first 4096 bytes.
+ std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+ if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed";
+ return false;
+ }
+ if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+ PERROR << __PRETTY_FUNCTION__ << "read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed";
+ return false;
+ }
+ if (ParseGeometry(buffer.get(), geometry)) {
+ return true;
+ }
+
+ // Try the backup copy in the last 4096 bytes.
+ if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed, offset " << -LP_METADATA_GEOMETRY_SIZE;
+ return false;
+ }
+ if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+ PERROR << __PRETTY_FUNCTION__ << "backup read " << LP_METADATA_GEOMETRY_SIZE
+ << " bytes failed";
+ return false;
+ }
+ return ParseGeometry(buffer.get(), geometry);
+}
+
+// Helper function to read geometry from a device without an open descriptor.
+bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry) {
+ android::base::unique_fd fd(open(block_device, O_RDONLY));
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+ return false;
+ }
+ return ReadLogicalPartitionGeometry(fd, geometry);
+}
+
+static bool ValidateTableBounds(const LpMetadataHeader& header,
+ const LpMetadataTableDescriptor& table) {
+ if (table.offset > header.tables_size) {
+ return false;
+ }
+ uint64_t table_size = uint64_t(table.num_entries) * table.entry_size;
+ if (header.tables_size - table.offset < table_size) {
+ return false;
+ }
+ return true;
+}
+
+static bool ValidateMetadataHeader(const LpMetadataHeader& header) {
+ // To compute the header's checksum, we have to temporarily set its checksum
+ // field to 0.
+ {
+ LpMetadataHeader temp = header;
+ memset(&temp.header_checksum, 0, sizeof(temp.header_checksum));
+ SHA256(&temp, sizeof(temp), temp.header_checksum);
+ if (memcmp(temp.header_checksum, header.header_checksum, sizeof(temp.header_checksum)) != 0) {
+ LERROR << "Logical partition metadata has invalid checksum.";
+ return false;
+ }
+ }
+
+ // Do basic validation of key metadata bits.
+ if (header.magic != LP_METADATA_HEADER_MAGIC) {
+ LERROR << "Logical partition metadata has invalid magic value.";
+ return false;
+ }
+ // Check that the version is compatible.
+ if (header.major_version != LP_METADATA_MAJOR_VERSION ||
+ header.minor_version > LP_METADATA_MINOR_VERSION) {
+ LERROR << "Logical partition metadata has incompatible version.";
+ return false;
+ }
+ if (!ValidateTableBounds(header, header.partitions) ||
+ !ValidateTableBounds(header, header.extents)) {
+ LERROR << "Logical partition metadata has invalid table bounds.";
+ return false;
+ }
+ // Check that table entry sizes can accomodate their respective structs. If
+ // table sizes change, these checks will have to be adjusted.
+ if (header.partitions.entry_size != sizeof(LpMetadataPartition)) {
+ LERROR << "Logical partition metadata has invalid partition table entry size.";
+ return false;
+ }
+ if (header.extents.entry_size != sizeof(LpMetadataExtent)) {
+ LERROR << "Logical partition metadata has invalid extent table entry size.";
+ return false;
+ }
+ return true;
+}
+
+using ReadMetadataFn = std::function<bool(void* buffer, size_t num_bytes)>;
+
+// Parse and validate all metadata at the current position in the given file
+// descriptor.
+static std::unique_ptr<LpMetadata> ParseMetadata(int fd) {
+ // First read and validate the header.
+ std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
+ if (!android::base::ReadFully(fd, &metadata->header, sizeof(metadata->header))) {
+ PERROR << __PRETTY_FUNCTION__ << "read " << sizeof(metadata->header) << "bytes failed";
+ return nullptr;
+ }
+ if (!ValidateMetadataHeader(metadata->header)) {
+ return nullptr;
+ }
+
+ LpMetadataHeader& header = metadata->header;
+
+ // Read the metadata payload. Allocation is fallible in case the metadata is
+ // corrupt and has some huge value.
+ std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[header.tables_size]);
+ if (!buffer) {
+ LERROR << "Out of memory reading logical partition tables.";
+ return nullptr;
+ }
+ if (!android::base::ReadFully(fd, buffer.get(), header.tables_size)) {
+ PERROR << __PRETTY_FUNCTION__ << "read " << header.tables_size << "bytes failed";
+ return nullptr;
+ }
+
+ uint8_t checksum[32];
+ SHA256(buffer.get(), header.tables_size, checksum);
+ if (memcmp(checksum, header.tables_checksum, sizeof(checksum)) != 0) {
+ LERROR << "Logical partition metadata has invalid table checksum.";
+ return nullptr;
+ }
+
+ // ValidateTableSize ensured that |cursor| is valid for the number of
+ // entries in the table.
+ uint8_t* cursor = buffer.get() + header.partitions.offset;
+ for (size_t i = 0; i < header.partitions.num_entries; i++) {
+ LpMetadataPartition partition;
+ memcpy(&partition, cursor, sizeof(partition));
+ cursor += header.partitions.entry_size;
+
+ if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK) {
+ LERROR << "Logical partition has invalid attribute set.";
+ return nullptr;
+ }
+ if (partition.first_extent_index + partition.num_extents > header.extents.num_entries) {
+ LERROR << "Logical partition has invalid extent list.";
+ return nullptr;
+ }
+
+ metadata->partitions.push_back(partition);
+ }
+
+ cursor = buffer.get() + header.extents.offset;
+ for (size_t i = 0; i < header.extents.num_entries; i++) {
+ LpMetadataExtent extent;
+ memcpy(&extent, cursor, sizeof(extent));
+ cursor += header.extents.entry_size;
+
+ metadata->extents.push_back(extent);
+ }
+
+ return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) {
+ LpMetadataGeometry geometry;
+ if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+ return nullptr;
+ }
+
+ if (slot_number >= geometry.metadata_slot_count) {
+ LERROR << __PRETTY_FUNCTION__ << "invalid metadata slot number";
+ return nullptr;
+ }
+
+ // First try the primary copy.
+ int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
+ if (SeekFile64(fd, offset, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+ return nullptr;
+ }
+ std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd);
+
+ // If the primary copy failed, try the backup copy.
+ if (!metadata) {
+ offset = GetBackupMetadataOffset(geometry, slot_number);
+ if (SeekFile64(fd, offset, SEEK_END) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+ return nullptr;
+ }
+ metadata = ParseMetadata(fd);
+ }
+
+ if (metadata) {
+ metadata->geometry = geometry;
+ }
+ return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number) {
+ android::base::unique_fd fd(open(block_device, O_RDONLY));
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+ return nullptr;
+ }
+ return ReadMetadata(fd, slot_number);
+}
+
+std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) {
+ LpMetadataGeometry geometry;
+ if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+ return nullptr;
+ }
+ if (SeekFile64(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << LP_METADATA_GEOMETRY_SIZE;
+ return nullptr;
+ }
+ std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd);
+ if (!metadata) {
+ return nullptr;
+ }
+ metadata->geometry = geometry;
+ return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
+ android::base::unique_fd fd(open(file, O_RDONLY));
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+ return nullptr;
+ }
+ return ReadFromImageFile(fd);
+}
+
+static std::string NameFromFixedArray(const char* name, size_t buffer_size) {
+ // If the end of the buffer has a null character, it's safe to assume the
+ // buffer is null terminated. Otherwise, we cap the string to the input
+ // buffer size.
+ if (name[buffer_size - 1] == '\0') {
+ return std::string(name);
+ }
+ return std::string(name, buffer_size);
+}
+
+std::string GetPartitionName(const LpMetadataPartition& partition) {
+ return NameFromFixedArray(partition.name, sizeof(partition.name));
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
new file mode 100644
index 0000000..5310cab
--- /dev/null
+++ b/fs_mgr/liblp/utility.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <ext4_utils/ext4_utils.h>
+#include <openssl/sha.h>
+#include <uuid/uuid.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+bool GetDescriptorSize(int fd, uint64_t* size) {
+ struct stat s;
+ if (fstat(fd, &s) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "fstat failed";
+ return false;
+ }
+
+ if (S_ISBLK(s.st_mode)) {
+ return get_block_device_size(fd);
+ }
+
+ int64_t result = SeekFile64(fd, 0, SEEK_END);
+ if (result == -1) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed";
+ return false;
+ }
+
+ *size = result;
+ return true;
+}
+
+int64_t SeekFile64(int fd, int64_t offset, int whence) {
+ static_assert(sizeof(off_t) == sizeof(int64_t), "Need 64-bit lseek");
+ return lseek(fd, offset, whence);
+}
+
+int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
+ CHECK(slot_number < geometry.metadata_slot_count);
+
+ int64_t offset = LP_METADATA_GEOMETRY_SIZE + geometry.metadata_max_size * slot_number;
+ CHECK(offset + geometry.metadata_max_size <=
+ int64_t(geometry.first_logical_sector * LP_SECTOR_SIZE));
+ return offset;
+}
+
+int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
+ CHECK(slot_number < geometry.metadata_slot_count);
+ int64_t start = int64_t(-LP_METADATA_GEOMETRY_SIZE) -
+ int64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
+ return start + int64_t(geometry.metadata_max_size * slot_number);
+}
+
+void SHA256(const void* data, size_t length, uint8_t out[32]) {
+ SHA256_CTX c;
+ SHA256_Init(&c);
+ SHA256_Update(&c, data, length);
+ SHA256_Final(out, &c);
+}
+
+std::string GetPartitionGuid(const LpMetadataPartition& partition) {
+ // 32 hex characters, four hyphens. Unfortunately libext2_uuid provides no
+ // macro to assist with buffer sizing.
+ static const size_t kGuidLen = 36;
+ char buffer[kGuidLen + 1];
+ uuid_unparse_upper(partition.guid, buffer);
+ return buffer;
+}
+
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
+ if (suffix.empty()) {
+ return 0;
+ }
+ if (suffix.size() != 2 || suffix[0] != '_' || suffix[1] < 'a') {
+ LERROR << __PRETTY_FUNCTION__ << "slot '" << suffix
+ << "' does not have a recognized format.";
+ return 0;
+ }
+ return suffix[1] - 'a';
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
new file mode 100644
index 0000000..09ed314
--- /dev/null
+++ b/fs_mgr/liblp/utility.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBLP_UTILITY_H
+#define LIBLP_UTILITY_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+
+#include "liblp/metadata_format.h"
+
+#define LP_TAG "[liblp]"
+#define LERROR LOG(ERROR) << LP_TAG
+#define PERROR PLOG(ERROR) << LP_TAG
+
+namespace android {
+namespace fs_mgr {
+
+// Determine the size of a block device (or file). Logs and returns false on
+// error. After calling this, the position of |fd| may have changed.
+bool GetDescriptorSize(int fd, uint64_t* size);
+
+// Return the offset of a primary metadata slot, relative to the start of the
+// device.
+int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
+
+// Return the offset of a backup metadata slot, relative to the end of the
+// device.
+int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
+
+// Cross-platform helper for lseek64().
+int64_t SeekFile64(int fd, int64_t offset, int whence);
+
+// Compute a SHA256 hash.
+void SHA256(const void* data, size_t length, uint8_t out[32]);
+
+} // namespace fs_mgr
+} // namespace android
+
+#endif // LIBLP_UTILITY_H
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
new file mode 100644
index 0000000..25e8a25
--- /dev/null
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utility.h"
+#include <gtest/gtest.h>
+
+using namespace android;
+using namespace android::fs_mgr;
+
+TEST(liblp, SlotNumberForSlotSuffix) {
+ EXPECT_EQ(SlotNumberForSlotSuffix(""), 0);
+ EXPECT_EQ(SlotNumberForSlotSuffix("_a"), 0);
+ EXPECT_EQ(SlotNumberForSlotSuffix("_b"), 1);
+ EXPECT_EQ(SlotNumberForSlotSuffix("_c"), 2);
+ EXPECT_EQ(SlotNumberForSlotSuffix("_d"), 3);
+}
+
+TEST(liblp, GetMetadataOffset) {
+ LpMetadataGeometry geometry = {
+ LP_METADATA_GEOMETRY_MAGIC, sizeof(geometry), {0}, 16384, 4, 10000, 80000};
+ EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096);
+ EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384);
+ EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2);
+ EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), 4096 + 16384 * 3);
+
+ EXPECT_EQ(GetBackupMetadataOffset(geometry, 3), -4096 - 16384 * 1);
+ EXPECT_EQ(GetBackupMetadataOffset(geometry, 2), -4096 - 16384 * 2);
+ EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), -4096 - 16384 * 3);
+ EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), -4096 - 16384 * 4);
+}
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
new file mode 100644
index 0000000..89cbabd
--- /dev/null
+++ b/fs_mgr/liblp/writer.cpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+
+#include "liblp/reader.h"
+#include "liblp/writer.h"
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+static std::string SerializeGeometry(const LpMetadataGeometry& input) {
+ LpMetadataGeometry geometry = input;
+ memset(geometry.checksum, 0, sizeof(geometry.checksum));
+ SHA256(&geometry, sizeof(geometry), geometry.checksum);
+ return std::string(reinterpret_cast<const char*>(&geometry), sizeof(geometry));
+}
+
+static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {
+ return g1.metadata_max_size == g2.metadata_max_size &&
+ g1.metadata_slot_count == g2.metadata_slot_count &&
+ g1.first_logical_sector == g2.first_logical_sector &&
+ g1.last_logical_sector == g2.last_logical_sector;
+}
+
+static std::string SerializeMetadata(const LpMetadata& input) {
+ LpMetadata metadata = input;
+ LpMetadataHeader& header = metadata.header;
+
+ // Serialize individual tables.
+ std::string partitions(reinterpret_cast<const char*>(metadata.partitions.data()),
+ metadata.partitions.size() * sizeof(LpMetadataPartition));
+ std::string extents(reinterpret_cast<const char*>(metadata.extents.data()),
+ metadata.extents.size() * sizeof(LpMetadataExtent));
+
+ // Compute positions of tables.
+ header.partitions.offset = 0;
+ header.extents.offset = header.partitions.offset + partitions.size();
+ header.tables_size = header.extents.offset + extents.size();
+
+ // Compute payload checksum.
+ std::string tables = partitions + extents;
+ SHA256(tables.data(), tables.size(), header.tables_checksum);
+
+ // Compute header checksum.
+ memset(header.header_checksum, 0, sizeof(header.header_checksum));
+ SHA256(&header, sizeof(header), header.header_checksum);
+
+ std::string header_blob =
+ std::string(reinterpret_cast<const char*>(&metadata.header), sizeof(metadata.header));
+ return header_blob + tables;
+}
+
+// Perform sanity checks so we don't accidentally overwrite valid metadata
+// with potentially invalid metadata, or random partition data with metadata.
+static bool ValidateGeometryAndMetadata(const LpMetadata& metadata, uint64_t blockdevice_size,
+ uint64_t metadata_size) {
+ const LpMetadataHeader& header = metadata.header;
+ const LpMetadataGeometry& geometry = metadata.geometry;
+ // Validate the usable sector range.
+ if (geometry.first_logical_sector > geometry.last_logical_sector) {
+ LERROR << "Logical partition metadata has invalid sector range.";
+ return false;
+ }
+ // Make sure we're writing within the space reserved.
+ if (metadata_size > geometry.metadata_max_size) {
+ LERROR << "Logical partition metadata is too large.";
+ return false;
+ }
+
+ // Make sure the device has enough space to store two backup copies of the
+ // metadata.
+ uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE +
+ uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
+ if (reserved_size > blockdevice_size ||
+ reserved_size > geometry.first_logical_sector * LP_SECTOR_SIZE) {
+ LERROR << "Not enough space to store all logical partition metadata slots.";
+ return false;
+ }
+ if (blockdevice_size - reserved_size < (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE) {
+ LERROR << "Not enough space to backup all logical partition metadata slots.";
+ return false;
+ }
+
+ // Make sure all partition entries reference valid extents.
+ for (const auto& partition : metadata.partitions) {
+ if (partition.first_extent_index + partition.num_extents > metadata.extents.size()) {
+ LERROR << "Partition references invalid extent.";
+ return false;
+ }
+ }
+
+ // Make sure all linear extents have a valid range.
+ for (const auto& extent : metadata.extents) {
+ if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+ uint64_t physical_sector = extent.target_data;
+ if (physical_sector < geometry.first_logical_sector ||
+ physical_sector + extent.num_sectors > geometry.last_logical_sector) {
+ LERROR << "Extent table entry is out of bounds.";
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool WritePartitionTable(int fd, const LpMetadata& metadata, SyncMode sync_mode,
+ uint32_t slot_number) {
+ uint64_t size;
+ if (!GetDescriptorSize(fd, &size)) {
+ return false;
+ }
+
+ const LpMetadataGeometry& geometry = metadata.geometry;
+ if (sync_mode != SyncMode::Flash) {
+ // Verify that the old geometry is identical. If it's not, then we've
+ // based this new metadata on invalid assumptions.
+ LpMetadataGeometry old_geometry;
+ if (!ReadLogicalPartitionGeometry(fd, &old_geometry)) {
+ return false;
+ }
+ if (!CompareGeometry(geometry, old_geometry)) {
+ LERROR << "Incompatible geometry in new logical partition metadata";
+ return false;
+ }
+ }
+
+ // Make sure we're writing to a valid metadata slot.
+ if (slot_number >= geometry.metadata_slot_count) {
+ LERROR << "Invalid logical partition metadata slot number.";
+ return false;
+ }
+
+ // Before writing geometry and/or logical partition tables, perform some
+ // basic checks that the geometry and tables are coherent, and will fit
+ // on the given block device.
+ std::string blob = SerializeMetadata(metadata);
+ if (!ValidateGeometryAndMetadata(metadata, size, blob.size())) {
+ return false;
+ }
+
+ // First write geometry if this is a flash operation. It gets written to
+ // the first and last 4096-byte regions of the device.
+ if (sync_mode == SyncMode::Flash) {
+ std::string blob = SerializeGeometry(metadata.geometry);
+ if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0";
+ return false;
+ }
+ if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+ PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+ return false;
+ }
+ if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE;
+ return false;
+ }
+ if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+ PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+ return false;
+ }
+ }
+
+ // Write the primary copy of the metadata.
+ int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number);
+ if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << primary_offset;
+ return false;
+ }
+ if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+ PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+ return false;
+ }
+
+ // Write the backup copy of the metadata.
+ int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number);
+ int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_END);
+ if (abs_offset == (int64_t)-1) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << backup_offset;
+ return false;
+ }
+ if (abs_offset < int64_t((geometry.last_logical_sector + 1) * LP_SECTOR_SIZE)) {
+ PERROR << __PRETTY_FUNCTION__ << "backup offset " << abs_offset
+ << " is within logical partition bounds, sector " << geometry.last_logical_sector;
+ return false;
+ }
+ if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+ PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+ return false;
+ }
+ return true;
+}
+
+bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
+ uint32_t slot_number) {
+ android::base::unique_fd fd(open(block_device, O_RDWR | O_SYNC));
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+ return false;
+ }
+ return WritePartitionTable(fd, metadata, sync_mode, slot_number);
+}
+
+bool WriteToImageFile(int fd, const LpMetadata& input) {
+ std::string geometry = SerializeGeometry(input.geometry);
+ std::string padding(LP_METADATA_GEOMETRY_SIZE - geometry.size(), '\0');
+ std::string metadata = SerializeMetadata(input);
+
+ std::string everything = geometry + padding + metadata;
+
+ if (!android::base::WriteFully(fd, everything.data(), everything.size())) {
+ PERROR << __PRETTY_FUNCTION__ << "write " << everything.size() << " bytes failed";
+ return false;
+ }
+ return true;
+}
+
+bool WriteToImageFile(const char* file, const LpMetadata& input) {
+ android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+ return false;
+ }
+ return WriteToImageFile(fd, input);
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
new file mode 100644
index 0000000..5497223
--- /dev/null
+++ b/fs_mgr/tests/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+ name: "fs_mgr_unit_test",
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ static_libs: [
+ "libfs_mgr",
+ "libfstab",
+ ],
+
+ srcs: [
+ "fs_mgr_test.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
new file mode 100644
index 0000000..2e76752
--- /dev/null
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+#include "../fs_mgr_priv_boot_config.h"
+
+namespace {
+
+const std::string cmdline =
+ "rcupdate.rcu_expedited=1 rootwait ro "
+ "init=/init androidboot.bootdevice=1d84000.ufshc "
+ "androidboot.baseband=sdy androidboot.keymaster=1 skip_initramfs "
+ "androidboot.serialno=BLAHBLAHBLAH androidboot.slot_suffix=_a "
+ "androidboot.hardware.platform=sdw813 androidboot.hardware=foo "
+ "androidboot.revision=EVT1.0 androidboot.bootloader=burp-0.1-7521 "
+ "androidboot.hardware.sku=mary androidboot.hardware.radio.subtype=0 "
+ "androidboot.dtbo_idx=2 androidboot.mode=normal "
+ "androidboot.hardware.ddr=1GB,combuchi,LPDDR4X "
+ "androidboot.ddr_info=combuchiandroidboot.ddr_size=2GB "
+ "androidboot.hardware.ufs=2GB,combushi "
+ "androidboot.boottime=0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123 "
+ "androidboot.ramdump=disabled "
+ "dm=\"1 vroot none ro 1,0 10416 verity 1 624684 fec_start 624684\" "
+ "root=/dev/dm-0 "
+ "androidboot.vbmeta.device=PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb "
+ "androidboot.vbmeta.avb_version=\"1.1\" "
+ "androidboot.vbmeta.device_state=unlocked "
+ "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=5248 "
+ "androidboot.vbmeta.digest="
+ "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860 "
+ "androidboot.vbmeta.invalidate_on_error=yes "
+ "androidboot.veritymode=enforcing androidboot.verifiedbootstate=orange "
+ "androidboot.space=\"sha256 5248 androidboot.nospace=nope\" "
+ "printk.devkmsg=on msm_rtb.filter=0x237 ehci-hcd.park=3 "
+ "\"string =\"\"string '\" "
+ "service_locator.enable=1 firmware_class.path=/vendor/firmware "
+ "cgroup.memory=nokmem lpm_levels.sleep_disabled=1 "
+ "buildvariant=userdebug console=null "
+ "terminator=\"truncated";
+
+const std::vector<std::pair<std::string, std::string>> result_space = {
+ {"rcupdate.rcu_expedited", "1"},
+ {"rootwait", ""},
+ {"ro", ""},
+ {"init", "/init"},
+ {"androidboot.bootdevice", "1d84000.ufshc"},
+ {"androidboot.baseband", "sdy"},
+ {"androidboot.keymaster", "1"},
+ {"skip_initramfs", ""},
+ {"androidboot.serialno", "BLAHBLAHBLAH"},
+ {"androidboot.slot_suffix", "_a"},
+ {"androidboot.hardware.platform", "sdw813"},
+ {"androidboot.hardware", "foo"},
+ {"androidboot.revision", "EVT1.0"},
+ {"androidboot.bootloader", "burp-0.1-7521"},
+ {"androidboot.hardware.sku", "mary"},
+ {"androidboot.hardware.radio.subtype", "0"},
+ {"androidboot.dtbo_idx", "2"},
+ {"androidboot.mode", "normal"},
+ {"androidboot.hardware.ddr", "1GB,combuchi,LPDDR4X"},
+ {"androidboot.ddr_info", "combuchiandroidboot.ddr_size=2GB"},
+ {"androidboot.hardware.ufs", "2GB,combushi"},
+ {"androidboot.boottime", "0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123"},
+ {"androidboot.ramdump", "disabled"},
+ {"dm", "1 vroot none ro 1,0 10416 verity 1 624684 fec_start 624684"},
+ {"root", "/dev/dm-0"},
+ {"androidboot.vbmeta.device", "PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb"},
+ {"androidboot.vbmeta.avb_version", "1.1"},
+ {"androidboot.vbmeta.device_state", "unlocked"},
+ {"androidboot.vbmeta.hash_alg", "sha256"},
+ {"androidboot.vbmeta.size", "5248"},
+ {"androidboot.vbmeta.digest",
+ "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860"},
+ {"androidboot.vbmeta.invalidate_on_error", "yes"},
+ {"androidboot.veritymode", "enforcing"},
+ {"androidboot.verifiedbootstate", "orange"},
+ {"androidboot.space", "sha256 5248 androidboot.nospace=nope"},
+ {"printk.devkmsg", "on"},
+ {"msm_rtb.filter", "0x237"},
+ {"ehci-hcd.park", "3"},
+ {"string ", "string '"},
+ {"service_locator.enable", "1"},
+ {"firmware_class.path", "/vendor/firmware"},
+ {"cgroup.memory", "nokmem"},
+ {"lpm_levels.sleep_disabled", "1"},
+ {"buildvariant", "userdebug"},
+ {"console", "null"},
+ {"terminator", "truncated"},
+};
+
+} // namespace
+
+TEST(fs_mgr, fs_mgr_parse_boot_config) {
+ EXPECT_EQ(result_space, fs_mgr_parse_boot_config(cmdline));
+}
+
+TEST(fs_mgr, fs_mgr_get_boot_config_from_kernel_cmdline) {
+ std::string content;
+ for (const auto& entry : result_space) {
+ static constexpr char androidboot[] = "androidboot.";
+ if (!android::base::StartsWith(entry.first, androidboot)) continue;
+ auto key = entry.first.substr(strlen(androidboot));
+ EXPECT_TRUE(fs_mgr_get_boot_config_from_kernel(cmdline, key, &content)) << " for " << key;
+ EXPECT_EQ(entry.second, content);
+ }
+ EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "vbmeta.avb_versio", &content));
+ EXPECT_TRUE(content.empty()) << content;
+ EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "nospace", &content));
+ EXPECT_TRUE(content.empty()) << content;
+}
diff --git a/fs_mgr/tools/Android.bp b/fs_mgr/tools/Android.bp
new file mode 100644
index 0000000..4d4aae4
--- /dev/null
+++ b/fs_mgr/tools/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_binary {
+ name: "dmctl",
+ srcs: ["dmctl.cpp"],
+
+ static_libs: [
+ "libfs_mgr",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ cflags: ["-Werror"],
+}
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
new file mode 100644
index 0000000..9d48b8c
--- /dev/null
+++ b/fs_mgr/tools/dmctl.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <linux/dm-ioctl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/parseint.h>
+#include <android-base/unique_fd.h>
+#include <libdm/dm.h>
+
+#include <functional>
+#include <iomanip>
+#include <ios>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+using DeviceMapper = ::android::dm::DeviceMapper;
+using DmTable = ::android::dm::DmTable;
+using DmTarget = ::android::dm::DmTarget;
+using DmTargetLinear = ::android::dm::DmTargetLinear;
+using DmTargetZero = ::android::dm::DmTargetZero;
+using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
+using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
+
+static int Usage(void) {
+ std::cerr << "usage: dmctl <command> [command options]" << std::endl;
+ std::cerr << "commands:" << std::endl;
+ std::cerr << " create <dm-name> [-ro] <targets...>" << std::endl;
+ std::cerr << " delete <dm-name>" << std::endl;
+ std::cerr << " list <devices | targets>" << std::endl;
+ std::cerr << " getpath <dm-name>" << std::endl;
+ std::cerr << " help" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << "Target syntax:" << std::endl;
+ std::cerr << " <target_type> <start_sector> <num_sectors> [target_data]" << std::endl;
+ return -EINVAL;
+}
+
+class TargetParser final {
+ public:
+ TargetParser(int argc, char** argv) : arg_index_(0), argc_(argc), argv_(argv) {}
+
+ bool More() const { return arg_index_ < argc_; }
+ std::unique_ptr<DmTarget> Next() {
+ if (!HasArgs(3)) {
+ std::cerr << "Expected <target_type> <start_sector> <num_sectors>" << std::endl;
+ return nullptr;
+ }
+
+ std::string target_type = NextArg();
+ uint64_t start_sector, num_sectors;
+ if (!android::base::ParseUint(NextArg(), &start_sector)) {
+ std::cerr << "Expected start sector, got: " << PreviousArg() << std::endl;
+ return nullptr;
+ }
+ if (!android::base::ParseUint(NextArg(), &num_sectors) || !num_sectors) {
+ std::cerr << "Expected non-zero sector count, got: " << PreviousArg() << std::endl;
+ return nullptr;
+ }
+
+ if (target_type == "zero") {
+ return std::make_unique<DmTargetZero>(start_sector, num_sectors);
+ } else if (target_type == "linear") {
+ if (!HasArgs(2)) {
+ std::cerr << "Expected \"linear\" <block_device> <sector>" << std::endl;
+ return nullptr;
+ }
+
+ std::string block_device = NextArg();
+ uint64_t physical_sector;
+ if (!android::base::ParseUint(NextArg(), &physical_sector)) {
+ std::cerr << "Expected sector, got: \"" << PreviousArg() << "\"" << std::endl;
+ return nullptr;
+ }
+ return std::make_unique<DmTargetLinear>(start_sector, num_sectors, block_device,
+ physical_sector);
+ } else {
+ std::cerr << "Unrecognized target type: " << target_type << std::endl;
+ return nullptr;
+ }
+ }
+
+ private:
+ bool HasArgs(int count) { return arg_index_ + count <= argc_; }
+ const char* NextArg() {
+ CHECK(arg_index_ < argc_);
+ return argv_[arg_index_++];
+ }
+ const char* PreviousArg() {
+ CHECK(arg_index_ >= 0);
+ return argv_[arg_index_ - 1];
+ }
+
+ private:
+ int arg_index_;
+ int argc_;
+ char** argv_;
+};
+
+static int DmCreateCmdHandler(int argc, char** argv) {
+ if (argc < 1) {
+ std::cerr << "Usage: dmctl create <dm-name> [-ro] <targets...>" << std::endl;
+ return -EINVAL;
+ }
+ std::string name = argv[0];
+
+ // Parse extended options first.
+ DmTable table;
+ int arg_index = 1;
+ while (arg_index < argc && argv[arg_index][0] == '-') {
+ if (strcmp(argv[arg_index], "-ro") == 0) {
+ table.set_readonly(true);
+ } else {
+ std::cerr << "Unrecognized option: " << argv[arg_index] << std::endl;
+ return -EINVAL;
+ }
+ arg_index++;
+ }
+
+ // Parse everything else as target information.
+ TargetParser parser(argc - arg_index, argv + arg_index);
+ while (parser.More()) {
+ std::unique_ptr<DmTarget> target = parser.Next();
+ if (!target || !table.AddTarget(std::move(target))) {
+ return -EINVAL;
+ }
+ }
+
+ if (table.num_targets() == 0) {
+ std::cerr << "Must define at least one target." << std::endl;
+ return -EINVAL;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.CreateDevice(name, table)) {
+ std::cerr << "Failed to create device-mapper device with name: " << name << std::endl;
+ return -EIO;
+ }
+ return 0;
+}
+
+static int DmDeleteCmdHandler(int argc, char** argv) {
+ if (argc < 1) {
+ std::cerr << "Usage: dmctl delete <name>" << std::endl;
+ return -EINVAL;
+ }
+
+ std::string name = argv[0];
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.DeleteDevice(name)) {
+ std::cerr << "Failed to delete [" << name << "]" << std::endl;
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int DmListTargets(DeviceMapper& dm) {
+ std::vector<DmTargetTypeInfo> targets;
+ if (!dm.GetAvailableTargets(&targets)) {
+ std::cerr << "Failed to read available device mapper targets" << std::endl;
+ return -errno;
+ }
+
+ std::cout << "Available Device Mapper Targets:" << std::endl;
+ if (targets.empty()) {
+ std::cout << " <empty>" << std::endl;
+ return 0;
+ }
+
+ for (const auto& target : targets) {
+ std::cout << std::left << std::setw(20) << target.name() << " : " << target.version()
+ << std::endl;
+ }
+
+ return 0;
+}
+
+static int DmListDevices(DeviceMapper& dm) {
+ std::vector<DmBlockDevice> devices;
+ if (!dm.GetAvailableDevices(&devices)) {
+ std::cerr << "Failed to read available device mapper devices" << std::endl;
+ return -errno;
+ }
+ std::cout << "Available Device Mapper Devices:" << std::endl;
+ if (devices.empty()) {
+ std::cout << " <empty>" << std::endl;
+ return 0;
+ }
+
+ for (const auto& dev : devices) {
+ std::cout << std::left << std::setw(20) << dev.name() << " : " << dev.Major() << ":"
+ << dev.Minor() << std::endl;
+ }
+
+ return 0;
+}
+
+static const std::map<std::string, std::function<int(DeviceMapper&)>> listmap = {
+ {"targets", DmListTargets},
+ {"devices", DmListDevices},
+};
+
+static int DmListCmdHandler(int argc, char** argv) {
+ if (argc < 1) {
+ std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+ return -EINVAL;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ for (const auto& l : listmap) {
+ if (l.first == argv[0]) return l.second(dm);
+ }
+
+ std::cerr << "Invalid argument to \'dmctl list\': " << argv[0] << std::endl;
+ return -EINVAL;
+}
+
+static int HelpCmdHandler(int /* argc */, char** /* argv */) {
+ Usage();
+ return 0;
+}
+
+static int GetPathCmdHandler(int argc, char** argv) {
+ if (argc != 1) {
+ std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+ return -EINVAL;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ std::string path;
+ if (!dm.GetDmDevicePathByName(argv[0], &path)) {
+ std::cerr << "Could not query path of device \"" << argv[0] << "\"." << std::endl;
+ return -EINVAL;
+ }
+ std::cout << path << std::endl;
+ return 0;
+}
+
+static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
+ // clang-format off
+ {"create", DmCreateCmdHandler},
+ {"delete", DmDeleteCmdHandler},
+ {"list", DmListCmdHandler},
+ {"help", HelpCmdHandler},
+ {"getpath", GetPathCmdHandler},
+ // clang-format on
+};
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv, &android::base::StderrLogger);
+ if (argc < 2) {
+ return Usage();
+ }
+
+ for (const auto& cmd : cmdmap) {
+ if (cmd.first == argv[1]) {
+ return cmd.second(argc - 2, argv + 2);
+ }
+ }
+
+ return Usage();
+}
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 61c8804..abb387c 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -234,11 +234,13 @@
virtual int verify(uint32_t uid,
const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
const uint8_t *provided_password, uint32_t provided_password_length, bool *request_reenroll) {
- uint8_t *auth_token;
+ uint8_t *auth_token = nullptr;
uint32_t auth_token_length;
- return verifyChallenge(uid, 0, enrolled_password_handle, enrolled_password_handle_length,
+ int ret = verifyChallenge(uid, 0, enrolled_password_handle, enrolled_password_handle_length,
provided_password, provided_password_length,
&auth_token, &auth_token_length, request_reenroll);
+ delete [] auth_token;
+ return ret;
}
virtual int verifyChallenge(uint32_t uid, uint64_t challenge,
diff --git a/init/Android.bp b/init/Android.bp
index 25877c0..cf7637f 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -67,18 +67,9 @@
"libsquashfs_utils",
"liblogwrap",
"libext4_utils",
- "libcutils",
- "libbase",
- "libc",
"libseccomp_policy",
- "libselinux",
- "liblog",
"libcrypto_utils",
- "libcrypto",
- "libc++_static",
- "libdl",
"libsparse",
- "libz",
"libprocessgroup",
"libavb",
"libkeyutils",
@@ -86,6 +77,17 @@
"libpropertyinfoserializer",
"libpropertyinfoparser",
],
+ shared_libs: [
+ "libcutils",
+ "libbase",
+ "libc",
+ "liblog",
+ "libcrypto",
+ "libc++",
+ "libdl",
+ "libz",
+ "libselinux",
+ ],
}
cc_library_static {
@@ -166,7 +168,6 @@
cc_test {
name: "init_tests",
defaults: ["init_defaults"],
- static_executable: true,
srcs: [
"devices_test.cpp",
"init_test.cpp",
@@ -187,7 +188,6 @@
cc_benchmark {
name: "init_benchmarks",
- static_executable: true,
defaults: ["init_defaults"],
srcs: [
"subcontext_benchmark.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index c4a6a50..a81a0f6 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -45,7 +45,6 @@
LOCAL_MODULE:= init
-LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
@@ -59,18 +58,9 @@
libsquashfs_utils \
liblogwrap \
libext4_utils \
- libcutils \
- libbase \
- libc \
libseccomp_policy \
- libselinux \
- liblog \
libcrypto_utils \
- libcrypto \
- libc++_static \
- libdl \
libsparse \
- libz \
libprocessgroup \
libavb \
libkeyutils \
@@ -78,6 +68,26 @@
libpropertyinfoserializer \
libpropertyinfoparser \
+shared_libs := \
+ libcutils \
+ libbase \
+ liblog \
+ libcrypto \
+ libdl \
+ libz \
+ libselinux \
+
+ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+# init is static executable for non-system-as-root devices, because the dynamic linker
+# and shared libs are not available before /system is mounted, but init has to run
+# before the partition is mounted.
+LOCAL_STATIC_LIBRARIES += $(shared_libs) libc++_static
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+else
+LOCAL_SHARED_LIBRARIES := $(shared_libs) libc++
+endif
+shared_libs :=
+
LOCAL_REQUIRED_MODULES := \
e2fsdroid \
mke2fs \
diff --git a/init/README.md b/init/README.md
index 550ef05..b0a73b9 100644
--- a/init/README.md
+++ b/init/README.md
@@ -752,3 +752,22 @@
kill -SIGCONT 4343
> strace runs
+
+Host Init Script Verification
+-----------------------------
+
+Init scripts are checked for correctness during build time. Specifically the below is checked.
+
+1) Well formatted action, service and import sections, e.g. no actions without a preceding 'on'
+line, and no extraneous lines after an 'import' statement.
+2) All commands map to a valid keyword and the argument count is within the correct range.
+3) All service options are valid. This is stricter than how commands are checked as the service
+options' arguments are fully parsed, e.g. UIDs and GIDs must resolve.
+
+There are other parts of init scripts that are only parsed at runtime and therefore not checked
+during build time, among them are the below.
+
+1) The validity of the arguments of commands, e.g. no checking if file paths actually exist, if
+SELinux would permit the operation, or if the UIDs and GIDs resolve.
+2) No checking if a service exists or has a valid SELinux domain defined
+3) No checking if a service has not been previously defined in a different init script.
diff --git a/init/host_import_parser.cpp b/init/host_import_parser.cpp
index faf6fc1..93e363f 100644
--- a/init/host_import_parser.cpp
+++ b/init/host_import_parser.cpp
@@ -23,22 +23,17 @@
namespace android {
namespace init {
-Result<Success> HostImportParser::ParseSection(std::vector<std::string>&& args,
- const std::string& filename, int line) {
+Result<Success> HostImportParser::ParseSection(std::vector<std::string>&& args, const std::string&,
+ int) {
if (args.size() != 2) {
return Error() << "single argument needed for import\n";
}
- auto import_path = args[1];
+ return Success();
+}
- if (StartsWith(import_path, "/system") || StartsWith(import_path, "/product") ||
- StartsWith(import_path, "/odm") || StartsWith(import_path, "/vendor")) {
- import_path = out_dir_ + "/" + import_path;
- } else {
- import_path = out_dir_ + "/root/" + import_path;
- }
-
- return ImportParser::ParseSection({"import", import_path}, filename, line);
+Result<Success> HostImportParser::ParseLineSection(std::vector<std::string>&&, int) {
+ return Error() << "Unexpected line found after import statement";
}
} // namespace init
diff --git a/init/host_import_parser.h b/init/host_import_parser.h
index e2980b2..52b8891 100644
--- a/init/host_import_parser.h
+++ b/init/host_import_parser.h
@@ -19,21 +19,16 @@
#include <string>
#include <vector>
-#include "import_parser.h"
#include "parser.h"
namespace android {
namespace init {
-class HostImportParser : public ImportParser {
+class HostImportParser : public SectionParser {
public:
- HostImportParser(const std::string& out_dir, Parser* parser)
- : ImportParser(parser), out_dir_(out_dir) {}
- Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line) override;
-
- private:
- std::string out_dir_;
+ HostImportParser() {}
+ Result<Success> ParseSection(std::vector<std::string>&& args, const std::string&, int) override;
+ Result<Success> ParseLineSection(std::vector<std::string>&&, int) override;
};
} // namespace init
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index d6884af..8407729 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -17,6 +17,7 @@
#include <errno.h>
#include <pwd.h>
#include <stdio.h>
+#include <stdlib.h>
#include <iostream>
#include <string>
@@ -45,11 +46,11 @@
using android::base::ReadFileToString;
using android::base::Split;
-static std::string out_dir;
+static std::string passwd_file;
static std::vector<std::pair<std::string, int>> GetVendorPasswd() {
std::string passwd;
- if (!ReadFileToString(out_dir + "/vendor/etc/passwd", &passwd)) {
+ if (!ReadFileToString(passwd_file, &passwd)) {
return {};
}
@@ -102,6 +103,14 @@
}
}
+ unsigned int oem_uid;
+ if (sscanf(login, "oem_%u", &oem_uid) == 1) {
+ snprintf(static_name, sizeof(static_name), "%s", login);
+ static_passwd.pw_uid = oem_uid;
+ static_passwd.pw_gid = oem_uid;
+ return &static_passwd;
+ }
+
errno = ENOENT;
return nullptr;
}
@@ -118,20 +127,14 @@
int main(int argc, char** argv) {
android::base::InitLogging(argv, &android::base::StdioLogger);
android::base::SetMinimumLogSeverity(android::base::ERROR);
- if (argc != 3) {
- LOG(ERROR) << "Usage: " << argv[0] << " <out directory> <properties>";
- return -1;
+
+ if (argc != 2 && argc != 3) {
+ LOG(ERROR) << "Usage: " << argv[0] << " <init rc file> [passwd file]";
+ return EXIT_FAILURE;
}
- out_dir = argv[1];
-
- auto properties = Split(argv[2], ",");
- for (const auto& property : properties) {
- auto split_property = Split(property, "=");
- if (split_property.size() != 2) {
- continue;
- }
- property_set(split_property[0], split_property[1]);
+ if (argc == 3) {
+ passwd_file = argv[2];
}
const BuiltinFunctionMap function_map;
@@ -141,22 +144,23 @@
Parser parser;
parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
- parser.AddSectionParser("import", std::make_unique<HostImportParser>(out_dir, &parser));
+ parser.AddSectionParser("import", std::make_unique<HostImportParser>());
- if (!parser.ParseConfig(argv[1] + "/root/init.rc"s)) {
- LOG(ERROR) << "Failed to find root init.rc script";
- return -1;
+ if (!parser.ParseConfigFileInsecure(argv[1])) {
+ LOG(ERROR) << "Failed to open init rc script '" << argv[1] << "'";
+ return EXIT_FAILURE;
}
if (parser.parse_error_count() > 0) {
- LOG(ERROR) << "Init script parsing failed with " << parser.parse_error_count() << " errors";
- return -1;
+ LOG(ERROR) << "Failed to parse init script '" << argv[1] << "' with "
+ << parser.parse_error_count() << " errors";
+ return EXIT_FAILURE;
}
- return 0;
+ return EXIT_SUCCESS;
}
} // namespace init
} // namespace android
int main(int argc, char** argv) {
- android::init::main(argc, argv);
+ return android::init::main(argc, argv);
}
diff --git a/init/init.cpp b/init/init.cpp
index 82648d9..77c4fc4 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -352,21 +352,23 @@
}
static void export_kernel_boot_props() {
+ constexpr const char* UNSET = "";
struct {
const char *src_prop;
const char *dst_prop;
const char *default_value;
} prop_map[] = {
- { "ro.boot.serialno", "ro.serialno", "", },
+ { "ro.boot.serialno", "ro.serialno", UNSET, },
{ "ro.boot.mode", "ro.bootmode", "unknown", },
{ "ro.boot.baseband", "ro.baseband", "unknown", },
{ "ro.boot.bootloader", "ro.bootloader", "unknown", },
{ "ro.boot.hardware", "ro.hardware", "unknown", },
{ "ro.boot.revision", "ro.revision", "0", },
};
- for (size_t i = 0; i < arraysize(prop_map); i++) {
- std::string value = GetProperty(prop_map[i].src_prop, "");
- property_set(prop_map[i].dst_prop, (!value.empty()) ? value : prop_map[i].default_value);
+ for (const auto& prop : prop_map) {
+ std::string value = GetProperty(prop.src_prop, prop.default_value);
+ if (value != UNSET)
+ property_set(prop.dst_prop, value);
}
}
@@ -604,47 +606,61 @@
if (is_first_stage) {
boot_clock::time_point start_time = boot_clock::now();
+ std::vector<std::pair<std::string, int>> errors;
+#define CHECKCALL(x) \
+ if (x != 0) errors.emplace_back(#x " failed", errno);
+
// Clear the umask.
umask(0);
- clearenv();
- setenv("PATH", _PATH_DEFPATH, 1);
+ CHECKCALL(clearenv());
+ CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
// Get the basic filesystem setup we need put together in the initramdisk
// on / and then we'll let the rc file figure out the rest.
- mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
- mkdir("/dev/pts", 0755);
- mkdir("/dev/socket", 0755);
- mount("devpts", "/dev/pts", "devpts", 0, NULL);
- #define MAKE_STR(x) __STRING(x)
- mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
+ CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
+ CHECKCALL(mkdir("/dev/pts", 0755));
+ CHECKCALL(mkdir("/dev/socket", 0755));
+ CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
+#define MAKE_STR(x) __STRING(x)
+ CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
+#undef MAKE_STR
// Don't expose the raw commandline to unprivileged processes.
- chmod("/proc/cmdline", 0440);
+ CHECKCALL(chmod("/proc/cmdline", 0440));
gid_t groups[] = { AID_READPROC };
- setgroups(arraysize(groups), groups);
- mount("sysfs", "/sys", "sysfs", 0, NULL);
- mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
+ CHECKCALL(setgroups(arraysize(groups), groups));
+ CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
+ CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
- mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
+ CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
if constexpr (WORLD_WRITABLE_KMSG) {
- mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
+ CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
}
- mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
- mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
+ CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
+ CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
// Mount staging areas for devices managed by vold
// See storage config details at http://source.android.com/devices/storage/
- mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
- "mode=0755,uid=0,gid=1000");
+ CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+ "mode=0755,uid=0,gid=1000"));
// /mnt/vendor is used to mount vendor-specific partitions that can not be
// part of the vendor partition, e.g. because they are mounted read-write.
- mkdir("/mnt/vendor", 0755);
+ CHECKCALL(mkdir("/mnt/vendor", 0755));
+
+#undef CHECKCALL
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
// talk to the outside world...
InitKernelLogging(argv);
+ if (!errors.empty()) {
+ for (const auto& [error_string, error_errno] : errors) {
+ LOG(ERROR) << error_string << " " << strerror(error_errno);
+ }
+ LOG(FATAL) << "Init encountered errors starting first stage, aborting";
+ }
+
LOG(INFO) << "init first stage started!";
if (!DoFirstStageMount()) {
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index db60ce1..2bc9f3a 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -29,6 +29,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
+#include <liblp/metadata_format.h>
#include "devices.h"
#include "fs_mgr.h"
@@ -75,6 +76,7 @@
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
std::unique_ptr<LogicalPartitionTable> dm_linear_table_;
+ std::string lp_metadata_partition_;
std::vector<fstab_rec*> mount_fstab_recs_;
std::set<std::string> required_devices_partition_names_;
std::unique_ptr<DeviceHandler> device_handler_;
@@ -120,13 +122,17 @@
}
static inline bool IsDmLinearEnabled() {
- bool enabled = false;
- import_kernel_cmdline(
- false, [&enabled](const std::string& key, const std::string& value, bool in_qemu) {
- if (key == "androidboot.logical_partitions" && value == "1") {
- enabled = true;
- }
- });
+ static bool checked = false;
+ static bool enabled = false;
+ if (checked) {
+ return enabled;
+ }
+ import_kernel_cmdline(false, [](const std::string& key, const std::string& value, bool in_qemu) {
+ if (key == "androidboot.logical_partitions" && value == "1") {
+ enabled = true;
+ }
+ });
+ checked = true;
return enabled;
}
@@ -163,7 +169,7 @@
}
bool FirstStageMount::DoFirstStageMount() {
- if (!dm_linear_table_ && mount_fstab_recs_.empty()) {
+ if (!IsDmLinearEnabled() && mount_fstab_recs_.empty()) {
// Nothing to mount.
LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
return true;
@@ -184,14 +190,18 @@
bool FirstStageMount::GetBackingDmLinearDevices() {
// Add any additional devices required for dm-linear mappings.
- if (!dm_linear_table_) {
+ if (!IsDmLinearEnabled()) {
return true;
}
- for (const auto& partition : dm_linear_table_->partitions) {
- for (const auto& extent : partition.extents) {
- const std::string& partition_name = android::base::Basename(extent.block_device());
- required_devices_partition_names_.emplace(partition_name);
+ required_devices_partition_names_.emplace(LP_METADATA_PARTITION_NAME);
+
+ if (dm_linear_table_) {
+ for (const auto& partition : dm_linear_table_->partitions) {
+ for (const auto& extent : partition.extents) {
+ const std::string& partition_name = android::base::Basename(extent.block_device());
+ required_devices_partition_names_.emplace(partition_name);
+ }
}
}
return true;
@@ -205,7 +215,7 @@
return true;
}
- if (dm_linear_table_ || need_dm_verity_) {
+ if (IsDmLinearEnabled() || need_dm_verity_) {
const std::string dm_path = "/devices/virtual/misc/device-mapper";
bool found = false;
auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
@@ -253,10 +263,21 @@
}
bool FirstStageMount::CreateLogicalPartitions() {
- if (!dm_linear_table_) {
+ if (!IsDmLinearEnabled()) {
return true;
}
- return android::fs_mgr::CreateLogicalPartitions(*dm_linear_table_.get());
+
+ if (lp_metadata_partition_.empty()) {
+ LOG(ERROR) << "Could not locate logical partition tables in partition "
+ << LP_METADATA_PARTITION_NAME;
+ return false;
+ }
+ if (dm_linear_table_) {
+ if (!android::fs_mgr::CreateLogicalPartitions(*dm_linear_table_.get())) {
+ return false;
+ }
+ }
+ return android::fs_mgr::CreateLogicalPartitions(lp_metadata_partition_);
}
ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent) {
@@ -266,6 +287,10 @@
auto iter = required_devices_partition_names_.find(name);
if (iter != required_devices_partition_names_.end()) {
LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
+ if (IsDmLinearEnabled() && name == LP_METADATA_PARTITION_NAME) {
+ std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
+ lp_metadata_partition_ = links[0];
+ }
required_devices_partition_names_.erase(iter);
device_handler_->HandleDeviceEvent(uevent);
if (required_devices_partition_names_.empty()) {
diff --git a/init/parser.cpp b/init/parser.cpp
index ee6ee06..fa0fd11 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -19,6 +19,7 @@
#include <dirent.h>
#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -39,14 +40,13 @@
line_callbacks_.emplace_back(prefix, callback);
}
-void Parser::ParseData(const std::string& filename, const std::string& data) {
- // TODO: Use a parser with const input and remove this copy
- std::vector<char> data_copy(data.begin(), data.end());
- data_copy.push_back('\0');
+void Parser::ParseData(const std::string& filename, std::string* data) {
+ data->push_back('\n'); // TODO: fix tokenizer
+ data->push_back('\0');
parse_state state;
state.line = 0;
- state.ptr = &data_copy[0];
+ state.ptr = data->data();
state.nexttoken = 0;
SectionParser* section_parser = nullptr;
@@ -69,25 +69,29 @@
switch (next_token(&state)) {
case T_EOF:
end_section();
+
+ for (const auto& [section_name, section_parser] : section_parsers_) {
+ section_parser->EndFile();
+ }
+
return;
- case T_NEWLINE:
+ case T_NEWLINE: {
state.line++;
if (args.empty()) break;
// If we have a line matching a prefix we recognize, call its callback and unset any
// current section parsers. This is meant for /sys/ and /dev/ line entries for
// uevent.
- for (const auto& [prefix, callback] : line_callbacks_) {
- if (android::base::StartsWith(args[0], prefix)) {
- end_section();
+ auto line_callback = std::find_if(
+ line_callbacks_.begin(), line_callbacks_.end(),
+ [&args](const auto& c) { return android::base::StartsWith(args[0], c.first); });
+ if (line_callback != line_callbacks_.end()) {
+ end_section();
- if (auto result = callback(std::move(args)); !result) {
- parse_error_count_++;
- LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
- }
- break;
+ if (auto result = line_callback->second(std::move(args)); !result) {
+ parse_error_count_++;
+ LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
- }
- if (section_parsers_.count(args[0])) {
+ } else if (section_parsers_.count(args[0])) {
end_section();
section_parser = section_parsers_[args[0]].get();
section_start_line = state.line;
@@ -111,6 +115,7 @@
}
args.clear();
break;
+ }
case T_TEXT:
args.emplace_back(state.text);
break;
@@ -118,6 +123,16 @@
}
}
+bool Parser::ParseConfigFileInsecure(const std::string& path) {
+ std::string config_contents;
+ if (!android::base::ReadFileToString(path, &config_contents)) {
+ return false;
+ }
+
+ ParseData(path, &config_contents);
+ return true;
+}
+
bool Parser::ParseConfigFile(const std::string& path) {
LOG(INFO) << "Parsing file " << path << "...";
android::base::Timer t;
@@ -127,11 +142,7 @@
return false;
}
- config_contents->push_back('\n'); // TODO: fix parse_config.
- ParseData(path, *config_contents);
- for (const auto& [section_name, section_parser] : section_parsers_) {
- section_parser->EndFile();
- }
+ ParseData(path, &config_contents.value());
LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
return true;
diff --git a/init/parser.h b/init/parser.h
index 3501d8c..2454b6a 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -75,10 +75,13 @@
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
+ // Host init verifier check file permissions.
+ bool ParseConfigFileInsecure(const std::string& path);
+
size_t parse_error_count() const { return parse_error_count_; }
private:
- void ParseData(const std::string& filename, const std::string& data);
+ void ParseData(const std::string& filename, std::string* data);
bool ParseConfigFile(const std::string& path);
bool ParseConfigDir(const std::string& path);
diff --git a/init/service.cpp b/init/service.cpp
index 565cae7..95b37ab 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -787,9 +787,9 @@
flags_ |= SVC_EXEC;
is_exec_service_running_ = true;
- LOG(INFO) << "SVC_EXEC pid " << pid_ << " (uid " << uid_ << " gid " << gid_ << "+"
- << supp_gids_.size() << " context " << (!seclabel_.empty() ? seclabel_ : "default")
- << ") started; waiting...";
+ LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << uid_ << " gid "
+ << gid_ << "+" << supp_gids_.size() << " context "
+ << (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
return Success();
}
diff --git a/libcutils/OWNERS b/libcutils/OWNERS
new file mode 100644
index 0000000..c18ed51
--- /dev/null
+++ b/libcutils/OWNERS
@@ -0,0 +1,4 @@
+cferris@google.com
+enh@google.com
+jmgao@google.com
+tomcherry@google.com
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 6169324..267b7b3 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -154,6 +154,7 @@
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump64" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/debuggerd" },
{ 00750, AID_ROOT, AID_ROOT, 0, "system/bin/install-recovery.sh" },
+ { 00550, AID_LOGD, AID_LOGD, 0, "system/bin/logd" },
{ 00700, AID_ROOT, AID_ROOT, 0, "system/bin/secilc" },
{ 00750, AID_ROOT, AID_ROOT, 0, "system/bin/uncrypt" },
{ 00600, AID_ROOT, AID_ROOT, 0, "system/build.prop" },
@@ -179,12 +180,6 @@
// in user builds.
{ 00700, AID_SYSTEM, AID_SHELL, CAP_MASK_LONG(CAP_BLOCK_SUSPEND),
"system/bin/inputflinger" },
- { 00550, AID_LOGD, AID_LOGD, CAP_MASK_LONG(CAP_SYSLOG) |
- CAP_MASK_LONG(CAP_AUDIT_CONTROL) |
- CAP_MASK_LONG(CAP_SETGID),
- "system/bin/logd" },
- { 00550, AID_SYSTEM, AID_LOG, CAP_MASK_LONG(CAP_SYSLOG),
- "system/bin/bootstat" },
{ 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) |
CAP_MASK_LONG(CAP_SETGID),
"system/bin/run-as" },
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index 52cbe8b..ee9220d 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -115,16 +115,8 @@
*/
int __android_log_print(int prio, const char* tag, const char* fmt, ...)
#if defined(__GNUC__)
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
- __attribute__((__format__(gnu_printf, 3, 4)))
-#else
__attribute__((__format__(printf, 3, 4)))
#endif
-#else
- __attribute__((__format__(printf, 3, 4)))
-#endif
-#endif
;
/**
@@ -133,16 +125,8 @@
*/
int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap)
#if defined(__GNUC__)
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
- __attribute__((__format__(gnu_printf, 3, 0)))
-#else
__attribute__((__format__(printf, 3, 0)))
#endif
-#else
- __attribute__((__format__(printf, 3, 0)))
-#endif
-#endif
;
/**
@@ -164,16 +148,8 @@
...)
#if defined(__GNUC__)
__attribute__((__noreturn__))
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
- __attribute__((__format__(gnu_printf, 3, 4)))
-#else
__attribute__((__format__(printf, 3, 4)))
#endif
-#else
- __attribute__((__format__(printf, 3, 4)))
-#endif
-#endif
;
#ifndef log_id_t_defined
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
index 329ba85..6d78ed6 100644
--- a/liblog/tests/libc_test.cpp
+++ b/liblog/tests/libc_test.cpp
@@ -21,6 +21,7 @@
TEST(libc, __pstore_append) {
#ifdef __ANDROID__
+#ifndef NO_PSTORE
FILE* fp;
ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "a")));
static const char message[] = "libc.__pstore_append\n";
@@ -43,6 +44,9 @@
"Reboot, ensure string libc.__pstore_append is in "
"/sys/fs/pstore/pmsg-ramoops-0\n");
}
+#else /* NO_PSTORE */
+ GTEST_LOG_(INFO) << "This test does nothing because of NO_PSTORE.\n";
+#endif /* NO_PSTORE */
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 597a6bb..a8a9a12 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -3180,113 +3180,6 @@
}
#endif // USING_LOGGER_DEFAULT
-#ifdef USING_LOGGER_DEFAULT // Do not retest event mapping functionality
-#ifdef __ANDROID__
-// must be: '<needle:> 0 kB'
-static bool isZero(const std::string& content, std::string::size_type pos,
- const char* needle) {
- std::string::size_type offset = content.find(needle, pos);
- return (offset != std::string::npos) &&
- ((offset = content.find_first_not_of(" \t", offset + strlen(needle))) !=
- std::string::npos) &&
- (content.find_first_not_of('0', offset) != offset);
-}
-
-// must not be: '<needle:> 0 kB'
-static bool isNotZero(const std::string& content, std::string::size_type pos,
- const char* needle) {
- std::string::size_type offset = content.find(needle, pos);
- return (offset != std::string::npos) &&
- ((offset = content.find_first_not_of(" \t", offset + strlen(needle))) !=
- std::string::npos) &&
- (content.find_first_not_of("123456789", offset) != offset);
-}
-
-static void event_log_tags_test_smap(pid_t pid) {
- std::string filename = android::base::StringPrintf("/proc/%d/smaps", pid);
-
- std::string content;
- if (!android::base::ReadFileToString(filename, &content)) return;
-
- bool shared_ok = false;
- bool private_ok = false;
- bool anonymous_ok = false;
- bool pass_ok = false;
-
- static const char event_log_tags[] = "event-log-tags";
- std::string::size_type pos = 0;
- while ((pos = content.find(event_log_tags, pos)) != std::string::npos) {
- pos += strlen(event_log_tags);
-
- // must not be: 'Shared_Clean: 0 kB'
- bool ok =
- isNotZero(content, pos, "Shared_Clean:") ||
- // If not /etc/event-log-tags, thus r/w, then half points
- // back for not 'Shared_Dirty: 0 kB'
- ((content.substr(pos - 5 - strlen(event_log_tags), 5) != "/etc/") &&
- isNotZero(content, pos, "Shared_Dirty:"));
- if (ok && !pass_ok) {
- shared_ok = true;
- } else if (!ok) {
- shared_ok = false;
- }
-
- // must be: 'Private_Dirty: 0 kB' and 'Private_Clean: 0 kB'
- ok = isZero(content, pos, "Private_Dirty:") ||
- isZero(content, pos, "Private_Clean:");
- if (ok && !pass_ok) {
- private_ok = true;
- } else if (!ok) {
- private_ok = false;
- }
-
- // must be: 'Anonymous: 0 kB'
- ok = isZero(content, pos, "Anonymous:");
- if (ok && !pass_ok) {
- anonymous_ok = true;
- } else if (!ok) {
- anonymous_ok = false;
- }
-
- pass_ok = true;
- }
- content = "";
-
- if (!pass_ok) return;
- if (shared_ok && anonymous_ok && private_ok) return;
-
- filename = android::base::StringPrintf("/proc/%d/comm", pid);
- android::base::ReadFileToString(filename, &content);
- content = android::base::StringPrintf(
- "%d:%s", pid, content.substr(0, content.find('\n')).c_str());
-
- EXPECT_TRUE(IsOk(shared_ok, content));
- EXPECT_TRUE(IsOk(private_ok, content));
- EXPECT_TRUE(IsOk(anonymous_ok, content));
-}
-#endif // __ANDROID__
-
-TEST(liblog, event_log_tags) {
-#ifdef __ANDROID__
- std::unique_ptr<DIR, int (*)(DIR*)> proc_dir(opendir("/proc"), closedir);
- ASSERT_FALSE(!proc_dir);
-
- dirent* e;
- while ((e = readdir(proc_dir.get()))) {
- if (e->d_type != DT_DIR) continue;
- if (!isdigit(e->d_name[0])) continue;
- long long id = atoll(e->d_name);
- if (id <= 0) continue;
- pid_t pid = id;
- if (id != pid) continue;
- event_log_tags_test_smap(pid);
- }
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-#endif // USING_LOGGER_DEFAULT
-
#ifdef USING_LOGGER_DEFAULT // Do not retest ratelimit
TEST(liblog, __android_log_ratelimit) {
time_t state = 0;
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
index 444a5ac..443c3ea 100644
--- a/liblog/tests/log_read_test.cpp
+++ b/liblog/tests/log_read_test.cpp
@@ -27,6 +27,7 @@
// Test the APIs in this standalone include file
#include <log/log_read.h>
// Do not use anything in log/log_time.h despite side effects of the above.
+#include <private/android_logger.h>
TEST(liblog, __android_log_write__android_logger_list_read) {
#ifdef __ANDROID__
@@ -105,7 +106,10 @@
// framework (b/68266385).
EXPECT_LE( // boolean 1 or 0 depending on expected content or empty
!!((strcmp("crash", name) != 0) &&
- ((strcmp("kernel", name) != 0) || __android_log_is_debuggable()) &&
+ ((strcmp("kernel", name) != 0) ||
+ __android_logger_property_get_bool(
+ "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG |
+ BOOL_DEFAULT_FLAG_SVELTE)) &&
(strcmp("stats", name) != 0)),
android_logger_get_log_readable_size(logger));
} else {
diff --git a/libsparse/.clang-format b/libsparse/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libsparse/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index c7c089f..8ad339f 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -6,11 +6,11 @@
recovery_available: true,
unique_host_soname: true,
srcs: [
- "backed_block.c",
- "output_file.c",
- "sparse.c",
- "sparse_crc32.c",
- "sparse_err.c",
+ "backed_block.cpp",
+ "output_file.cpp",
+ "sparse.cpp",
+ "sparse_crc32.cpp",
+ "sparse_err.cpp",
"sparse_read.cpp",
],
cflags: ["-Werror"],
@@ -31,8 +31,8 @@
name: "simg2img",
host_supported: true,
srcs: [
- "simg2img.c",
- "sparse_crc32.c",
+ "simg2img.cpp",
+ "sparse_crc32.cpp",
],
static_libs: [
"libsparse",
@@ -46,7 +46,7 @@
cc_binary {
name: "img2simg",
host_supported: true,
- srcs: ["img2simg.c"],
+ srcs: ["img2simg.cpp"],
static_libs: [
"libsparse",
"libz",
@@ -58,7 +58,7 @@
cc_binary_host {
name: "append2simg",
- srcs: ["append2simg.c"],
+ srcs: ["append2simg.cpp"],
static_libs: [
"libsparse",
"libz",
diff --git a/libsparse/OWNERS b/libsparse/OWNERS
new file mode 100644
index 0000000..70b375f
--- /dev/null
+++ b/libsparse/OWNERS
@@ -0,0 +1 @@
+ccross@google.com
diff --git a/libsparse/append2simg.c b/libsparse/append2simg.c
deleted file mode 100644
index eef8764..0000000
--- a/libsparse/append2simg.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-#define _GNU_SOURCE
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-#include "sparse_file.h"
-#include "backed_block.h"
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#endif
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define off64_t off_t
-#endif
-
-void usage()
-{
- fprintf(stderr, "Usage: append2simg <output> <input>\n");
-}
-
-int main(int argc, char *argv[])
-{
- int output;
- int output_block;
- char *output_path;
- struct sparse_file *sparse_output;
-
- int input;
- char *input_path;
- off64_t input_len;
-
- int tmp_fd;
- char *tmp_path;
-
- int ret;
-
- if (argc == 3) {
- output_path = argv[1];
- input_path = argv[2];
- } else {
- usage();
- exit(-1);
- }
-
- ret = asprintf(&tmp_path, "%s.append2simg", output_path);
- if (ret < 0) {
- fprintf(stderr, "Couldn't allocate filename\n");
- exit(-1);
- }
-
- output = open(output_path, O_RDWR | O_BINARY);
- if (output < 0) {
- fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno));
- exit(-1);
- }
-
- sparse_output = sparse_file_import_auto(output, false, true);
- if (!sparse_output) {
- fprintf(stderr, "Couldn't import output file\n");
- exit(-1);
- }
-
- input = open(input_path, O_RDONLY | O_BINARY);
- if (input < 0) {
- fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno));
- exit(-1);
- }
-
- input_len = lseek64(input, 0, SEEK_END);
- if (input_len < 0) {
- fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno));
- exit(-1);
- } else if (input_len % sparse_output->block_size) {
- fprintf(stderr, "Input file is not a multiple of the output file's block size");
- exit(-1);
- }
- lseek64(input, 0, SEEK_SET);
-
- output_block = sparse_output->len / sparse_output->block_size;
- if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) {
- fprintf(stderr, "Couldn't add input file\n");
- exit(-1);
- }
- sparse_output->len += input_len;
-
- tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664);
- if (tmp_fd < 0) {
- fprintf(stderr, "Couldn't open temporary file (%s)\n", strerror(errno));
- exit(-1);
- }
-
- lseek64(output, 0, SEEK_SET);
- if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) {
- fprintf(stderr, "Failed to write sparse file\n");
- exit(-1);
- }
-
- sparse_file_destroy(sparse_output);
- close(tmp_fd);
- close(output);
- close(input);
-
- ret = rename(tmp_path, output_path);
- if (ret < 0) {
- fprintf(stderr, "Failed to rename temporary file (%s)\n", strerror(errno));
- exit(-1);
- }
-
- free(tmp_path);
-
- exit(0);
-}
diff --git a/libsparse/append2simg.cpp b/libsparse/append2simg.cpp
new file mode 100644
index 0000000..99f4339
--- /dev/null
+++ b/libsparse/append2simg.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+#include "backed_block.h"
+#include "sparse_file.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#endif
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define off64_t off_t
+#endif
+
+void usage() {
+ fprintf(stderr, "Usage: append2simg <output> <input>\n");
+}
+
+int main(int argc, char* argv[]) {
+ int output;
+ int output_block;
+ char* output_path;
+ struct sparse_file* sparse_output;
+
+ int input;
+ char* input_path;
+ off64_t input_len;
+
+ int tmp_fd;
+ char* tmp_path;
+
+ int ret;
+
+ if (argc == 3) {
+ output_path = argv[1];
+ input_path = argv[2];
+ } else {
+ usage();
+ exit(-1);
+ }
+
+ ret = asprintf(&tmp_path, "%s.append2simg", output_path);
+ if (ret < 0) {
+ fprintf(stderr, "Couldn't allocate filename\n");
+ exit(-1);
+ }
+
+ output = open(output_path, O_RDWR | O_BINARY);
+ if (output < 0) {
+ fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno));
+ exit(-1);
+ }
+
+ sparse_output = sparse_file_import_auto(output, false, true);
+ if (!sparse_output) {
+ fprintf(stderr, "Couldn't import output file\n");
+ exit(-1);
+ }
+
+ input = open(input_path, O_RDONLY | O_BINARY);
+ if (input < 0) {
+ fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno));
+ exit(-1);
+ }
+
+ input_len = lseek64(input, 0, SEEK_END);
+ if (input_len < 0) {
+ fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno));
+ exit(-1);
+ } else if (input_len % sparse_output->block_size) {
+ fprintf(stderr, "Input file is not a multiple of the output file's block size");
+ exit(-1);
+ }
+ lseek64(input, 0, SEEK_SET);
+
+ output_block = sparse_output->len / sparse_output->block_size;
+ if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) {
+ fprintf(stderr, "Couldn't add input file\n");
+ exit(-1);
+ }
+ sparse_output->len += input_len;
+
+ tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664);
+ if (tmp_fd < 0) {
+ fprintf(stderr, "Couldn't open temporary file (%s)\n", strerror(errno));
+ exit(-1);
+ }
+
+ lseek64(output, 0, SEEK_SET);
+ if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) {
+ fprintf(stderr, "Failed to write sparse file\n");
+ exit(-1);
+ }
+
+ sparse_file_destroy(sparse_output);
+ close(tmp_fd);
+ close(output);
+ close(input);
+
+ ret = rename(tmp_path, output_path);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to rename temporary file (%s)\n", strerror(errno));
+ exit(-1);
+ }
+
+ free(tmp_path);
+
+ exit(0);
+}
diff --git a/libsparse/backed_block.c b/libsparse/backed_block.c
deleted file mode 100644
index 794cd6b..0000000
--- a/libsparse/backed_block.c
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <assert.h>
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "backed_block.h"
-#include "sparse_defs.h"
-
-struct backed_block {
- unsigned int block;
- unsigned int len;
- enum backed_block_type type;
- union {
- struct {
- void *data;
- } data;
- struct {
- char *filename;
- int64_t offset;
- } file;
- struct {
- int fd;
- int64_t offset;
- } fd;
- struct {
- uint32_t val;
- } fill;
- };
- struct backed_block *next;
-};
-
-struct backed_block_list {
- struct backed_block *data_blocks;
- struct backed_block *last_used;
- unsigned int block_size;
-};
-
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl)
-{
- return bbl->data_blocks;
-}
-
-struct backed_block *backed_block_iter_next(struct backed_block *bb)
-{
- return bb->next;
-}
-
-unsigned int backed_block_len(struct backed_block *bb)
-{
- return bb->len;
-}
-
-unsigned int backed_block_block(struct backed_block *bb)
-{
- return bb->block;
-}
-
-void *backed_block_data(struct backed_block *bb)
-{
- assert(bb->type == BACKED_BLOCK_DATA);
- return bb->data.data;
-}
-
-const char *backed_block_filename(struct backed_block *bb)
-{
- assert(bb->type == BACKED_BLOCK_FILE);
- return bb->file.filename;
-}
-
-int backed_block_fd(struct backed_block *bb)
-{
- assert(bb->type == BACKED_BLOCK_FD);
- return bb->fd.fd;
-}
-
-int64_t backed_block_file_offset(struct backed_block *bb)
-{
- assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD);
- if (bb->type == BACKED_BLOCK_FILE) {
- return bb->file.offset;
- } else { /* bb->type == BACKED_BLOCK_FD */
- return bb->fd.offset;
- }
-}
-
-uint32_t backed_block_fill_val(struct backed_block *bb)
-{
- assert(bb->type == BACKED_BLOCK_FILL);
- return bb->fill.val;
-}
-
-enum backed_block_type backed_block_type(struct backed_block *bb)
-{
- return bb->type;
-}
-
-void backed_block_destroy(struct backed_block *bb)
-{
- if (bb->type == BACKED_BLOCK_FILE) {
- free(bb->file.filename);
- }
-
- free(bb);
-}
-
-struct backed_block_list *backed_block_list_new(unsigned int block_size)
-{
- struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1);
- b->block_size = block_size;
- return b;
-}
-
-void backed_block_list_destroy(struct backed_block_list *bbl)
-{
- if (bbl->data_blocks) {
- struct backed_block *bb = bbl->data_blocks;
- while (bb) {
- struct backed_block *next = bb->next;
- backed_block_destroy(bb);
- bb = next;
- }
- }
-
- free(bbl);
-}
-
-void backed_block_list_move(struct backed_block_list *from,
- struct backed_block_list *to, struct backed_block *start,
- struct backed_block *end)
-{
- struct backed_block *bb;
-
- if (start == NULL) {
- start = from->data_blocks;
- }
-
- if (!end) {
- for (end = start; end && end->next; end = end->next)
- ;
- }
-
- if (start == NULL || end == NULL) {
- return;
- }
-
- from->last_used = NULL;
- to->last_used = NULL;
- if (from->data_blocks == start) {
- from->data_blocks = end->next;
- } else {
- for (bb = from->data_blocks; bb; bb = bb->next) {
- if (bb->next == start) {
- bb->next = end->next;
- break;
- }
- }
- }
-
- if (!to->data_blocks) {
- to->data_blocks = start;
- end->next = NULL;
- } else {
- for (bb = to->data_blocks; bb; bb = bb->next) {
- if (!bb->next || bb->next->block > start->block) {
- end->next = bb->next;
- bb->next = start;
- break;
- }
- }
- }
-}
-
-/* may free b */
-static int merge_bb(struct backed_block_list *bbl,
- struct backed_block *a, struct backed_block *b)
-{
- unsigned int block_len;
-
- /* Block doesn't exist (possible if one block is the last block) */
- if (!a || !b) {
- return -EINVAL;
- }
-
- assert(a->block < b->block);
-
- /* Blocks are of different types */
- if (a->type != b->type) {
- return -EINVAL;
- }
-
- /* Blocks are not adjacent */
- block_len = a->len / bbl->block_size; /* rounds down */
- if (a->block + block_len != b->block) {
- return -EINVAL;
- }
-
- switch (a->type) {
- case BACKED_BLOCK_DATA:
- /* Don't support merging data for now */
- return -EINVAL;
- case BACKED_BLOCK_FILL:
- if (a->fill.val != b->fill.val) {
- return -EINVAL;
- }
- break;
- case BACKED_BLOCK_FILE:
- /* Already make sure b->type is BACKED_BLOCK_FILE */
- if (strcmp(a->file.filename, b->file.filename) ||
- a->file.offset + a->len != b->file.offset) {
- return -EINVAL;
- }
- break;
- case BACKED_BLOCK_FD:
- if (a->fd.fd != b->fd.fd ||
- a->fd.offset + a->len != b->fd.offset) {
- return -EINVAL;
- }
- break;
- }
-
- /* Blocks are compatible and adjacent, with a before b. Merge b into a,
- * and free b */
- a->len += b->len;
- a->next = b->next;
-
- backed_block_destroy(b);
-
- return 0;
-}
-
-static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
-{
- struct backed_block *bb;
-
- if (bbl->data_blocks == NULL) {
- bbl->data_blocks = new_bb;
- return 0;
- }
-
- if (bbl->data_blocks->block > new_bb->block) {
- new_bb->next = bbl->data_blocks;
- bbl->data_blocks = new_bb;
- return 0;
- }
-
- /* Optimization: blocks are mostly queued in sequence, so save the
- pointer to the last bb that was added, and start searching from
- there if the next block number is higher */
- if (bbl->last_used && new_bb->block > bbl->last_used->block)
- bb = bbl->last_used;
- else
- bb = bbl->data_blocks;
- bbl->last_used = new_bb;
-
- for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
- ;
-
- if (bb->next == NULL) {
- bb->next = new_bb;
- } else {
- new_bb->next = bb->next;
- bb->next = new_bb;
- }
-
- merge_bb(bbl, new_bb, new_bb->next);
- if (!merge_bb(bbl, bb, new_bb)) {
- /* new_bb destroyed, point to retained as last_used */
- bbl->last_used = bb;
- }
-
- return 0;
-}
-
-/* Queues a fill block of memory to be written to the specified data blocks */
-int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
- unsigned int len, unsigned int block)
-{
- struct backed_block *bb = calloc(1, sizeof(struct backed_block));
- if (bb == NULL) {
- return -ENOMEM;
- }
-
- bb->block = block;
- bb->len = len;
- bb->type = BACKED_BLOCK_FILL;
- bb->fill.val = fill_val;
- bb->next = NULL;
-
- return queue_bb(bbl, bb);
-}
-
-/* Queues a block of memory to be written to the specified data blocks */
-int backed_block_add_data(struct backed_block_list *bbl, void *data,
- unsigned int len, unsigned int block)
-{
- struct backed_block *bb = calloc(1, sizeof(struct backed_block));
- if (bb == NULL) {
- return -ENOMEM;
- }
-
- bb->block = block;
- bb->len = len;
- bb->type = BACKED_BLOCK_DATA;
- bb->data.data = data;
- bb->next = NULL;
-
- return queue_bb(bbl, bb);
-}
-
-/* Queues a chunk of a file on disk to be written to the specified data blocks */
-int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
- int64_t offset, unsigned int len, unsigned int block)
-{
- struct backed_block *bb = calloc(1, sizeof(struct backed_block));
- if (bb == NULL) {
- return -ENOMEM;
- }
-
- bb->block = block;
- bb->len = len;
- bb->type = BACKED_BLOCK_FILE;
- bb->file.filename = strdup(filename);
- bb->file.offset = offset;
- bb->next = NULL;
-
- return queue_bb(bbl, bb);
-}
-
-/* Queues a chunk of a fd to be written to the specified data blocks */
-int backed_block_add_fd(struct backed_block_list *bbl, int fd, int64_t offset,
- unsigned int len, unsigned int block)
-{
- struct backed_block *bb = calloc(1, sizeof(struct backed_block));
- if (bb == NULL) {
- return -ENOMEM;
- }
-
- bb->block = block;
- bb->len = len;
- bb->type = BACKED_BLOCK_FD;
- bb->fd.fd = fd;
- bb->fd.offset = offset;
- bb->next = NULL;
-
- return queue_bb(bbl, bb);
-}
-
-int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
- unsigned int max_len)
-{
- struct backed_block *new_bb;
-
- max_len = ALIGN_DOWN(max_len, bbl->block_size);
-
- if (bb->len <= max_len) {
- return 0;
- }
-
- new_bb = malloc(sizeof(struct backed_block));
- if (new_bb == NULL) {
- return -ENOMEM;
- }
-
- *new_bb = *bb;
-
- new_bb->len = bb->len - max_len;
- new_bb->block = bb->block + max_len / bbl->block_size;
- new_bb->next = bb->next;
- bb->next = new_bb;
- bb->len = max_len;
-
- switch (bb->type) {
- case BACKED_BLOCK_DATA:
- new_bb->data.data = (char *)bb->data.data + max_len;
- break;
- case BACKED_BLOCK_FILE:
- new_bb->file.offset += max_len;
- break;
- case BACKED_BLOCK_FD:
- new_bb->fd.offset += max_len;
- break;
- case BACKED_BLOCK_FILL:
- break;
- }
-
- return 0;
-}
diff --git a/libsparse/backed_block.cpp b/libsparse/backed_block.cpp
new file mode 100644
index 0000000..7f5632e
--- /dev/null
+++ b/libsparse/backed_block.cpp
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "backed_block.h"
+#include "sparse_defs.h"
+
+struct backed_block {
+ unsigned int block;
+ unsigned int len;
+ enum backed_block_type type;
+ union {
+ struct {
+ void* data;
+ } data;
+ struct {
+ char* filename;
+ int64_t offset;
+ } file;
+ struct {
+ int fd;
+ int64_t offset;
+ } fd;
+ struct {
+ uint32_t val;
+ } fill;
+ };
+ struct backed_block* next;
+};
+
+struct backed_block_list {
+ struct backed_block* data_blocks;
+ struct backed_block* last_used;
+ unsigned int block_size;
+};
+
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl) {
+ return bbl->data_blocks;
+}
+
+struct backed_block* backed_block_iter_next(struct backed_block* bb) {
+ return bb->next;
+}
+
+unsigned int backed_block_len(struct backed_block* bb) {
+ return bb->len;
+}
+
+unsigned int backed_block_block(struct backed_block* bb) {
+ return bb->block;
+}
+
+void* backed_block_data(struct backed_block* bb) {
+ assert(bb->type == BACKED_BLOCK_DATA);
+ return bb->data.data;
+}
+
+const char* backed_block_filename(struct backed_block* bb) {
+ assert(bb->type == BACKED_BLOCK_FILE);
+ return bb->file.filename;
+}
+
+int backed_block_fd(struct backed_block* bb) {
+ assert(bb->type == BACKED_BLOCK_FD);
+ return bb->fd.fd;
+}
+
+int64_t backed_block_file_offset(struct backed_block* bb) {
+ assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD);
+ if (bb->type == BACKED_BLOCK_FILE) {
+ return bb->file.offset;
+ } else { /* bb->type == BACKED_BLOCK_FD */
+ return bb->fd.offset;
+ }
+}
+
+uint32_t backed_block_fill_val(struct backed_block* bb) {
+ assert(bb->type == BACKED_BLOCK_FILL);
+ return bb->fill.val;
+}
+
+enum backed_block_type backed_block_type(struct backed_block* bb) {
+ return bb->type;
+}
+
+void backed_block_destroy(struct backed_block* bb) {
+ if (bb->type == BACKED_BLOCK_FILE) {
+ free(bb->file.filename);
+ }
+
+ free(bb);
+}
+
+struct backed_block_list* backed_block_list_new(unsigned int block_size) {
+ struct backed_block_list* b =
+ reinterpret_cast<backed_block_list*>(calloc(sizeof(struct backed_block_list), 1));
+ b->block_size = block_size;
+ return b;
+}
+
+void backed_block_list_destroy(struct backed_block_list* bbl) {
+ if (bbl->data_blocks) {
+ struct backed_block* bb = bbl->data_blocks;
+ while (bb) {
+ struct backed_block* next = bb->next;
+ backed_block_destroy(bb);
+ bb = next;
+ }
+ }
+
+ free(bbl);
+}
+
+void backed_block_list_move(struct backed_block_list* from, struct backed_block_list* to,
+ struct backed_block* start, struct backed_block* end) {
+ struct backed_block* bb;
+
+ if (start == NULL) {
+ start = from->data_blocks;
+ }
+
+ if (!end) {
+ for (end = start; end && end->next; end = end->next)
+ ;
+ }
+
+ if (start == NULL || end == NULL) {
+ return;
+ }
+
+ from->last_used = NULL;
+ to->last_used = NULL;
+ if (from->data_blocks == start) {
+ from->data_blocks = end->next;
+ } else {
+ for (bb = from->data_blocks; bb; bb = bb->next) {
+ if (bb->next == start) {
+ bb->next = end->next;
+ break;
+ }
+ }
+ }
+
+ if (!to->data_blocks) {
+ to->data_blocks = start;
+ end->next = NULL;
+ } else {
+ for (bb = to->data_blocks; bb; bb = bb->next) {
+ if (!bb->next || bb->next->block > start->block) {
+ end->next = bb->next;
+ bb->next = start;
+ break;
+ }
+ }
+ }
+}
+
+/* may free b */
+static int merge_bb(struct backed_block_list* bbl, struct backed_block* a, struct backed_block* b) {
+ unsigned int block_len;
+
+ /* Block doesn't exist (possible if one block is the last block) */
+ if (!a || !b) {
+ return -EINVAL;
+ }
+
+ assert(a->block < b->block);
+
+ /* Blocks are of different types */
+ if (a->type != b->type) {
+ return -EINVAL;
+ }
+
+ /* Blocks are not adjacent */
+ block_len = a->len / bbl->block_size; /* rounds down */
+ if (a->block + block_len != b->block) {
+ return -EINVAL;
+ }
+
+ switch (a->type) {
+ case BACKED_BLOCK_DATA:
+ /* Don't support merging data for now */
+ return -EINVAL;
+ case BACKED_BLOCK_FILL:
+ if (a->fill.val != b->fill.val) {
+ return -EINVAL;
+ }
+ break;
+ case BACKED_BLOCK_FILE:
+ /* Already make sure b->type is BACKED_BLOCK_FILE */
+ if (strcmp(a->file.filename, b->file.filename) || a->file.offset + a->len != b->file.offset) {
+ return -EINVAL;
+ }
+ break;
+ case BACKED_BLOCK_FD:
+ if (a->fd.fd != b->fd.fd || a->fd.offset + a->len != b->fd.offset) {
+ return -EINVAL;
+ }
+ break;
+ }
+
+ /* Blocks are compatible and adjacent, with a before b. Merge b into a,
+ * and free b */
+ a->len += b->len;
+ a->next = b->next;
+
+ backed_block_destroy(b);
+
+ return 0;
+}
+
+static int queue_bb(struct backed_block_list* bbl, struct backed_block* new_bb) {
+ struct backed_block* bb;
+
+ if (bbl->data_blocks == NULL) {
+ bbl->data_blocks = new_bb;
+ return 0;
+ }
+
+ if (bbl->data_blocks->block > new_bb->block) {
+ new_bb->next = bbl->data_blocks;
+ bbl->data_blocks = new_bb;
+ return 0;
+ }
+
+ /* Optimization: blocks are mostly queued in sequence, so save the
+ pointer to the last bb that was added, and start searching from
+ there if the next block number is higher */
+ if (bbl->last_used && new_bb->block > bbl->last_used->block)
+ bb = bbl->last_used;
+ else
+ bb = bbl->data_blocks;
+ bbl->last_used = new_bb;
+
+ for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
+ ;
+
+ if (bb->next == NULL) {
+ bb->next = new_bb;
+ } else {
+ new_bb->next = bb->next;
+ bb->next = new_bb;
+ }
+
+ merge_bb(bbl, new_bb, new_bb->next);
+ if (!merge_bb(bbl, bb, new_bb)) {
+ /* new_bb destroyed, point to retained as last_used */
+ bbl->last_used = bb;
+ }
+
+ return 0;
+}
+
+/* Queues a fill block of memory to be written to the specified data blocks */
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+ unsigned int block) {
+ struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+ if (bb == NULL) {
+ return -ENOMEM;
+ }
+
+ bb->block = block;
+ bb->len = len;
+ bb->type = BACKED_BLOCK_FILL;
+ bb->fill.val = fill_val;
+ bb->next = NULL;
+
+ return queue_bb(bbl, bb);
+}
+
+/* Queues a block of memory to be written to the specified data blocks */
+int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+ unsigned int block) {
+ struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+ if (bb == NULL) {
+ return -ENOMEM;
+ }
+
+ bb->block = block;
+ bb->len = len;
+ bb->type = BACKED_BLOCK_DATA;
+ bb->data.data = data;
+ bb->next = NULL;
+
+ return queue_bb(bbl, bb);
+}
+
+/* Queues a chunk of a file on disk to be written to the specified data blocks */
+int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
+ unsigned int len, unsigned int block) {
+ struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+ if (bb == NULL) {
+ return -ENOMEM;
+ }
+
+ bb->block = block;
+ bb->len = len;
+ bb->type = BACKED_BLOCK_FILE;
+ bb->file.filename = strdup(filename);
+ bb->file.offset = offset;
+ bb->next = NULL;
+
+ return queue_bb(bbl, bb);
+}
+
+/* Queues a chunk of a fd to be written to the specified data blocks */
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+ unsigned int block) {
+ struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+ if (bb == NULL) {
+ return -ENOMEM;
+ }
+
+ bb->block = block;
+ bb->len = len;
+ bb->type = BACKED_BLOCK_FD;
+ bb->fd.fd = fd;
+ bb->fd.offset = offset;
+ bb->next = NULL;
+
+ return queue_bb(bbl, bb);
+}
+
+int backed_block_split(struct backed_block_list* bbl, struct backed_block* bb,
+ unsigned int max_len) {
+ struct backed_block* new_bb;
+
+ max_len = ALIGN_DOWN(max_len, bbl->block_size);
+
+ if (bb->len <= max_len) {
+ return 0;
+ }
+
+ new_bb = reinterpret_cast<backed_block*>(malloc(sizeof(struct backed_block)));
+ if (new_bb == NULL) {
+ return -ENOMEM;
+ }
+
+ *new_bb = *bb;
+
+ new_bb->len = bb->len - max_len;
+ new_bb->block = bb->block + max_len / bbl->block_size;
+ new_bb->next = bb->next;
+ bb->next = new_bb;
+ bb->len = max_len;
+
+ switch (bb->type) {
+ case BACKED_BLOCK_DATA:
+ new_bb->data.data = (char*)bb->data.data + max_len;
+ break;
+ case BACKED_BLOCK_FILE:
+ new_bb->file.offset += max_len;
+ break;
+ case BACKED_BLOCK_FD:
+ new_bb->fd.offset += max_len;
+ break;
+ case BACKED_BLOCK_FILL:
+ break;
+ }
+
+ return 0;
+}
diff --git a/libsparse/backed_block.h b/libsparse/backed_block.h
index 1a159be..3a75460 100644
--- a/libsparse/backed_block.h
+++ b/libsparse/backed_block.h
@@ -23,42 +23,40 @@
struct backed_block;
enum backed_block_type {
- BACKED_BLOCK_DATA,
- BACKED_BLOCK_FILE,
- BACKED_BLOCK_FD,
- BACKED_BLOCK_FILL,
+ BACKED_BLOCK_DATA,
+ BACKED_BLOCK_FILE,
+ BACKED_BLOCK_FD,
+ BACKED_BLOCK_FILL,
};
-int backed_block_add_data(struct backed_block_list *bbl, void *data,
- unsigned int len, unsigned int block);
-int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
- unsigned int len, unsigned int block);
-int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
- int64_t offset, unsigned int len, unsigned int block);
-int backed_block_add_fd(struct backed_block_list *bbl, int fd,
- int64_t offset, unsigned int len, unsigned int block);
+int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+ unsigned int block);
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+ unsigned int block);
+int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
+ unsigned int len, unsigned int block);
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+ unsigned int block);
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
-struct backed_block *backed_block_iter_next(struct backed_block *bb);
-unsigned int backed_block_len(struct backed_block *bb);
-unsigned int backed_block_block(struct backed_block *bb);
-void *backed_block_data(struct backed_block *bb);
-const char *backed_block_filename(struct backed_block *bb);
-int backed_block_fd(struct backed_block *bb);
-int64_t backed_block_file_offset(struct backed_block *bb);
-uint32_t backed_block_fill_val(struct backed_block *bb);
-enum backed_block_type backed_block_type(struct backed_block *bb);
-int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
- unsigned int max_len);
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl);
+struct backed_block* backed_block_iter_next(struct backed_block* bb);
+unsigned int backed_block_len(struct backed_block* bb);
+unsigned int backed_block_block(struct backed_block* bb);
+void* backed_block_data(struct backed_block* bb);
+const char* backed_block_filename(struct backed_block* bb);
+int backed_block_fd(struct backed_block* bb);
+int64_t backed_block_file_offset(struct backed_block* bb);
+uint32_t backed_block_fill_val(struct backed_block* bb);
+enum backed_block_type backed_block_type(struct backed_block* bb);
+int backed_block_split(struct backed_block_list* bbl, struct backed_block* bb, unsigned int max_len);
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
-struct backed_block *backed_block_iter_next(struct backed_block *bb);
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl);
+struct backed_block* backed_block_iter_next(struct backed_block* bb);
-struct backed_block_list *backed_block_list_new(unsigned int block_size);
-void backed_block_list_destroy(struct backed_block_list *bbl);
+struct backed_block_list* backed_block_list_new(unsigned int block_size);
+void backed_block_list_destroy(struct backed_block_list* bbl);
-void backed_block_list_move(struct backed_block_list *from,
- struct backed_block_list *to, struct backed_block *start,
- struct backed_block *end);
+void backed_block_list_move(struct backed_block_list* from, struct backed_block_list* to,
+ struct backed_block* start, struct backed_block* end);
#endif
diff --git a/libsparse/defs.h b/libsparse/defs.h
index 34e63c5..28e5cab 100644
--- a/libsparse/defs.h
+++ b/libsparse/defs.h
@@ -17,7 +17,7 @@
#ifndef _LIBSPARSE_DEFS_H_
#ifndef __unused
-#define __unused __attribute__((__unused__))
+#define __unused __attribute__((__unused__))
#endif
#endif /* _LIBSPARSE_DEFS_H_ */
diff --git a/libsparse/img2simg.c b/libsparse/img2simg.c
deleted file mode 100644
index a0db36f..0000000
--- a/libsparse/img2simg.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define off64_t off_t
-#endif
-
-void usage()
-{
- fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n");
-}
-
-int main(int argc, char *argv[])
-{
- int in;
- int out;
- int ret;
- struct sparse_file *s;
- unsigned int block_size = 4096;
- off64_t len;
-
- if (argc < 3 || argc > 4) {
- usage();
- exit(-1);
- }
-
- if (argc == 4) {
- block_size = atoi(argv[3]);
- }
-
- if (block_size < 1024 || block_size % 4 != 0) {
- usage();
- exit(-1);
- }
-
- if (strcmp(argv[1], "-") == 0) {
- in = STDIN_FILENO;
- } else {
- in = open(argv[1], O_RDONLY | O_BINARY);
- if (in < 0) {
- fprintf(stderr, "Cannot open input file %s\n", argv[1]);
- exit(-1);
- }
- }
-
- if (strcmp(argv[2], "-") == 0) {
- out = STDOUT_FILENO;
- } else {
- out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
- if (out < 0) {
- fprintf(stderr, "Cannot open output file %s\n", argv[2]);
- exit(-1);
- }
- }
-
- len = lseek64(in, 0, SEEK_END);
- lseek64(in, 0, SEEK_SET);
-
- s = sparse_file_new(block_size, len);
- if (!s) {
- fprintf(stderr, "Failed to create sparse file\n");
- exit(-1);
- }
-
- sparse_file_verbose(s);
- ret = sparse_file_read(s, in, false, false);
- if (ret) {
- fprintf(stderr, "Failed to read file\n");
- exit(-1);
- }
-
- ret = sparse_file_write(s, out, false, true, false);
- if (ret) {
- fprintf(stderr, "Failed to write sparse file\n");
- exit(-1);
- }
-
- close(in);
- close(out);
-
- exit(0);
-}
diff --git a/libsparse/img2simg.cpp b/libsparse/img2simg.cpp
new file mode 100644
index 0000000..4c2c6ca
--- /dev/null
+++ b/libsparse/img2simg.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define off64_t off_t
+#endif
+
+void usage() {
+ fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n");
+}
+
+int main(int argc, char* argv[]) {
+ int in;
+ int out;
+ int ret;
+ struct sparse_file* s;
+ unsigned int block_size = 4096;
+ off64_t len;
+
+ if (argc < 3 || argc > 4) {
+ usage();
+ exit(-1);
+ }
+
+ if (argc == 4) {
+ block_size = atoi(argv[3]);
+ }
+
+ if (block_size < 1024 || block_size % 4 != 0) {
+ usage();
+ exit(-1);
+ }
+
+ if (strcmp(argv[1], "-") == 0) {
+ in = STDIN_FILENO;
+ } else {
+ in = open(argv[1], O_RDONLY | O_BINARY);
+ if (in < 0) {
+ fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+ exit(-1);
+ }
+ }
+
+ if (strcmp(argv[2], "-") == 0) {
+ out = STDOUT_FILENO;
+ } else {
+ out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+ if (out < 0) {
+ fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+ exit(-1);
+ }
+ }
+
+ len = lseek64(in, 0, SEEK_END);
+ lseek64(in, 0, SEEK_SET);
+
+ s = sparse_file_new(block_size, len);
+ if (!s) {
+ fprintf(stderr, "Failed to create sparse file\n");
+ exit(-1);
+ }
+
+ sparse_file_verbose(s);
+ ret = sparse_file_read(s, in, false, false);
+ if (ret) {
+ fprintf(stderr, "Failed to read file\n");
+ exit(-1);
+ }
+
+ ret = sparse_file_write(s, out, false, true, false);
+ if (ret) {
+ fprintf(stderr, "Failed to write sparse file\n");
+ exit(-1);
+ }
+
+ close(in);
+ close(out);
+
+ exit(0);
+}
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 1b91ead..3d5fb0c 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -246,9 +246,24 @@
int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc);
/**
- * sparse_file_import - import an existing sparse file
+ * sparse_file_read_buf - read a buffer into a sparse file cookie
*
* @s - sparse file cookie
+ * @buf - buffer to read from
+ * @crc - verify the crc of a file in the Android sparse file format
+ *
+ * Reads a buffer into a sparse file cookie. The buffer must remain
+ * valid until the sparse file cookie is freed. If crc is true, the
+ * crc of the sparse file will be verified.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int sparse_file_read_buf(struct sparse_file *s, char *buf, bool crc);
+
+/**
+ * sparse_file_import - import an existing sparse file
+ *
+ * @fd - file descriptor to read from
* @verbose - print verbose errors while reading the sparse file
* @crc - verify the crc of a file in the Android sparse file format
*
@@ -261,6 +276,21 @@
struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc);
/**
+ * sparse_file_import_buf - import an existing sparse file from a buffer
+ *
+ * @buf - buffer to read from
+ * @verbose - print verbose errors while reading the sparse file
+ * @crc - verify the crc of a file in the Android sparse file format
+ *
+ * Reads existing sparse file data into a sparse file cookie, recreating the same
+ * sparse cookie that was used to write it. If verbose is true, prints verbose
+ * errors when the sparse file is formatted incorrectly.
+ *
+ * Returns a new sparse file cookie on success, NULL on error.
+ */
+struct sparse_file *sparse_file_import_buf(char* buf, bool verbose, bool crc);
+
+/**
* sparse_file_import_auto - import an existing sparse or normal file
*
* @fd - file descriptor to read from
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
deleted file mode 100644
index 002ad27..0000000
--- a/libsparse/output_file.c
+++ /dev/null
@@ -1,776 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <zlib.h>
-
-#include "defs.h"
-#include "output_file.h"
-#include "sparse_crc32.h"
-#include "sparse_format.h"
-
-#ifndef _WIN32
-#include <sys/mman.h>
-#define O_BINARY 0
-#else
-#define ftruncate64 ftruncate
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define ftruncate64 ftruncate
-#define mmap64 mmap
-#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))
-#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
-
-#define container_of(inner, outer_t, elem) \
- ((outer_t *)((char *)(inner) - offsetof(outer_t, elem)))
-
-struct output_file_ops {
- int (*open)(struct output_file *, int fd);
- int (*skip)(struct output_file *, int64_t);
- int (*pad)(struct output_file *, int64_t);
- int (*write)(struct output_file *, void *, size_t);
- void (*close)(struct output_file *);
-};
-
-struct sparse_file_ops {
- int (*write_data_chunk)(struct output_file *out, unsigned int len,
- void *data);
- int (*write_fill_chunk)(struct output_file *out, unsigned int len,
- uint32_t fill_val);
- int (*write_skip_chunk)(struct output_file *out, int64_t len);
- int (*write_end_chunk)(struct output_file *out);
-};
-
-struct output_file {
- int64_t cur_out_ptr;
- unsigned int chunk_cnt;
- uint32_t crc32;
- struct output_file_ops *ops;
- struct sparse_file_ops *sparse_ops;
- int use_crc;
- unsigned int block_size;
- int64_t len;
- char *zero_buf;
- uint32_t *fill_buf;
- char *buf;
-};
-
-struct output_file_gz {
- struct output_file out;
- gzFile gz_fd;
-};
-
-#define to_output_file_gz(_o) \
- container_of((_o), struct output_file_gz, out)
-
-struct output_file_normal {
- struct output_file out;
- int fd;
-};
-
-#define to_output_file_normal(_o) \
- container_of((_o), struct output_file_normal, out)
-
-struct output_file_callback {
- struct output_file out;
- void *priv;
- int (*write)(void *priv, const void *buf, size_t len);
-};
-
-#define to_output_file_callback(_o) \
- container_of((_o), struct output_file_callback, out)
-
-static int file_open(struct output_file *out, int fd)
-{
- struct output_file_normal *outn = to_output_file_normal(out);
-
- outn->fd = fd;
- return 0;
-}
-
-static int file_skip(struct output_file *out, int64_t cnt)
-{
- off64_t ret;
- struct output_file_normal *outn = to_output_file_normal(out);
-
- ret = lseek64(outn->fd, cnt, SEEK_CUR);
- if (ret < 0) {
- error_errno("lseek64");
- return -1;
- }
- return 0;
-}
-
-static int file_pad(struct output_file *out, int64_t len)
-{
- int ret;
- struct output_file_normal *outn = to_output_file_normal(out);
-
- ret = ftruncate64(outn->fd, len);
- if (ret < 0) {
- return -errno;
- }
-
- return 0;
-}
-
-static int file_write(struct output_file *out, void *data, size_t len)
-{
- ssize_t ret;
- struct output_file_normal *outn = to_output_file_normal(out);
-
- while (len > 0) {
- ret = write(outn->fd, data, len);
- if (ret < 0) {
- if (errno == EINTR) {
- continue;
- }
- error_errno("write");
- return -1;
- }
-
- data = (char *)data + ret;
- len -= ret;
- }
-
- return 0;
-}
-
-static void file_close(struct output_file *out)
-{
- struct output_file_normal *outn = to_output_file_normal(out);
-
- free(outn);
-}
-
-static struct output_file_ops file_ops = {
- .open = file_open,
- .skip = file_skip,
- .pad = file_pad,
- .write = file_write,
- .close = file_close,
-};
-
-static int gz_file_open(struct output_file *out, int fd)
-{
- struct output_file_gz *outgz = to_output_file_gz(out);
-
- outgz->gz_fd = gzdopen(fd, "wb9");
- if (!outgz->gz_fd) {
- error_errno("gzopen");
- return -errno;
- }
-
- return 0;
-}
-
-
-static int gz_file_skip(struct output_file *out, int64_t cnt)
-{
- off64_t ret;
- struct output_file_gz *outgz = to_output_file_gz(out);
-
- ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR);
- if (ret < 0) {
- error_errno("gzseek");
- return -1;
- }
- return 0;
-}
-
-static int gz_file_pad(struct output_file *out, int64_t len)
-{
- off64_t ret;
- struct output_file_gz *outgz = to_output_file_gz(out);
-
- ret = gztell(outgz->gz_fd);
- if (ret < 0) {
- return -1;
- }
-
- if (ret >= len) {
- return 0;
- }
-
- ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET);
- if (ret < 0) {
- return -1;
- }
-
- gzwrite(outgz->gz_fd, "", 1);
-
- return 0;
-}
-
-static int gz_file_write(struct output_file *out, void *data, size_t len)
-{
- int ret;
- 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));
- if (ret == 0) {
- error("gzwrite %s", gzerror(outgz->gz_fd, NULL));
- return -1;
- }
- len -= ret;
- data = (char *)data + ret;
- }
-
- return 0;
-}
-
-static void gz_file_close(struct output_file *out)
-{
- struct output_file_gz *outgz = to_output_file_gz(out);
-
- gzclose(outgz->gz_fd);
- free(outgz);
-}
-
-static struct output_file_ops gz_file_ops = {
- .open = gz_file_open,
- .skip = gz_file_skip,
- .pad = gz_file_pad,
- .write = gz_file_write,
- .close = gz_file_close,
-};
-
-static int callback_file_open(struct output_file *out __unused, int fd __unused)
-{
- return 0;
-}
-
-static int callback_file_skip(struct output_file *out, int64_t off)
-{
- struct output_file_callback *outc = to_output_file_callback(out);
- int to_write;
- int ret;
-
- while (off > 0) {
- to_write = min(off, (int64_t)INT_MAX);
- ret = outc->write(outc->priv, NULL, to_write);
- if (ret < 0) {
- return ret;
- }
- off -= to_write;
- }
-
- return 0;
-}
-
-static int callback_file_pad(struct output_file *out __unused, int64_t len __unused)
-{
- return -1;
-}
-
-static int callback_file_write(struct output_file *out, void *data, size_t len)
-{
- struct output_file_callback *outc = to_output_file_callback(out);
-
- return outc->write(outc->priv, data, len);
-}
-
-static void callback_file_close(struct output_file *out)
-{
- struct output_file_callback *outc = to_output_file_callback(out);
-
- free(outc);
-}
-
-static struct output_file_ops callback_file_ops = {
- .open = callback_file_open,
- .skip = callback_file_skip,
- .pad = callback_file_pad,
- .write = callback_file_write,
- .close = callback_file_close,
-};
-
-int read_all(int fd, void *buf, size_t len)
-{
- size_t total = 0;
- int ret;
- char *ptr = buf;
-
- while (total < len) {
- ret = read(fd, ptr, len - total);
-
- if (ret < 0)
- return -errno;
-
- if (ret == 0)
- return -EINVAL;
-
- ptr += ret;
- total += ret;
- }
-
- return 0;
-}
-
-static int write_sparse_skip_chunk(struct output_file *out, int64_t skip_len)
-{
- chunk_header_t chunk_header;
- int ret;
-
- if (skip_len % out->block_size) {
- error("don't care size %"PRIi64" is not a multiple of the block size %u",
- skip_len, out->block_size);
- return -1;
- }
-
- /* We are skipping data, so emit a don't care chunk. */
- chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
- chunk_header.reserved1 = 0;
- chunk_header.chunk_sz = skip_len / out->block_size;
- chunk_header.total_sz = CHUNK_HEADER_LEN;
- ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
- if (ret < 0)
- return -1;
-
- out->cur_out_ptr += skip_len;
- out->chunk_cnt++;
-
- return 0;
-}
-
-static int write_sparse_fill_chunk(struct output_file *out, unsigned int len,
- uint32_t fill_val)
-{
- chunk_header_t chunk_header;
- int rnd_up_len, count;
- int ret;
-
- /* Round up the fill length to a multiple of the block size */
- rnd_up_len = ALIGN(len, out->block_size);
-
- /* Finally we can safely emit a chunk of data */
- chunk_header.chunk_type = CHUNK_TYPE_FILL;
- chunk_header.reserved1 = 0;
- chunk_header.chunk_sz = rnd_up_len / out->block_size;
- chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);
- ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
-
- if (ret < 0)
- return -1;
- ret = out->ops->write(out, &fill_val, sizeof(fill_val));
- if (ret < 0)
- return -1;
-
- if (out->use_crc) {
- count = out->block_size / sizeof(uint32_t);
- while (count--)
- out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t));
- }
-
- out->cur_out_ptr += rnd_up_len;
- out->chunk_cnt++;
-
- return 0;
-}
-
-static int write_sparse_data_chunk(struct output_file *out, unsigned int len,
- void *data)
-{
- chunk_header_t chunk_header;
- int rnd_up_len, zero_len;
- int ret;
-
- /* Round up the data length to a multiple of the block size */
- rnd_up_len = ALIGN(len, out->block_size);
- zero_len = rnd_up_len - len;
-
- /* Finally we can safely emit a chunk of data */
- chunk_header.chunk_type = CHUNK_TYPE_RAW;
- chunk_header.reserved1 = 0;
- chunk_header.chunk_sz = rnd_up_len / out->block_size;
- chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
- ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
-
- if (ret < 0)
- return -1;
- ret = out->ops->write(out, data, len);
- if (ret < 0)
- return -1;
- if (zero_len) {
- ret = out->ops->write(out, out->zero_buf, zero_len);
- if (ret < 0)
- return -1;
- }
-
- if (out->use_crc) {
- out->crc32 = sparse_crc32(out->crc32, data, len);
- if (zero_len)
- out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
- }
-
- out->cur_out_ptr += rnd_up_len;
- out->chunk_cnt++;
-
- return 0;
-}
-
-int write_sparse_end_chunk(struct output_file *out)
-{
- chunk_header_t chunk_header;
- int ret;
-
- if (out->use_crc) {
- chunk_header.chunk_type = CHUNK_TYPE_CRC32;
- chunk_header.reserved1 = 0;
- chunk_header.chunk_sz = 0;
- chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
-
- ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
- if (ret < 0) {
- return ret;
- }
- out->ops->write(out, &out->crc32, 4);
- if (ret < 0) {
- return ret;
- }
-
- out->chunk_cnt++;
- }
-
- return 0;
-}
-
-static struct sparse_file_ops sparse_file_ops = {
- .write_data_chunk = write_sparse_data_chunk,
- .write_fill_chunk = write_sparse_fill_chunk,
- .write_skip_chunk = write_sparse_skip_chunk,
- .write_end_chunk = write_sparse_end_chunk,
-};
-
-static int write_normal_data_chunk(struct output_file *out, unsigned int len,
- void *data)
-{
- int ret;
- unsigned int rnd_up_len = ALIGN(len, out->block_size);
-
- ret = out->ops->write(out, data, len);
- if (ret < 0) {
- return ret;
- }
-
- if (rnd_up_len > len) {
- ret = out->ops->skip(out, rnd_up_len - len);
- }
-
- return ret;
-}
-
-static int write_normal_fill_chunk(struct output_file *out, unsigned int len,
- uint32_t fill_val)
-{
- int ret;
- unsigned int i;
- unsigned int write_len;
-
- /* Initialize fill_buf with the fill_val */
- for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
- out->fill_buf[i] = fill_val;
- }
-
- while (len) {
- write_len = min(len, out->block_size);
- ret = out->ops->write(out, out->fill_buf, write_len);
- if (ret < 0) {
- return ret;
- }
-
- len -= write_len;
- }
-
- return 0;
-}
-
-static int write_normal_skip_chunk(struct output_file *out, int64_t len)
-{
- return out->ops->skip(out, len);
-}
-
-int write_normal_end_chunk(struct output_file *out)
-{
- return out->ops->pad(out, out->len);
-}
-
-static struct sparse_file_ops normal_file_ops = {
- .write_data_chunk = write_normal_data_chunk,
- .write_fill_chunk = write_normal_fill_chunk,
- .write_skip_chunk = write_normal_skip_chunk,
- .write_end_chunk = write_normal_end_chunk,
-};
-
-void output_file_close(struct output_file *out)
-{
- out->sparse_ops->write_end_chunk(out);
- out->ops->close(out);
-}
-
-static int output_file_init(struct output_file *out, int block_size,
- int64_t len, bool sparse, int chunks, bool crc)
-{
- int ret;
-
- out->len = len;
- out->block_size = block_size;
- out->cur_out_ptr = 0ll;
- out->chunk_cnt = 0;
- out->crc32 = 0;
- out->use_crc = crc;
-
- out->zero_buf = calloc(block_size, 1);
- if (!out->zero_buf) {
- error_errno("malloc zero_buf");
- return -ENOMEM;
- }
-
- out->fill_buf = calloc(block_size, 1);
- if (!out->fill_buf) {
- error_errno("malloc fill_buf");
- ret = -ENOMEM;
- goto err_fill_buf;
- }
-
- if (sparse) {
- out->sparse_ops = &sparse_file_ops;
- } else {
- out->sparse_ops = &normal_file_ops;
- }
-
- if (sparse) {
- sparse_header_t sparse_header = {
- .magic = SPARSE_HEADER_MAGIC,
- .major_version = SPARSE_HEADER_MAJOR_VER,
- .minor_version = SPARSE_HEADER_MINOR_VER,
- .file_hdr_sz = SPARSE_HEADER_LEN,
- .chunk_hdr_sz = CHUNK_HEADER_LEN,
- .blk_sz = out->block_size,
- .total_blks = DIV_ROUND_UP(out->len, out->block_size),
- .total_chunks = chunks,
- .image_checksum = 0
- };
-
- if (out->use_crc) {
- sparse_header.total_chunks++;
- }
-
- ret = out->ops->write(out, &sparse_header, sizeof(sparse_header));
- if (ret < 0) {
- goto err_write;
- }
- }
-
- return 0;
-
-err_write:
- free(out->fill_buf);
-err_fill_buf:
- free(out->zero_buf);
- return ret;
-}
-
-static struct output_file *output_file_new_gz(void)
-{
- struct output_file_gz *outgz = calloc(1, sizeof(struct output_file_gz));
- if (!outgz) {
- error_errno("malloc struct outgz");
- return NULL;
- }
-
- outgz->out.ops = &gz_file_ops;
-
- return &outgz->out;
-}
-
-static struct output_file *output_file_new_normal(void)
-{
- struct output_file_normal *outn = calloc(1, sizeof(struct output_file_normal));
- if (!outn) {
- error_errno("malloc struct outn");
- return NULL;
- }
-
- outn->out.ops = &file_ops;
-
- return &outn->out;
-}
-
-struct output_file *output_file_open_callback(
- int (*write)(void *, const void *, size_t),
- void *priv, unsigned int block_size, int64_t len,
- int gz __unused, int sparse, int chunks, int crc)
-{
- int ret;
- struct output_file_callback *outc;
-
- outc = calloc(1, sizeof(struct output_file_callback));
- if (!outc) {
- error_errno("malloc struct outc");
- return NULL;
- }
-
- outc->out.ops = &callback_file_ops;
- outc->priv = priv;
- outc->write = write;
-
- ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
- if (ret < 0) {
- free(outc);
- return NULL;
- }
-
- return &outc->out;
-}
-
-struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len,
- int gz, int sparse, int chunks, int crc)
-{
- int ret;
- struct output_file *out;
-
- if (gz) {
- out = output_file_new_gz();
- } else {
- out = output_file_new_normal();
- }
- if (!out) {
- return NULL;
- }
-
- out->ops->open(out, fd);
-
- ret = output_file_init(out, block_size, len, sparse, chunks, crc);
- if (ret < 0) {
- free(out);
- return NULL;
- }
-
- return out;
-}
-
-/* Write a contiguous region of data blocks from a memory buffer */
-int write_data_chunk(struct output_file *out, unsigned int len, void *data)
-{
- return out->sparse_ops->write_data_chunk(out, len, data);
-}
-
-/* Write a contiguous region of data blocks with a fill value */
-int write_fill_chunk(struct output_file *out, unsigned int len,
- uint32_t fill_val)
-{
- return out->sparse_ops->write_fill_chunk(out, len, fill_val);
-}
-
-int write_fd_chunk(struct output_file *out, unsigned int len,
- int fd, int64_t offset)
-{
- int ret;
- int64_t aligned_offset;
- int aligned_diff;
- uint64_t buffer_size;
- char *ptr;
-
- aligned_offset = offset & ~(4096 - 1);
- aligned_diff = offset - aligned_offset;
- buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
-
-#ifndef _WIN32
- if (buffer_size > SIZE_MAX)
- return -E2BIG;
- char *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd,
- aligned_offset);
- if (data == MAP_FAILED) {
- return -errno;
- }
- ptr = data + aligned_diff;
-#else
- off64_t pos;
- char *data = malloc(len);
- if (!data) {
- return -errno;
- }
- pos = lseek64(fd, offset, SEEK_SET);
- if (pos < 0) {
- free(data);
- return -errno;
- }
- ret = read_all(fd, data, len);
- if (ret < 0) {
- free(data);
- return ret;
- }
- ptr = data;
-#endif
-
- ret = out->sparse_ops->write_data_chunk(out, len, ptr);
-
-#ifndef _WIN32
- munmap(data, buffer_size);
-#else
- free(data);
-#endif
-
- return ret;
-}
-
-/* Write a contiguous region of data blocks from a file */
-int write_file_chunk(struct output_file *out, unsigned int len,
- const char *file, int64_t offset)
-{
- int ret;
-
- int file_fd = open(file, O_RDONLY | O_BINARY);
- if (file_fd < 0) {
- return -errno;
- }
-
- ret = write_fd_chunk(out, len, file_fd, offset);
-
- close(file_fd);
-
- return ret;
-}
-
-int write_skip_chunk(struct output_file *out, int64_t len)
-{
- return out->sparse_ops->write_skip_chunk(out, len);
-}
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
new file mode 100644
index 0000000..5388e77
--- /dev/null
+++ b/libsparse/output_file.cpp
@@ -0,0 +1,720 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <zlib.h>
+
+#include "defs.h"
+#include "output_file.h"
+#include "sparse_crc32.h"
+#include "sparse_format.h"
+
+#ifndef _WIN32
+#include <sys/mman.h>
+#define O_BINARY 0
+#else
+#define ftruncate64 ftruncate
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define ftruncate64 ftruncate
+#define mmap64 mmap
+#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))
+#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
+
+#define container_of(inner, outer_t, elem) ((outer_t*)((char*)(inner)-offsetof(outer_t, elem)))
+
+struct output_file_ops {
+ int (*open)(struct output_file*, int fd);
+ int (*skip)(struct output_file*, int64_t);
+ int (*pad)(struct output_file*, int64_t);
+ int (*write)(struct output_file*, void*, size_t);
+ void (*close)(struct output_file*);
+};
+
+struct sparse_file_ops {
+ int (*write_data_chunk)(struct output_file* out, unsigned int len, void* data);
+ int (*write_fill_chunk)(struct output_file* out, unsigned int len, uint32_t fill_val);
+ int (*write_skip_chunk)(struct output_file* out, int64_t len);
+ int (*write_end_chunk)(struct output_file* out);
+};
+
+struct output_file {
+ int64_t cur_out_ptr;
+ unsigned int chunk_cnt;
+ uint32_t crc32;
+ struct output_file_ops* ops;
+ struct sparse_file_ops* sparse_ops;
+ int use_crc;
+ unsigned int block_size;
+ int64_t len;
+ char* zero_buf;
+ uint32_t* fill_buf;
+ char* buf;
+};
+
+struct output_file_gz {
+ struct output_file out;
+ gzFile gz_fd;
+};
+
+#define to_output_file_gz(_o) container_of((_o), struct output_file_gz, out)
+
+struct output_file_normal {
+ struct output_file out;
+ int fd;
+};
+
+#define to_output_file_normal(_o) container_of((_o), struct output_file_normal, out)
+
+struct output_file_callback {
+ struct output_file out;
+ void* priv;
+ int (*write)(void* priv, const void* buf, size_t len);
+};
+
+#define to_output_file_callback(_o) container_of((_o), struct output_file_callback, out)
+
+static int file_open(struct output_file* out, int fd) {
+ struct output_file_normal* outn = to_output_file_normal(out);
+
+ outn->fd = fd;
+ return 0;
+}
+
+static int file_skip(struct output_file* out, int64_t cnt) {
+ off64_t ret;
+ struct output_file_normal* outn = to_output_file_normal(out);
+
+ ret = lseek64(outn->fd, cnt, SEEK_CUR);
+ if (ret < 0) {
+ error_errno("lseek64");
+ return -1;
+ }
+ return 0;
+}
+
+static int file_pad(struct output_file* out, int64_t len) {
+ int ret;
+ struct output_file_normal* outn = to_output_file_normal(out);
+
+ ret = ftruncate64(outn->fd, len);
+ if (ret < 0) {
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int file_write(struct output_file* out, void* data, size_t len) {
+ ssize_t ret;
+ struct output_file_normal* outn = to_output_file_normal(out);
+
+ while (len > 0) {
+ ret = write(outn->fd, data, len);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ error_errno("write");
+ return -1;
+ }
+
+ data = (char*)data + ret;
+ len -= ret;
+ }
+
+ return 0;
+}
+
+static void file_close(struct output_file* out) {
+ struct output_file_normal* outn = to_output_file_normal(out);
+
+ free(outn);
+}
+
+static struct output_file_ops file_ops = {
+ .open = file_open,
+ .skip = file_skip,
+ .pad = file_pad,
+ .write = file_write,
+ .close = file_close,
+};
+
+static int gz_file_open(struct output_file* out, int fd) {
+ struct output_file_gz* outgz = to_output_file_gz(out);
+
+ outgz->gz_fd = gzdopen(fd, "wb9");
+ if (!outgz->gz_fd) {
+ error_errno("gzopen");
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int gz_file_skip(struct output_file* out, int64_t cnt) {
+ off64_t ret;
+ struct output_file_gz* outgz = to_output_file_gz(out);
+
+ ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR);
+ if (ret < 0) {
+ error_errno("gzseek");
+ return -1;
+ }
+ return 0;
+}
+
+static int gz_file_pad(struct output_file* out, int64_t len) {
+ off64_t ret;
+ struct output_file_gz* outgz = to_output_file_gz(out);
+
+ ret = gztell(outgz->gz_fd);
+ if (ret < 0) {
+ return -1;
+ }
+
+ if (ret >= len) {
+ return 0;
+ }
+
+ ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET);
+ if (ret < 0) {
+ return -1;
+ }
+
+ gzwrite(outgz->gz_fd, "", 1);
+
+ return 0;
+}
+
+static int gz_file_write(struct output_file* out, void* data, size_t len) {
+ int ret;
+ 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));
+ if (ret == 0) {
+ error("gzwrite %s", gzerror(outgz->gz_fd, NULL));
+ return -1;
+ }
+ len -= ret;
+ data = (char*)data + ret;
+ }
+
+ return 0;
+}
+
+static void gz_file_close(struct output_file* out) {
+ struct output_file_gz* outgz = to_output_file_gz(out);
+
+ gzclose(outgz->gz_fd);
+ free(outgz);
+}
+
+static struct output_file_ops gz_file_ops = {
+ .open = gz_file_open,
+ .skip = gz_file_skip,
+ .pad = gz_file_pad,
+ .write = gz_file_write,
+ .close = gz_file_close,
+};
+
+static int callback_file_open(struct output_file* out __unused, int fd __unused) {
+ return 0;
+}
+
+static int callback_file_skip(struct output_file* out, int64_t off) {
+ struct output_file_callback* outc = to_output_file_callback(out);
+ int to_write;
+ int ret;
+
+ while (off > 0) {
+ to_write = min(off, (int64_t)INT_MAX);
+ ret = outc->write(outc->priv, NULL, to_write);
+ if (ret < 0) {
+ return ret;
+ }
+ off -= to_write;
+ }
+
+ return 0;
+}
+
+static int callback_file_pad(struct output_file* out __unused, int64_t len __unused) {
+ return -1;
+}
+
+static int callback_file_write(struct output_file* out, void* data, size_t len) {
+ struct output_file_callback* outc = to_output_file_callback(out);
+
+ return outc->write(outc->priv, data, len);
+}
+
+static void callback_file_close(struct output_file* out) {
+ struct output_file_callback* outc = to_output_file_callback(out);
+
+ free(outc);
+}
+
+static struct output_file_ops callback_file_ops = {
+ .open = callback_file_open,
+ .skip = callback_file_skip,
+ .pad = callback_file_pad,
+ .write = callback_file_write,
+ .close = callback_file_close,
+};
+
+int read_all(int fd, void* buf, size_t len) {
+ size_t total = 0;
+ int ret;
+ char* ptr = reinterpret_cast<char*>(buf);
+
+ while (total < len) {
+ ret = read(fd, ptr, len - total);
+
+ if (ret < 0) return -errno;
+
+ if (ret == 0) return -EINVAL;
+
+ ptr += ret;
+ total += ret;
+ }
+
+ return 0;
+}
+
+static int write_sparse_skip_chunk(struct output_file* out, int64_t skip_len) {
+ chunk_header_t chunk_header;
+ int ret;
+
+ if (skip_len % out->block_size) {
+ error("don't care size %" PRIi64 " is not a multiple of the block size %u", skip_len,
+ out->block_size);
+ return -1;
+ }
+
+ /* We are skipping data, so emit a don't care chunk. */
+ chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
+ chunk_header.reserved1 = 0;
+ chunk_header.chunk_sz = skip_len / out->block_size;
+ chunk_header.total_sz = CHUNK_HEADER_LEN;
+ ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+ if (ret < 0) return -1;
+
+ out->cur_out_ptr += skip_len;
+ out->chunk_cnt++;
+
+ return 0;
+}
+
+static int write_sparse_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+ chunk_header_t chunk_header;
+ int rnd_up_len, count;
+ int ret;
+
+ /* Round up the fill length to a multiple of the block size */
+ rnd_up_len = ALIGN(len, out->block_size);
+
+ /* Finally we can safely emit a chunk of data */
+ chunk_header.chunk_type = CHUNK_TYPE_FILL;
+ chunk_header.reserved1 = 0;
+ chunk_header.chunk_sz = rnd_up_len / out->block_size;
+ chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);
+ ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+
+ if (ret < 0) return -1;
+ ret = out->ops->write(out, &fill_val, sizeof(fill_val));
+ if (ret < 0) return -1;
+
+ if (out->use_crc) {
+ count = out->block_size / sizeof(uint32_t);
+ while (count--) out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t));
+ }
+
+ out->cur_out_ptr += rnd_up_len;
+ out->chunk_cnt++;
+
+ return 0;
+}
+
+static int write_sparse_data_chunk(struct output_file* out, unsigned int len, void* data) {
+ chunk_header_t chunk_header;
+ int rnd_up_len, zero_len;
+ int ret;
+
+ /* Round up the data length to a multiple of the block size */
+ rnd_up_len = ALIGN(len, out->block_size);
+ zero_len = rnd_up_len - len;
+
+ /* Finally we can safely emit a chunk of data */
+ chunk_header.chunk_type = CHUNK_TYPE_RAW;
+ chunk_header.reserved1 = 0;
+ chunk_header.chunk_sz = rnd_up_len / out->block_size;
+ chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
+ ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+
+ if (ret < 0) return -1;
+ ret = out->ops->write(out, data, len);
+ if (ret < 0) return -1;
+ if (zero_len) {
+ ret = out->ops->write(out, out->zero_buf, zero_len);
+ if (ret < 0) return -1;
+ }
+
+ if (out->use_crc) {
+ out->crc32 = sparse_crc32(out->crc32, data, len);
+ if (zero_len) out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
+ }
+
+ out->cur_out_ptr += rnd_up_len;
+ out->chunk_cnt++;
+
+ return 0;
+}
+
+int write_sparse_end_chunk(struct output_file* out) {
+ chunk_header_t chunk_header;
+ int ret;
+
+ if (out->use_crc) {
+ chunk_header.chunk_type = CHUNK_TYPE_CRC32;
+ chunk_header.reserved1 = 0;
+ chunk_header.chunk_sz = 0;
+ chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
+
+ ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+ if (ret < 0) {
+ return ret;
+ }
+ out->ops->write(out, &out->crc32, 4);
+ if (ret < 0) {
+ return ret;
+ }
+
+ out->chunk_cnt++;
+ }
+
+ return 0;
+}
+
+static struct sparse_file_ops sparse_file_ops = {
+ .write_data_chunk = write_sparse_data_chunk,
+ .write_fill_chunk = write_sparse_fill_chunk,
+ .write_skip_chunk = write_sparse_skip_chunk,
+ .write_end_chunk = write_sparse_end_chunk,
+};
+
+static int write_normal_data_chunk(struct output_file* out, unsigned int len, void* data) {
+ int ret;
+ unsigned int rnd_up_len = ALIGN(len, out->block_size);
+
+ ret = out->ops->write(out, data, len);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (rnd_up_len > len) {
+ ret = out->ops->skip(out, rnd_up_len - len);
+ }
+
+ return ret;
+}
+
+static int write_normal_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+ int ret;
+ unsigned int i;
+ unsigned int write_len;
+
+ /* Initialize fill_buf with the fill_val */
+ for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
+ out->fill_buf[i] = fill_val;
+ }
+
+ while (len) {
+ write_len = min(len, out->block_size);
+ ret = out->ops->write(out, out->fill_buf, write_len);
+ if (ret < 0) {
+ return ret;
+ }
+
+ len -= write_len;
+ }
+
+ return 0;
+}
+
+static int write_normal_skip_chunk(struct output_file* out, int64_t len) {
+ return out->ops->skip(out, len);
+}
+
+int write_normal_end_chunk(struct output_file* out) {
+ return out->ops->pad(out, out->len);
+}
+
+static struct sparse_file_ops normal_file_ops = {
+ .write_data_chunk = write_normal_data_chunk,
+ .write_fill_chunk = write_normal_fill_chunk,
+ .write_skip_chunk = write_normal_skip_chunk,
+ .write_end_chunk = write_normal_end_chunk,
+};
+
+void output_file_close(struct output_file* out) {
+ out->sparse_ops->write_end_chunk(out);
+ out->ops->close(out);
+}
+
+static int output_file_init(struct output_file* out, int block_size, int64_t len, bool sparse,
+ int chunks, bool crc) {
+ int ret;
+
+ out->len = len;
+ out->block_size = block_size;
+ out->cur_out_ptr = 0ll;
+ out->chunk_cnt = 0;
+ out->crc32 = 0;
+ out->use_crc = crc;
+
+ out->zero_buf = reinterpret_cast<char*>(calloc(block_size, 1));
+ if (!out->zero_buf) {
+ error_errno("malloc zero_buf");
+ return -ENOMEM;
+ }
+
+ out->fill_buf = reinterpret_cast<uint32_t*>(calloc(block_size, 1));
+ if (!out->fill_buf) {
+ error_errno("malloc fill_buf");
+ ret = -ENOMEM;
+ goto err_fill_buf;
+ }
+
+ if (sparse) {
+ out->sparse_ops = &sparse_file_ops;
+ } else {
+ out->sparse_ops = &normal_file_ops;
+ }
+
+ if (sparse) {
+ sparse_header_t sparse_header = {
+ .magic = SPARSE_HEADER_MAGIC,
+ .major_version = SPARSE_HEADER_MAJOR_VER,
+ .minor_version = SPARSE_HEADER_MINOR_VER,
+ .file_hdr_sz = SPARSE_HEADER_LEN,
+ .chunk_hdr_sz = CHUNK_HEADER_LEN,
+ .blk_sz = out->block_size,
+ .total_blks = static_cast<unsigned>(DIV_ROUND_UP(out->len, out->block_size)),
+ .total_chunks = static_cast<unsigned>(chunks),
+ .image_checksum = 0};
+
+ if (out->use_crc) {
+ sparse_header.total_chunks++;
+ }
+
+ ret = out->ops->write(out, &sparse_header, sizeof(sparse_header));
+ if (ret < 0) {
+ goto err_write;
+ }
+ }
+
+ return 0;
+
+err_write:
+ free(out->fill_buf);
+err_fill_buf:
+ free(out->zero_buf);
+ return ret;
+}
+
+static struct output_file* output_file_new_gz(void) {
+ struct output_file_gz* outgz =
+ reinterpret_cast<struct output_file_gz*>(calloc(1, sizeof(struct output_file_gz)));
+ if (!outgz) {
+ error_errno("malloc struct outgz");
+ return NULL;
+ }
+
+ outgz->out.ops = &gz_file_ops;
+
+ return &outgz->out;
+}
+
+static struct output_file* output_file_new_normal(void) {
+ struct output_file_normal* outn =
+ reinterpret_cast<struct output_file_normal*>(calloc(1, sizeof(struct output_file_normal)));
+ if (!outn) {
+ error_errno("malloc struct outn");
+ return NULL;
+ }
+
+ outn->out.ops = &file_ops;
+
+ return &outn->out;
+}
+
+struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
+ unsigned int block_size, int64_t len, int gz __unused,
+ int sparse, int chunks, int crc) {
+ int ret;
+ struct output_file_callback* outc;
+
+ outc =
+ reinterpret_cast<struct output_file_callback*>(calloc(1, sizeof(struct output_file_callback)));
+ if (!outc) {
+ error_errno("malloc struct outc");
+ return NULL;
+ }
+
+ outc->out.ops = &callback_file_ops;
+ outc->priv = priv;
+ outc->write = write;
+
+ ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
+ if (ret < 0) {
+ free(outc);
+ return NULL;
+ }
+
+ return &outc->out;
+}
+
+struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz,
+ int sparse, int chunks, int crc) {
+ int ret;
+ struct output_file* out;
+
+ if (gz) {
+ out = output_file_new_gz();
+ } else {
+ out = output_file_new_normal();
+ }
+ if (!out) {
+ return NULL;
+ }
+
+ out->ops->open(out, fd);
+
+ ret = output_file_init(out, block_size, len, sparse, chunks, crc);
+ if (ret < 0) {
+ free(out);
+ return NULL;
+ }
+
+ return out;
+}
+
+/* Write a contiguous region of data blocks from a memory buffer */
+int write_data_chunk(struct output_file* out, unsigned int len, void* data) {
+ return out->sparse_ops->write_data_chunk(out, len, data);
+}
+
+/* Write a contiguous region of data blocks with a fill value */
+int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+ return out->sparse_ops->write_fill_chunk(out, len, fill_val);
+}
+
+int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset) {
+ int ret;
+ int64_t aligned_offset;
+ int aligned_diff;
+ uint64_t buffer_size;
+ char* ptr;
+
+ aligned_offset = offset & ~(4096 - 1);
+ aligned_diff = offset - aligned_offset;
+ buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
+
+#ifndef _WIN32
+ if (buffer_size > SIZE_MAX) return -E2BIG;
+ char* data =
+ reinterpret_cast<char*>(mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd, aligned_offset));
+ if (data == MAP_FAILED) {
+ return -errno;
+ }
+ ptr = data + aligned_diff;
+#else
+ off64_t pos;
+ char* data = reinterpret_cast<char*>(malloc(len));
+ if (!data) {
+ return -errno;
+ }
+ pos = lseek64(fd, offset, SEEK_SET);
+ if (pos < 0) {
+ free(data);
+ return -errno;
+ }
+ ret = read_all(fd, data, len);
+ if (ret < 0) {
+ free(data);
+ return ret;
+ }
+ ptr = data;
+#endif
+
+ ret = out->sparse_ops->write_data_chunk(out, len, ptr);
+
+#ifndef _WIN32
+ munmap(data, buffer_size);
+#else
+ free(data);
+#endif
+
+ return ret;
+}
+
+/* Write a contiguous region of data blocks from a file */
+int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset) {
+ int ret;
+
+ int file_fd = open(file, O_RDONLY | O_BINARY);
+ if (file_fd < 0) {
+ return -errno;
+ }
+
+ ret = write_fd_chunk(out, len, file_fd, offset);
+
+ close(file_fd);
+
+ return ret;
+}
+
+int write_skip_chunk(struct output_file* out, int64_t len) {
+ return out->sparse_ops->write_skip_chunk(out, len);
+}
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index 690f610..278430b 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -25,23 +25,19 @@
struct output_file;
-struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len,
- int gz, int sparse, int chunks, int crc);
-struct output_file *output_file_open_callback(
- int (*write)(void *, const void *, size_t),
- void *priv, unsigned int block_size, int64_t len, int gz, int sparse,
- int chunks, int crc);
-int write_data_chunk(struct output_file *out, unsigned int len, void *data);
-int write_fill_chunk(struct output_file *out, unsigned int len,
- uint32_t fill_val);
-int write_file_chunk(struct output_file *out, unsigned int len,
- const char *file, int64_t offset);
-int write_fd_chunk(struct output_file *out, unsigned int len,
- int fd, int64_t offset);
-int write_skip_chunk(struct output_file *out, int64_t len);
-void output_file_close(struct output_file *out);
+struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz,
+ int sparse, int chunks, int crc);
+struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
+ unsigned int block_size, int64_t len, int gz,
+ int sparse, int chunks, int crc);
+int write_data_chunk(struct output_file* out, unsigned int len, void* data);
+int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val);
+int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset);
+int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset);
+int write_skip_chunk(struct output_file* out, int64_t len);
+void output_file_close(struct output_file* out);
-int read_all(int fd, void *buf, size_t len);
+int read_all(int fd, void* buf, size_t len);
#ifdef __cplusplus
}
diff --git a/libsparse/simg2img.c b/libsparse/simg2img.c
deleted file mode 100644
index b9b438e..0000000
--- a/libsparse/simg2img.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <sparse/sparse.h>
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-void usage()
-{
- fprintf(stderr, "Usage: simg2img <sparse_image_files> <raw_image_file>\n");
-}
-
-int main(int argc, char *argv[])
-{
- int in;
- int out;
- int i;
- struct sparse_file *s;
-
- if (argc < 3) {
- usage();
- exit(-1);
- }
-
- out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
- if (out < 0) {
- fprintf(stderr, "Cannot open output file %s\n", argv[argc - 1]);
- exit(-1);
- }
-
- for (i = 1; i < argc - 1; i++) {
- if (strcmp(argv[i], "-") == 0) {
- in = STDIN_FILENO;
- } else {
- in = open(argv[i], O_RDONLY | O_BINARY);
- if (in < 0) {
- fprintf(stderr, "Cannot open input file %s\n", argv[i]);
- exit(-1);
- }
- }
-
- s = sparse_file_import(in, true, false);
- if (!s) {
- fprintf(stderr, "Failed to read sparse file\n");
- exit(-1);
- }
-
- if (lseek(out, 0, SEEK_SET) == -1) {
- perror("lseek failed");
- exit(EXIT_FAILURE);
- }
-
- if (sparse_file_write(s, out, false, false, false) < 0) {
- fprintf(stderr, "Cannot write output file\n");
- exit(-1);
- }
- sparse_file_destroy(s);
- close(in);
- }
-
- close(out);
-
- exit(0);
-}
-
diff --git a/libsparse/simg2img.cpp b/libsparse/simg2img.cpp
new file mode 100644
index 0000000..8ba5f69
--- /dev/null
+++ b/libsparse/simg2img.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sparse/sparse.h>
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+void usage() {
+ fprintf(stderr, "Usage: simg2img <sparse_image_files> <raw_image_file>\n");
+}
+
+int main(int argc, char* argv[]) {
+ int in;
+ int out;
+ int i;
+ struct sparse_file* s;
+
+ if (argc < 3) {
+ usage();
+ exit(-1);
+ }
+
+ out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+ if (out < 0) {
+ fprintf(stderr, "Cannot open output file %s\n", argv[argc - 1]);
+ exit(-1);
+ }
+
+ for (i = 1; i < argc - 1; i++) {
+ if (strcmp(argv[i], "-") == 0) {
+ in = STDIN_FILENO;
+ } else {
+ in = open(argv[i], O_RDONLY | O_BINARY);
+ if (in < 0) {
+ fprintf(stderr, "Cannot open input file %s\n", argv[i]);
+ exit(-1);
+ }
+ }
+
+ s = sparse_file_import(in, true, false);
+ if (!s) {
+ fprintf(stderr, "Failed to read sparse file\n");
+ exit(-1);
+ }
+
+ if (lseek(out, 0, SEEK_SET) == -1) {
+ perror("lseek failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (sparse_file_write(s, out, false, false, false) < 0) {
+ fprintf(stderr, "Cannot write output file\n");
+ exit(-1);
+ }
+ sparse_file_destroy(s);
+ close(in);
+ }
+
+ close(out);
+
+ exit(0);
+}
diff --git a/libsparse/simg2simg.c b/libsparse/simg2simg.c
deleted file mode 100644
index 5f9ccf6..0000000
--- a/libsparse/simg2simg.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-#define _GNU_SOURCE
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-void usage()
-{
- fprintf(stderr, "Usage: simg2simg <sparse image file> <sparse_image_file> <max_size>\n");
-}
-
-int main(int argc, char *argv[])
-{
- int in;
- int out;
- int i;
- int ret;
- struct sparse_file *s;
- int64_t max_size;
- struct sparse_file **out_s;
- int files;
- char filename[4096];
-
- if (argc != 4) {
- usage();
- exit(-1);
- }
-
- max_size = atoll(argv[3]);
-
- in = open(argv[1], O_RDONLY | O_BINARY);
- if (in < 0) {
- fprintf(stderr, "Cannot open input file %s\n", argv[1]);
- exit(-1);
- }
-
- s = sparse_file_import(in, true, false);
- if (!s) {
- fprintf(stderr, "Failed to import sparse file\n");
- exit(-1);
- }
-
- files = sparse_file_resparse(s, max_size, NULL, 0);
- if (files < 0) {
- fprintf(stderr, "Failed to resparse\n");
- exit(-1);
- }
-
- out_s = calloc(sizeof(struct sparse_file *), files);
- if (!out_s) {
- fprintf(stderr, "Failed to allocate sparse file array\n");
- exit(-1);
- }
-
- files = sparse_file_resparse(s, max_size, out_s, files);
- if (files < 0) {
- fprintf(stderr, "Failed to resparse\n");
- exit(-1);
- }
-
- for (i = 0; i < files; i++) {
- ret = snprintf(filename, sizeof(filename), "%s.%d", argv[2], i);
- if (ret >= (int)sizeof(filename)) {
- fprintf(stderr, "Filename too long\n");
- exit(-1);
- }
-
- out = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
- if (out < 0) {
- fprintf(stderr, "Cannot open output file %s\n", argv[2]);
- exit(-1);
- }
-
- ret = sparse_file_write(out_s[i], out, false, true, false);
- if (ret) {
- fprintf(stderr, "Failed to write sparse file\n");
- exit(-1);
- }
- close(out);
- }
-
- close(in);
-
- exit(0);
-}
diff --git a/libsparse/simg2simg.cpp b/libsparse/simg2simg.cpp
new file mode 100644
index 0000000..7e65701
--- /dev/null
+++ b/libsparse/simg2simg.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+void usage() {
+ fprintf(stderr, "Usage: simg2simg <sparse image file> <sparse_image_file> <max_size>\n");
+}
+
+int main(int argc, char* argv[]) {
+ int in;
+ int out;
+ int i;
+ int ret;
+ struct sparse_file* s;
+ int64_t max_size;
+ struct sparse_file** out_s;
+ int files;
+ char filename[4096];
+
+ if (argc != 4) {
+ usage();
+ exit(-1);
+ }
+
+ max_size = atoll(argv[3]);
+
+ in = open(argv[1], O_RDONLY | O_BINARY);
+ if (in < 0) {
+ fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+ exit(-1);
+ }
+
+ s = sparse_file_import(in, true, false);
+ if (!s) {
+ fprintf(stderr, "Failed to import sparse file\n");
+ exit(-1);
+ }
+
+ files = sparse_file_resparse(s, max_size, NULL, 0);
+ if (files < 0) {
+ fprintf(stderr, "Failed to resparse\n");
+ exit(-1);
+ }
+
+ out_s = calloc(sizeof(struct sparse_file*), files);
+ if (!out_s) {
+ fprintf(stderr, "Failed to allocate sparse file array\n");
+ exit(-1);
+ }
+
+ files = sparse_file_resparse(s, max_size, out_s, files);
+ if (files < 0) {
+ fprintf(stderr, "Failed to resparse\n");
+ exit(-1);
+ }
+
+ for (i = 0; i < files; i++) {
+ ret = snprintf(filename, sizeof(filename), "%s.%d", argv[2], i);
+ if (ret >= (int)sizeof(filename)) {
+ fprintf(stderr, "Filename too long\n");
+ exit(-1);
+ }
+
+ out = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+ if (out < 0) {
+ fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+ exit(-1);
+ }
+
+ ret = sparse_file_write(out_s[i], out, false, true, false);
+ if (ret) {
+ fprintf(stderr, "Failed to write sparse file\n");
+ exit(-1);
+ }
+ close(out);
+ }
+
+ close(in);
+
+ exit(0);
+}
diff --git a/libsparse/sparse.c b/libsparse/sparse.c
deleted file mode 100644
index 466435f..0000000
--- a/libsparse/sparse.c
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include <sparse/sparse.h>
-
-#include "defs.h"
-#include "sparse_file.h"
-
-#include "output_file.h"
-#include "backed_block.h"
-#include "sparse_defs.h"
-#include "sparse_format.h"
-
-struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len)
-{
- struct sparse_file *s = calloc(sizeof(struct sparse_file), 1);
- if (!s) {
- return NULL;
- }
-
- s->backed_block_list = backed_block_list_new(block_size);
- if (!s->backed_block_list) {
- free(s);
- return NULL;
- }
-
- s->block_size = block_size;
- s->len = len;
-
- return s;
-}
-
-void sparse_file_destroy(struct sparse_file *s)
-{
- backed_block_list_destroy(s->backed_block_list);
- free(s);
-}
-
-int sparse_file_add_data(struct sparse_file *s,
- void *data, unsigned int len, unsigned int block)
-{
- return backed_block_add_data(s->backed_block_list, data, len, block);
-}
-
-int sparse_file_add_fill(struct sparse_file *s,
- uint32_t fill_val, unsigned int len, unsigned int block)
-{
- return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
-}
-
-int sparse_file_add_file(struct sparse_file *s,
- const char *filename, int64_t file_offset, unsigned int len,
- unsigned int block)
-{
- return backed_block_add_file(s->backed_block_list, filename, file_offset,
- len, block);
-}
-
-int sparse_file_add_fd(struct sparse_file *s,
- int fd, int64_t file_offset, unsigned int len, unsigned int block)
-{
- return backed_block_add_fd(s->backed_block_list, fd, file_offset,
- len, block);
-}
-unsigned int sparse_count_chunks(struct sparse_file *s)
-{
- struct backed_block *bb;
- unsigned int last_block = 0;
- unsigned int chunks = 0;
-
- for (bb = backed_block_iter_new(s->backed_block_list); bb;
- bb = backed_block_iter_next(bb)) {
- if (backed_block_block(bb) > last_block) {
- /* If there is a gap between chunks, add a skip chunk */
- chunks++;
- }
- chunks++;
- last_block = backed_block_block(bb) +
- DIV_ROUND_UP(backed_block_len(bb), s->block_size);
- }
- if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {
- chunks++;
- }
-
- return chunks;
-}
-
-static int sparse_file_write_block(struct output_file *out,
- struct backed_block *bb)
-{
- int ret = -EINVAL;
-
- switch (backed_block_type(bb)) {
- case BACKED_BLOCK_DATA:
- ret = write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
- break;
- case BACKED_BLOCK_FILE:
- ret = write_file_chunk(out, backed_block_len(bb),
- backed_block_filename(bb),
- backed_block_file_offset(bb));
- break;
- case BACKED_BLOCK_FD:
- ret = write_fd_chunk(out, backed_block_len(bb),
- backed_block_fd(bb),
- backed_block_file_offset(bb));
- break;
- case BACKED_BLOCK_FILL:
- ret = write_fill_chunk(out, backed_block_len(bb),
- backed_block_fill_val(bb));
- break;
- }
-
- return ret;
-}
-
-static int write_all_blocks(struct sparse_file *s, struct output_file *out)
-{
- struct backed_block *bb;
- unsigned int last_block = 0;
- int64_t pad;
- int ret = 0;
-
- for (bb = backed_block_iter_new(s->backed_block_list); bb;
- bb = backed_block_iter_next(bb)) {
- if (backed_block_block(bb) > last_block) {
- unsigned int blocks = backed_block_block(bb) - last_block;
- write_skip_chunk(out, (int64_t)blocks * s->block_size);
- }
- ret = sparse_file_write_block(out, bb);
- if (ret)
- return ret;
- last_block = backed_block_block(bb) +
- DIV_ROUND_UP(backed_block_len(bb), s->block_size);
- }
-
- pad = s->len - (int64_t)last_block * s->block_size;
- assert(pad >= 0);
- if (pad > 0) {
- write_skip_chunk(out, pad);
- }
-
- return 0;
-}
-
-int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
- bool crc)
-{
- int ret;
- int chunks;
- struct output_file *out;
-
- chunks = sparse_count_chunks(s);
- out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
-
- if (!out)
- return -ENOMEM;
-
- ret = write_all_blocks(s, out);
-
- output_file_close(out);
-
- return ret;
-}
-
-int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc,
- int (*write)(void *priv, const void *data, size_t len), void *priv)
-{
- int ret;
- int chunks;
- struct output_file *out;
-
- chunks = sparse_count_chunks(s);
- out = output_file_open_callback(write, priv, s->block_size, s->len, false,
- sparse, chunks, crc);
-
- if (!out)
- return -ENOMEM;
-
- ret = write_all_blocks(s, out);
-
- output_file_close(out);
-
- return ret;
-}
-
-struct chunk_data {
- void *priv;
- unsigned int block;
- unsigned int nr_blocks;
- int (*write)(void *priv, const void *data, size_t len,
- unsigned int block, unsigned int nr_blocks);
-};
-
-static int foreach_chunk_write(void *priv, const void *data, size_t len)
-{
- struct chunk_data *chk = priv;
-
- return chk->write(chk->priv, data, len, chk->block, chk->nr_blocks);
-}
-
-int sparse_file_foreach_chunk(struct sparse_file *s, bool sparse, bool crc,
- int (*write)(void *priv, const void *data, size_t len, unsigned int block,
- unsigned int nr_blocks),
- void *priv)
-{
- int ret;
- int chunks;
- struct chunk_data chk;
- struct output_file *out;
- struct backed_block *bb;
-
- chk.priv = priv;
- chk.write = write;
- chk.block = chk.nr_blocks = 0;
- chunks = sparse_count_chunks(s);
- out = output_file_open_callback(foreach_chunk_write, &chk,
- s->block_size, s->len, false, sparse,
- chunks, crc);
-
- if (!out)
- return -ENOMEM;
-
- for (bb = backed_block_iter_new(s->backed_block_list); bb;
- bb = backed_block_iter_next(bb)) {
- chk.block = backed_block_block(bb);
- chk.nr_blocks = (backed_block_len(bb) - 1) / s->block_size + 1;
- ret = sparse_file_write_block(out, bb);
- if (ret)
- return ret;
- }
-
- output_file_close(out);
-
- return ret;
-}
-
-static int out_counter_write(void *priv, const void *data __unused, size_t len)
-{
- int64_t *count = priv;
- *count += len;
- return 0;
-}
-
-int64_t sparse_file_len(struct sparse_file *s, bool sparse, bool crc)
-{
- int ret;
- int chunks = sparse_count_chunks(s);
- int64_t count = 0;
- struct output_file *out;
-
- out = output_file_open_callback(out_counter_write, &count,
- s->block_size, s->len, false, sparse, chunks, crc);
- if (!out) {
- return -1;
- }
-
- ret = write_all_blocks(s, out);
-
- output_file_close(out);
-
- if (ret < 0) {
- return -1;
- }
-
- return count;
-}
-
-unsigned int sparse_file_block_size(struct sparse_file *s)
-{
- return s->block_size;
-}
-
-static struct backed_block *move_chunks_up_to_len(struct sparse_file *from,
- struct sparse_file *to, unsigned int len)
-{
- int64_t count = 0;
- struct output_file *out_counter;
- struct backed_block *last_bb = NULL;
- struct backed_block *bb;
- struct backed_block *start;
- unsigned int last_block = 0;
- int64_t file_len = 0;
- int ret;
-
- /*
- * overhead is sparse file header, the potential end skip
- * chunk and crc chunk.
- */
- int overhead = sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t) +
- sizeof(uint32_t);
- len -= overhead;
-
- start = backed_block_iter_new(from->backed_block_list);
- out_counter = output_file_open_callback(out_counter_write, &count,
- to->block_size, to->len, false, true, 0, false);
- if (!out_counter) {
- return NULL;
- }
-
- for (bb = start; bb; bb = backed_block_iter_next(bb)) {
- count = 0;
- if (backed_block_block(bb) > last_block)
- count += sizeof(chunk_header_t);
- last_block = backed_block_block(bb) +
- DIV_ROUND_UP(backed_block_len(bb), to->block_size);
-
- /* will call out_counter_write to update count */
- ret = sparse_file_write_block(out_counter, bb);
- if (ret) {
- bb = NULL;
- goto out;
- }
- if (file_len + count > len) {
- /*
- * If the remaining available size is more than 1/8th of the
- * requested size, split the chunk. Results in sparse files that
- * are at least 7/8ths of the requested size
- */
- file_len += sizeof(chunk_header_t);
- if (!last_bb || (len - file_len > (len / 8))) {
- backed_block_split(from->backed_block_list, bb, len - file_len);
- last_bb = bb;
- }
- goto move;
- }
- file_len += count;
- last_bb = bb;
- }
-
-move:
- backed_block_list_move(from->backed_block_list,
- to->backed_block_list, start, last_bb);
-
-out:
- output_file_close(out_counter);
-
- return bb;
-}
-
-int sparse_file_resparse(struct sparse_file *in_s, unsigned int max_len,
- struct sparse_file **out_s, int out_s_count)
-{
- struct backed_block *bb;
- struct sparse_file *s;
- struct sparse_file *tmp;
- int c = 0;
-
- tmp = sparse_file_new(in_s->block_size, in_s->len);
- if (!tmp) {
- return -ENOMEM;
- }
-
- do {
- s = sparse_file_new(in_s->block_size, in_s->len);
-
- bb = move_chunks_up_to_len(in_s, s, max_len);
-
- if (c < out_s_count) {
- out_s[c] = s;
- } else {
- backed_block_list_move(s->backed_block_list, tmp->backed_block_list,
- NULL, NULL);
- sparse_file_destroy(s);
- }
- c++;
- } while (bb);
-
- backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list,
- NULL, NULL);
-
- sparse_file_destroy(tmp);
-
- return c;
-}
-
-void sparse_file_verbose(struct sparse_file *s)
-{
- s->verbose = true;
-}
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
new file mode 100644
index 0000000..6ff97b6
--- /dev/null
+++ b/libsparse/sparse.cpp
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <sparse/sparse.h>
+
+#include "defs.h"
+#include "sparse_file.h"
+
+#include "backed_block.h"
+#include "output_file.h"
+#include "sparse_defs.h"
+#include "sparse_format.h"
+
+struct sparse_file* sparse_file_new(unsigned int block_size, int64_t len) {
+ struct sparse_file* s = reinterpret_cast<sparse_file*>(calloc(sizeof(struct sparse_file), 1));
+ if (!s) {
+ return NULL;
+ }
+
+ s->backed_block_list = backed_block_list_new(block_size);
+ if (!s->backed_block_list) {
+ free(s);
+ return NULL;
+ }
+
+ s->block_size = block_size;
+ s->len = len;
+
+ return s;
+}
+
+void sparse_file_destroy(struct sparse_file* s) {
+ backed_block_list_destroy(s->backed_block_list);
+ free(s);
+}
+
+int sparse_file_add_data(struct sparse_file* s, void* data, unsigned int len, unsigned int block) {
+ return backed_block_add_data(s->backed_block_list, data, len, block);
+}
+
+int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, unsigned int len,
+ unsigned int block) {
+ return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
+}
+
+int sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t file_offset,
+ unsigned int len, unsigned int block) {
+ return backed_block_add_file(s->backed_block_list, filename, file_offset, len, block);
+}
+
+int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, unsigned int len,
+ unsigned int block) {
+ return backed_block_add_fd(s->backed_block_list, fd, file_offset, len, block);
+}
+unsigned int sparse_count_chunks(struct sparse_file* s) {
+ struct backed_block* bb;
+ unsigned int last_block = 0;
+ unsigned int chunks = 0;
+
+ for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+ if (backed_block_block(bb) > last_block) {
+ /* If there is a gap between chunks, add a skip chunk */
+ chunks++;
+ }
+ chunks++;
+ last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), s->block_size);
+ }
+ if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {
+ chunks++;
+ }
+
+ return chunks;
+}
+
+static int sparse_file_write_block(struct output_file* out, struct backed_block* bb) {
+ int ret = -EINVAL;
+
+ switch (backed_block_type(bb)) {
+ case BACKED_BLOCK_DATA:
+ ret = write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
+ break;
+ case BACKED_BLOCK_FILE:
+ ret = write_file_chunk(out, backed_block_len(bb), backed_block_filename(bb),
+ backed_block_file_offset(bb));
+ break;
+ case BACKED_BLOCK_FD:
+ ret = write_fd_chunk(out, backed_block_len(bb), backed_block_fd(bb),
+ backed_block_file_offset(bb));
+ break;
+ case BACKED_BLOCK_FILL:
+ ret = write_fill_chunk(out, backed_block_len(bb), backed_block_fill_val(bb));
+ break;
+ }
+
+ return ret;
+}
+
+static int write_all_blocks(struct sparse_file* s, struct output_file* out) {
+ struct backed_block* bb;
+ unsigned int last_block = 0;
+ int64_t pad;
+ int ret = 0;
+
+ for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+ if (backed_block_block(bb) > last_block) {
+ unsigned int blocks = backed_block_block(bb) - last_block;
+ write_skip_chunk(out, (int64_t)blocks * s->block_size);
+ }
+ ret = sparse_file_write_block(out, bb);
+ if (ret) return ret;
+ last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), s->block_size);
+ }
+
+ pad = s->len - (int64_t)last_block * s->block_size;
+ assert(pad >= 0);
+ if (pad > 0) {
+ write_skip_chunk(out, pad);
+ }
+
+ return 0;
+}
+
+int sparse_file_write(struct sparse_file* s, int fd, bool gz, bool sparse, bool crc) {
+ int ret;
+ int chunks;
+ struct output_file* out;
+
+ chunks = sparse_count_chunks(s);
+ out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
+
+ if (!out) return -ENOMEM;
+
+ ret = write_all_blocks(s, out);
+
+ output_file_close(out);
+
+ return ret;
+}
+
+int sparse_file_callback(struct sparse_file* s, bool sparse, bool crc,
+ int (*write)(void* priv, const void* data, size_t len), void* priv) {
+ int ret;
+ int chunks;
+ struct output_file* out;
+
+ chunks = sparse_count_chunks(s);
+ out = output_file_open_callback(write, priv, s->block_size, s->len, false, sparse, chunks, crc);
+
+ if (!out) return -ENOMEM;
+
+ ret = write_all_blocks(s, out);
+
+ output_file_close(out);
+
+ return ret;
+}
+
+struct chunk_data {
+ void* priv;
+ unsigned int block;
+ unsigned int nr_blocks;
+ int (*write)(void* priv, const void* data, size_t len, unsigned int block, unsigned int nr_blocks);
+};
+
+static int foreach_chunk_write(void* priv, const void* data, size_t len) {
+ struct chunk_data* chk = reinterpret_cast<chunk_data*>(priv);
+
+ return chk->write(chk->priv, data, len, chk->block, chk->nr_blocks);
+}
+
+int sparse_file_foreach_chunk(struct sparse_file* s, bool sparse, bool crc,
+ int (*write)(void* priv, const void* data, size_t len,
+ unsigned int block, unsigned int nr_blocks),
+ void* priv) {
+ int ret;
+ int chunks;
+ struct chunk_data chk;
+ struct output_file* out;
+ struct backed_block* bb;
+
+ chk.priv = priv;
+ chk.write = write;
+ chk.block = chk.nr_blocks = 0;
+ chunks = sparse_count_chunks(s);
+ out = output_file_open_callback(foreach_chunk_write, &chk, s->block_size, s->len, false, sparse,
+ chunks, crc);
+
+ if (!out) return -ENOMEM;
+
+ for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+ chk.block = backed_block_block(bb);
+ chk.nr_blocks = (backed_block_len(bb) - 1) / s->block_size + 1;
+ ret = sparse_file_write_block(out, bb);
+ if (ret) return ret;
+ }
+
+ output_file_close(out);
+
+ return ret;
+}
+
+static int out_counter_write(void* priv, const void* data __unused, size_t len) {
+ int64_t* count = reinterpret_cast<int64_t*>(priv);
+ *count += len;
+ return 0;
+}
+
+int64_t sparse_file_len(struct sparse_file* s, bool sparse, bool crc) {
+ int ret;
+ int chunks = sparse_count_chunks(s);
+ int64_t count = 0;
+ struct output_file* out;
+
+ out = output_file_open_callback(out_counter_write, &count, s->block_size, s->len, false, sparse,
+ chunks, crc);
+ if (!out) {
+ return -1;
+ }
+
+ ret = write_all_blocks(s, out);
+
+ output_file_close(out);
+
+ if (ret < 0) {
+ return -1;
+ }
+
+ return count;
+}
+
+unsigned int sparse_file_block_size(struct sparse_file* s) {
+ return s->block_size;
+}
+
+static struct backed_block* move_chunks_up_to_len(struct sparse_file* from, struct sparse_file* to,
+ unsigned int len) {
+ int64_t count = 0;
+ struct output_file* out_counter;
+ struct backed_block* last_bb = NULL;
+ struct backed_block* bb;
+ struct backed_block* start;
+ unsigned int last_block = 0;
+ int64_t file_len = 0;
+ int ret;
+
+ /*
+ * overhead is sparse file header, the potential end skip
+ * chunk and crc chunk.
+ */
+ int overhead = sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t) + sizeof(uint32_t);
+ len -= overhead;
+
+ start = backed_block_iter_new(from->backed_block_list);
+ out_counter = output_file_open_callback(out_counter_write, &count, to->block_size, to->len, false,
+ true, 0, false);
+ if (!out_counter) {
+ return NULL;
+ }
+
+ for (bb = start; bb; bb = backed_block_iter_next(bb)) {
+ count = 0;
+ if (backed_block_block(bb) > last_block) count += sizeof(chunk_header_t);
+ last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), to->block_size);
+
+ /* will call out_counter_write to update count */
+ ret = sparse_file_write_block(out_counter, bb);
+ if (ret) {
+ bb = NULL;
+ goto out;
+ }
+ if (file_len + count > len) {
+ /*
+ * If the remaining available size is more than 1/8th of the
+ * requested size, split the chunk. Results in sparse files that
+ * are at least 7/8ths of the requested size
+ */
+ file_len += sizeof(chunk_header_t);
+ if (!last_bb || (len - file_len > (len / 8))) {
+ backed_block_split(from->backed_block_list, bb, len - file_len);
+ last_bb = bb;
+ }
+ goto move;
+ }
+ file_len += count;
+ last_bb = bb;
+ }
+
+move:
+ backed_block_list_move(from->backed_block_list, to->backed_block_list, start, last_bb);
+
+out:
+ output_file_close(out_counter);
+
+ return bb;
+}
+
+int sparse_file_resparse(struct sparse_file* in_s, unsigned int max_len, struct sparse_file** out_s,
+ int out_s_count) {
+ struct backed_block* bb;
+ struct sparse_file* s;
+ struct sparse_file* tmp;
+ int c = 0;
+
+ tmp = sparse_file_new(in_s->block_size, in_s->len);
+ if (!tmp) {
+ return -ENOMEM;
+ }
+
+ do {
+ s = sparse_file_new(in_s->block_size, in_s->len);
+
+ bb = move_chunks_up_to_len(in_s, s, max_len);
+
+ if (c < out_s_count) {
+ out_s[c] = s;
+ } else {
+ backed_block_list_move(s->backed_block_list, tmp->backed_block_list, NULL, NULL);
+ sparse_file_destroy(s);
+ }
+ c++;
+ } while (bb);
+
+ backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list, NULL, NULL);
+
+ sparse_file_destroy(tmp);
+
+ return c;
+}
+
+void sparse_file_verbose(struct sparse_file* s) {
+ s->verbose = true;
+}
diff --git a/libsparse/sparse_crc32.c b/libsparse/sparse_crc32.c
deleted file mode 100644
index 38bfe4a..0000000
--- a/libsparse/sparse_crc32.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/*-
- * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
- * code or tables extracted from it, as desired without restriction.
- */
-
-/*
- * First, the polynomial itself and its table of feedback terms. The
- * polynomial is
- * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
- *
- * Note that we take it "backwards" and put the highest-order term in
- * the lowest-order bit. The X^32 term is "implied"; the LSB is the
- * X^31 term, etc. The X^0 term (usually shown as "+1") results in
- * the MSB being 1
- *
- * Note that the usual hardware shift register implementation, which
- * is what we're using (we're merely optimizing it by doing eight-bit
- * chunks at a time) shifts bits into the lowest-order term. In our
- * implementation, that means shifting towards the right. Why do we
- * do it this way? Because the calculated CRC must be transmitted in
- * order from highest-order term to lowest-order term. UARTs transmit
- * characters in order from LSB to MSB. By storing the CRC this way
- * we hand it to the UART in the order low-byte to high-byte; the UART
- * sends each low-bit to hight-bit; and the result is transmission bit
- * by bit from highest- to lowest-order term without requiring any bit
- * shuffling on our part. Reception works similarly
- *
- * The feedback terms table consists of 256, 32-bit entries. Notes
- *
- * The table can be generated at runtime if desired; code to do so
- * is shown later. It might not be obvious, but the feedback
- * terms simply represent the results of eight shift/xor opera
- * tions for all combinations of data and CRC register values
- *
- * The values must be right-shifted by eight bits by the "updcrc
- * logic; the shift must be unsigned (bring in zeroes). On some
- * hardware you could probably optimize the shift in assembler by
- * using byte-swap instructions
- * polynomial $edb88320
- *
- *
- * CRC32 code derived from work by Gary S. Brown.
- */
-
-/* Code taken from FreeBSD 8 */
-#include <stdint.h>
-
-static uint32_t crc32_tab[] = {
- 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
- 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
- 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
- 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
- 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
- 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
- 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
- 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
- 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
- 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
- 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
- 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
- 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
- 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
- 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
- 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
- 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
- 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
- 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
- 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
- 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
- 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
- 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
- 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
- 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
- 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
- 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
- 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
- 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
- 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
- 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
- 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
- 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
- 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
- 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
- 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
- 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
- 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
- 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
- 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
- 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
- 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
- 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
-};
-
-/*
- * A function that calculates the CRC-32 based on the table above is
- * given below for documentation purposes. An equivalent implementation
- * of this function that's actually used in the kernel can be found
- * in sys/libkern.h, where it can be inlined.
- */
-
-uint32_t sparse_crc32(uint32_t crc_in, const void *buf, int size)
-{
- const uint8_t *p = buf;
- uint32_t crc;
-
- crc = crc_in ^ ~0U;
- while (size--)
- crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
- return crc ^ ~0U;
-}
-
diff --git a/libsparse/sparse_crc32.cpp b/libsparse/sparse_crc32.cpp
new file mode 100644
index 0000000..267322c
--- /dev/null
+++ b/libsparse/sparse_crc32.cpp
@@ -0,0 +1,97 @@
+/*-
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ */
+
+/*
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to hight-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera
+ * tions for all combinations of data and CRC register values
+ *
+ * The values must be right-shifted by eight bits by the "updcrc
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions
+ * polynomial $edb88320
+ *
+ *
+ * CRC32 code derived from work by Gary S. Brown.
+ */
+
+/* Code taken from FreeBSD 8 */
+#include <stdint.h>
+#include <stdio.h>
+
+static uint32_t crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
+
+/*
+ * A function that calculates the CRC-32 based on the table above is
+ * given below for documentation purposes. An equivalent implementation
+ * of this function that's actually used in the kernel can be found
+ * in sys/libkern.h, where it can be inlined.
+ */
+
+uint32_t sparse_crc32(uint32_t crc_in, const void* buf, size_t size) {
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(buf);
+ uint32_t crc;
+
+ crc = crc_in ^ ~0U;
+ while (size--) crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+ return crc ^ ~0U;
+}
diff --git a/libsparse/sparse_crc32.h b/libsparse/sparse_crc32.h
index 50cd9e9..2702c4f 100644
--- a/libsparse/sparse_crc32.h
+++ b/libsparse/sparse_crc32.h
@@ -19,14 +19,6 @@
#include <stdint.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-uint32_t sparse_crc32(uint32_t crc, const void *buf, size_t size);
-
-#ifdef __cplusplus
-}
-#endif
+uint32_t sparse_crc32(uint32_t crc, const void* buf, size_t size);
#endif
diff --git a/libsparse/sparse_defs.h b/libsparse/sparse_defs.h
index b99cfd5..9137805 100644
--- a/libsparse/sparse_defs.h
+++ b/libsparse/sparse_defs.h
@@ -39,11 +39,14 @@
typedef unsigned short int u16;
typedef unsigned char u8;
-#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
-#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
+#define DIV_ROUND_UP(x, y) (((x) + (y)-1) / (y))
+#define ALIGN(x, y) ((y)*DIV_ROUND_UP((x), (y)))
#define ALIGN_DOWN(x, y) ((y) * ((x) / (y)))
-#define error(fmt, args...) do { fprintf(stderr, "error: %s: " fmt "\n", __func__, ## args); } while (0)
+#define error(fmt, args...) \
+ do { \
+ fprintf(stderr, "error: %s: " fmt "\n", __func__, ##args); \
+ } while (0)
#define error_errno(s, args...) error(s ": %s", ##args, strerror(errno))
#endif
diff --git a/libsparse/sparse_err.c b/libsparse/sparse_err.cpp
similarity index 73%
rename from libsparse/sparse_err.c
rename to libsparse/sparse_err.cpp
index 0f392ad..6886d31 100644
--- a/libsparse/sparse_err.c
+++ b/libsparse/sparse_err.cpp
@@ -20,14 +20,13 @@
#include <stdio.h>
#include <unistd.h>
-void sparse_default_print(const char *fmt, ...)
-{
- va_list argp;
+void sparse_default_print(const char* fmt, ...) {
+ va_list argp;
- va_start(argp, fmt);
- vfprintf(stderr, fmt, argp);
- va_end(argp);
+ va_start(argp, fmt);
+ vfprintf(stderr, fmt, argp);
+ va_end(argp);
}
-void (*sparse_print_error)(const char *fmt, ...) = sparse_default_print;
-void (*sparse_print_verbose)(const char *fmt, ...) = sparse_default_print;
+void (*sparse_print_error)(const char* fmt, ...) = sparse_default_print;
+void (*sparse_print_verbose)(const char* fmt, ...) = sparse_default_print;
diff --git a/libsparse/sparse_file.h b/libsparse/sparse_file.h
index 763f43f..e565f63 100644
--- a/libsparse/sparse_file.h
+++ b/libsparse/sparse_file.h
@@ -24,12 +24,12 @@
#include <sparse/sparse.h>
struct sparse_file {
- unsigned int block_size;
- int64_t len;
- bool verbose;
+ unsigned int block_size;
+ int64_t len;
+ bool verbose;
- struct backed_block_list *backed_block_list;
- struct output_file *out;
+ struct backed_block_list* backed_block_list;
+ struct output_file* out;
};
#ifdef __cplusplus
diff --git a/libsparse/sparse_format.h b/libsparse/sparse_format.h
index 779e038..a8a721e 100644
--- a/libsparse/sparse_format.h
+++ b/libsparse/sparse_format.h
@@ -23,31 +23,31 @@
#endif
typedef struct sparse_header {
- __le32 magic; /* 0xed26ff3a */
- __le16 major_version; /* (0x1) - reject images with higher major versions */
- __le16 minor_version; /* (0x0) - allow images with higer minor versions */
- __le16 file_hdr_sz; /* 28 bytes for first revision of the file format */
- __le16 chunk_hdr_sz; /* 12 bytes for first revision of the file format */
- __le32 blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */
- __le32 total_blks; /* total blocks in the non-sparse output image */
- __le32 total_chunks; /* total chunks in the sparse input image */
- __le32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */
- /* as 0. Standard 802.3 polynomial, use a Public Domain */
- /* table implementation */
+ __le32 magic; /* 0xed26ff3a */
+ __le16 major_version; /* (0x1) - reject images with higher major versions */
+ __le16 minor_version; /* (0x0) - allow images with higer minor versions */
+ __le16 file_hdr_sz; /* 28 bytes for first revision of the file format */
+ __le16 chunk_hdr_sz; /* 12 bytes for first revision of the file format */
+ __le32 blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */
+ __le32 total_blks; /* total blocks in the non-sparse output image */
+ __le32 total_chunks; /* total chunks in the sparse input image */
+ __le32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */
+ /* as 0. Standard 802.3 polynomial, use a Public Domain */
+ /* table implementation */
} sparse_header_t;
-#define SPARSE_HEADER_MAGIC 0xed26ff3a
+#define SPARSE_HEADER_MAGIC 0xed26ff3a
-#define CHUNK_TYPE_RAW 0xCAC1
-#define CHUNK_TYPE_FILL 0xCAC2
-#define CHUNK_TYPE_DONT_CARE 0xCAC3
-#define CHUNK_TYPE_CRC32 0xCAC4
+#define CHUNK_TYPE_RAW 0xCAC1
+#define CHUNK_TYPE_FILL 0xCAC2
+#define CHUNK_TYPE_DONT_CARE 0xCAC3
+#define CHUNK_TYPE_CRC32 0xCAC4
typedef struct chunk_header {
- __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
- __le16 reserved1;
- __le32 chunk_sz; /* in blocks in output image */
- __le32 total_sz; /* in bytes of chunk input file including chunk header and data */
+ __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
+ __le16 reserved1;
+ __le32 chunk_sz; /* in blocks in output image */
+ __le32 total_sz; /* in bytes of chunk input file including chunk header and data */
} chunk_header_t;
/* Following a Raw or Fill or CRC32 chunk is data.
diff --git a/libsparse/sparse_read.cpp b/libsparse/sparse_read.cpp
index 4379635..56e2c9a 100644
--- a/libsparse/sparse_read.cpp
+++ b/libsparse/sparse_read.cpp
@@ -17,16 +17,16 @@
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
-#include <algorithm>
-#include <inttypes.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <string>
#include <unistd.h>
+#include <algorithm>
+#include <string>
#include <sparse/sparse.h>
@@ -37,447 +37,541 @@
#include "sparse_file.h"
#include "sparse_format.h"
-
#if defined(__APPLE__) && defined(__MACH__)
#define lseek64 lseek
#define off64_t off_t
#endif
#define SPARSE_HEADER_MAJOR_VER 1
-#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
+#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
static constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;
-static char *copybuf;
+static char* copybuf;
-static std::string ErrorString(int err)
-{
- if (err == -EOVERFLOW) return "EOF while reading file";
- if (err == -EINVAL) return "Invalid sparse file format";
- if (err == -ENOMEM) return "Failed allocation while reading file";
- return android::base::StringPrintf("Unknown error %d", err);
+static std::string ErrorString(int err) {
+ if (err == -EOVERFLOW) return "EOF while reading file";
+ if (err == -EINVAL) return "Invalid sparse file format";
+ if (err == -ENOMEM) return "Failed allocation while reading file";
+ return android::base::StringPrintf("Unknown error %d", err);
}
-static void verbose_error(bool verbose, int err, const char *fmt, ...)
-{
- if (!verbose) return;
+class SparseFileSource {
+ public:
+ /* Seeks the source ahead by the given offset. */
+ virtual void Seek(int64_t offset) = 0;
- std::string msg = ErrorString(err);
- if (fmt) {
- msg += " at ";
- va_list argp;
- va_start(argp, fmt);
- android::base::StringAppendV(&msg, fmt, argp);
- va_end(argp);
- }
- sparse_print_verbose("%s\n", msg.c_str());
+ /* Return the current offset. */
+ virtual int64_t GetOffset() = 0;
+
+ /* Set the current offset. Return 0 if successful. */
+ virtual int SetOffset(int64_t offset) = 0;
+
+ /* Adds the given length from the current offset of the source to the file at the given block.
+ * Return 0 if successful. */
+ virtual int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) = 0;
+
+ /* Get data of fixed size from the current offset and seek len bytes. Return 0 if successful. */
+ virtual int ReadValue(void* ptr, int len) = 0;
+
+ /* Find the crc32 of the next len bytes and seek ahead len bytes. Return 0 if successful. */
+ virtual int GetCrc32(uint32_t* crc32, int64_t len) = 0;
+
+ virtual ~SparseFileSource(){};
+};
+
+class SparseFileFdSource : public SparseFileSource {
+ private:
+ int fd;
+
+ public:
+ SparseFileFdSource(int fd) : fd(fd) {}
+ ~SparseFileFdSource() override {}
+
+ void Seek(int64_t off) override { lseek64(fd, off, SEEK_CUR); }
+
+ int64_t GetOffset() override { return lseek64(fd, 0, SEEK_CUR); }
+
+ int SetOffset(int64_t offset) override {
+ return lseek64(fd, offset, SEEK_SET) == offset ? 0 : -errno;
+ }
+
+ int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
+ return sparse_file_add_fd(s, fd, GetOffset(), len, block);
+ }
+
+ int ReadValue(void* ptr, int len) override { return read_all(fd, ptr, len); }
+
+ int GetCrc32(uint32_t* crc32, int64_t len) override {
+ int chunk;
+ int ret;
+ while (len) {
+ chunk = std::min(len, COPY_BUF_SIZE);
+ ret = read_all(fd, copybuf, chunk);
+ if (ret < 0) {
+ return ret;
+ }
+ *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+ len -= chunk;
+ }
+ return 0;
+ }
+};
+
+class SparseFileBufSource : public SparseFileSource {
+ private:
+ char* buf;
+ int64_t offset;
+
+ public:
+ SparseFileBufSource(char* buf) : buf(buf), offset(0) {}
+ ~SparseFileBufSource() override {}
+
+ void Seek(int64_t off) override {
+ buf += off;
+ offset += off;
+ }
+
+ int64_t GetOffset() override { return offset; }
+
+ int SetOffset(int64_t off) override {
+ buf += off - offset;
+ offset = off;
+ return 0;
+ }
+
+ int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
+ return sparse_file_add_data(s, buf, len, block);
+ }
+
+ int ReadValue(void* ptr, int len) override {
+ memcpy(ptr, buf, len);
+ Seek(len);
+ return 0;
+ }
+
+ int GetCrc32(uint32_t* crc32, int64_t len) override {
+ *crc32 = sparse_crc32(*crc32, buf, len);
+ Seek(len);
+ return 0;
+ }
+};
+
+static void verbose_error(bool verbose, int err, const char* fmt, ...) {
+ if (!verbose) return;
+
+ std::string msg = ErrorString(err);
+ if (fmt) {
+ msg += " at ";
+ va_list argp;
+ va_start(argp, fmt);
+ android::base::StringAppendV(&msg, fmt, argp);
+ va_end(argp);
+ }
+ sparse_print_verbose("%s\n", msg.c_str());
}
-static int process_raw_chunk(struct sparse_file *s, unsigned int chunk_size,
- int fd, int64_t offset, unsigned int blocks, unsigned int block,
- uint32_t *crc32)
-{
- int ret;
- int chunk;
- int64_t len = blocks * s->block_size;
+static int process_raw_chunk(struct sparse_file* s, unsigned int chunk_size,
+ SparseFileSource* source, unsigned int blocks, unsigned int block,
+ uint32_t* crc32) {
+ int ret;
+ int64_t len = blocks * s->block_size;
- if (chunk_size % s->block_size != 0) {
- return -EINVAL;
- }
+ if (chunk_size % s->block_size != 0) {
+ return -EINVAL;
+ }
- if (chunk_size / s->block_size != blocks) {
- return -EINVAL;
- }
+ if (chunk_size / s->block_size != blocks) {
+ return -EINVAL;
+ }
- ret = sparse_file_add_fd(s, fd, offset, len, block);
- if (ret < 0) {
- return ret;
- }
+ ret = source->AddToSparseFile(s, len, block);
+ if (ret < 0) {
+ return ret;
+ }
- if (crc32) {
- while (len) {
- chunk = std::min(len, COPY_BUF_SIZE);
- ret = read_all(fd, copybuf, chunk);
- if (ret < 0) {
- return ret;
- }
- *crc32 = sparse_crc32(*crc32, copybuf, chunk);
- len -= chunk;
- }
- } else {
- lseek64(fd, len, SEEK_CUR);
- }
+ if (crc32) {
+ ret = source->GetCrc32(crc32, len);
+ if (ret < 0) {
+ return ret;
+ }
+ } else {
+ source->Seek(len);
+ }
- return 0;
+ return 0;
}
-static int process_fill_chunk(struct sparse_file *s, unsigned int chunk_size,
- int fd, unsigned int blocks, unsigned int block, uint32_t *crc32)
-{
- int ret;
- int chunk;
- int64_t len = (int64_t)blocks * s->block_size;
- uint32_t fill_val;
- uint32_t *fillbuf;
- unsigned int i;
+static int process_fill_chunk(struct sparse_file* s, unsigned int chunk_size,
+ SparseFileSource* source, unsigned int blocks, unsigned int block,
+ uint32_t* crc32) {
+ int ret;
+ int chunk;
+ int64_t len = (int64_t)blocks * s->block_size;
+ uint32_t fill_val;
+ uint32_t* fillbuf;
+ unsigned int i;
- if (chunk_size != sizeof(fill_val)) {
- return -EINVAL;
- }
+ if (chunk_size != sizeof(fill_val)) {
+ return -EINVAL;
+ }
- ret = read_all(fd, &fill_val, sizeof(fill_val));
- if (ret < 0) {
- return ret;
- }
+ ret = source->ReadValue(&fill_val, sizeof(fill_val));
+ if (ret < 0) {
+ return ret;
+ }
- ret = sparse_file_add_fill(s, fill_val, len, block);
- if (ret < 0) {
- return ret;
- }
+ ret = sparse_file_add_fill(s, fill_val, len, block);
+ if (ret < 0) {
+ return ret;
+ }
- if (crc32) {
- /* Fill copy_buf with the fill value */
- fillbuf = (uint32_t *)copybuf;
- for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
- fillbuf[i] = fill_val;
- }
+ if (crc32) {
+ /* Fill copy_buf with the fill value */
+ fillbuf = (uint32_t*)copybuf;
+ for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
+ fillbuf[i] = fill_val;
+ }
- while (len) {
- chunk = std::min(len, COPY_BUF_SIZE);
- *crc32 = sparse_crc32(*crc32, copybuf, chunk);
- len -= chunk;
- }
- }
+ while (len) {
+ chunk = std::min(len, COPY_BUF_SIZE);
+ *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+ len -= chunk;
+ }
+ }
- return 0;
+ return 0;
}
-static int process_skip_chunk(struct sparse_file *s, unsigned int chunk_size,
- int fd __unused, unsigned int blocks,
- unsigned int block __unused, uint32_t *crc32)
-{
- if (chunk_size != 0) {
- return -EINVAL;
- }
+static int process_skip_chunk(struct sparse_file* s, unsigned int chunk_size,
+ SparseFileSource* source __unused, unsigned int blocks,
+ unsigned int block __unused, uint32_t* crc32) {
+ if (chunk_size != 0) {
+ return -EINVAL;
+ }
- if (crc32) {
- int64_t len = (int64_t)blocks * s->block_size;
- memset(copybuf, 0, COPY_BUF_SIZE);
+ if (crc32) {
+ int64_t len = (int64_t)blocks * s->block_size;
+ memset(copybuf, 0, COPY_BUF_SIZE);
- while (len) {
- int chunk = std::min(len, COPY_BUF_SIZE);
- *crc32 = sparse_crc32(*crc32, copybuf, chunk);
- len -= chunk;
- }
- }
+ while (len) {
+ int chunk = std::min(len, COPY_BUF_SIZE);
+ *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+ len -= chunk;
+ }
+ }
- return 0;
+ return 0;
}
-static int process_crc32_chunk(int fd, unsigned int chunk_size, uint32_t *crc32)
-{
- uint32_t file_crc32;
- int ret;
+static int process_crc32_chunk(SparseFileSource* source, unsigned int chunk_size, uint32_t* crc32) {
+ uint32_t file_crc32;
- if (chunk_size != sizeof(file_crc32)) {
- return -EINVAL;
- }
+ if (chunk_size != sizeof(file_crc32)) {
+ return -EINVAL;
+ }
- ret = read_all(fd, &file_crc32, sizeof(file_crc32));
- if (ret < 0) {
- return ret;
- }
+ int ret = source->ReadValue(&file_crc32, sizeof(file_crc32));
+ if (ret < 0) {
+ return ret;
+ }
- if (crc32 != NULL && file_crc32 != *crc32) {
- return -EINVAL;
- }
+ if (crc32 != NULL && file_crc32 != *crc32) {
+ return -EINVAL;
+ }
- return 0;
+ return 0;
}
-static int process_chunk(struct sparse_file *s, int fd, off64_t offset,
- unsigned int chunk_hdr_sz, chunk_header_t *chunk_header,
- unsigned int cur_block, uint32_t *crc_ptr)
-{
- int ret;
- unsigned int chunk_data_size;
+static int process_chunk(struct sparse_file* s, SparseFileSource* source, unsigned int chunk_hdr_sz,
+ chunk_header_t* chunk_header, unsigned int cur_block, uint32_t* crc_ptr) {
+ int ret;
+ unsigned int chunk_data_size;
+ int64_t offset = source->GetOffset();
- chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
+ chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
- switch (chunk_header->chunk_type) {
- case CHUNK_TYPE_RAW:
- ret = process_raw_chunk(s, chunk_data_size, fd, offset,
- chunk_header->chunk_sz, cur_block, crc_ptr);
- if (ret < 0) {
- verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
- return ret;
- }
- return chunk_header->chunk_sz;
- case CHUNK_TYPE_FILL:
- ret = process_fill_chunk(s, chunk_data_size, fd,
- chunk_header->chunk_sz, cur_block, crc_ptr);
- if (ret < 0) {
- verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
- return ret;
- }
- return chunk_header->chunk_sz;
- case CHUNK_TYPE_DONT_CARE:
- ret = process_skip_chunk(s, chunk_data_size, fd,
- chunk_header->chunk_sz, cur_block, crc_ptr);
- if (chunk_data_size != 0) {
- if (ret < 0) {
- verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
- return ret;
- }
- }
- return chunk_header->chunk_sz;
- case CHUNK_TYPE_CRC32:
- ret = process_crc32_chunk(fd, chunk_data_size, crc_ptr);
- if (ret < 0) {
- verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64,
- offset);
- return ret;
- }
- return 0;
- default:
- verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64,
- chunk_header->chunk_type, offset);
- }
+ switch (chunk_header->chunk_type) {
+ case CHUNK_TYPE_RAW:
+ ret =
+ process_raw_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block, crc_ptr);
+ if (ret < 0) {
+ verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
+ return ret;
+ }
+ return chunk_header->chunk_sz;
+ case CHUNK_TYPE_FILL:
+ ret = process_fill_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,
+ crc_ptr);
+ if (ret < 0) {
+ verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
+ return ret;
+ }
+ return chunk_header->chunk_sz;
+ case CHUNK_TYPE_DONT_CARE:
+ ret = process_skip_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,
+ crc_ptr);
+ if (chunk_data_size != 0) {
+ if (ret < 0) {
+ verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
+ return ret;
+ }
+ }
+ return chunk_header->chunk_sz;
+ case CHUNK_TYPE_CRC32:
+ ret = process_crc32_chunk(source, chunk_data_size, crc_ptr);
+ if (ret < 0) {
+ verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64, offset);
+ return ret;
+ }
+ return 0;
+ default:
+ verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64, chunk_header->chunk_type,
+ offset);
+ }
- return 0;
+ return 0;
}
-static int sparse_file_read_sparse(struct sparse_file *s, int fd, bool crc)
-{
- int ret;
- unsigned int i;
- sparse_header_t sparse_header;
- chunk_header_t chunk_header;
- uint32_t crc32 = 0;
- uint32_t *crc_ptr = 0;
- unsigned int cur_block = 0;
- off64_t offset;
+static int sparse_file_read_sparse(struct sparse_file* s, SparseFileSource* source, bool crc) {
+ int ret;
+ unsigned int i;
+ sparse_header_t sparse_header;
+ chunk_header_t chunk_header;
+ uint32_t crc32 = 0;
+ uint32_t* crc_ptr = 0;
+ unsigned int cur_block = 0;
- if (!copybuf) {
- copybuf = (char *)malloc(COPY_BUF_SIZE);
- }
+ if (!copybuf) {
+ copybuf = (char*)malloc(COPY_BUF_SIZE);
+ }
- if (!copybuf) {
- return -ENOMEM;
- }
+ if (!copybuf) {
+ return -ENOMEM;
+ }
- if (crc) {
- crc_ptr = &crc32;
- }
+ if (crc) {
+ crc_ptr = &crc32;
+ }
- ret = read_all(fd, &sparse_header, sizeof(sparse_header));
- if (ret < 0) {
- return ret;
- }
+ ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
+ if (ret < 0) {
+ return ret;
+ }
- if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
- return -EINVAL;
- }
+ if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
+ return -EINVAL;
+ }
- if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
- return -EINVAL;
- }
+ if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
+ return -EINVAL;
+ }
- if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
- return -EINVAL;
- }
+ if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
+ return -EINVAL;
+ }
- if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
- return -EINVAL;
- }
+ if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
+ return -EINVAL;
+ }
- if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
- /* Skip the remaining bytes in a header that is longer than
- * we expected.
- */
- lseek64(fd, sparse_header.file_hdr_sz - SPARSE_HEADER_LEN, SEEK_CUR);
- }
+ if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
+ /* Skip the remaining bytes in a header that is longer than
+ * we expected.
+ */
+ source->Seek(sparse_header.file_hdr_sz - SPARSE_HEADER_LEN);
+ }
- for (i = 0; i < sparse_header.total_chunks; i++) {
- ret = read_all(fd, &chunk_header, sizeof(chunk_header));
- if (ret < 0) {
- return ret;
- }
+ for (i = 0; i < sparse_header.total_chunks; i++) {
+ ret = source->ReadValue(&chunk_header, sizeof(chunk_header));
+ if (ret < 0) {
+ return ret;
+ }
- if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
- /* Skip the remaining bytes in a header that is longer than
- * we expected.
- */
- lseek64(fd, sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN, SEEK_CUR);
- }
+ if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
+ /* Skip the remaining bytes in a header that is longer than
+ * we expected.
+ */
+ source->Seek(sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN);
+ }
- offset = lseek64(fd, 0, SEEK_CUR);
+ ret = process_chunk(s, source, sparse_header.chunk_hdr_sz, &chunk_header, cur_block, crc_ptr);
+ if (ret < 0) {
+ return ret;
+ }
- ret = process_chunk(s, fd, offset, sparse_header.chunk_hdr_sz, &chunk_header,
- cur_block, crc_ptr);
- if (ret < 0) {
- return ret;
- }
+ cur_block += ret;
+ }
- cur_block += ret;
- }
+ if (sparse_header.total_blks != cur_block) {
+ return -EINVAL;
+ }
- if (sparse_header.total_blks != cur_block) {
- return -EINVAL;
- }
-
- return 0;
+ return 0;
}
-static int sparse_file_read_normal(struct sparse_file *s, int fd)
-{
- int ret;
- uint32_t *buf = (uint32_t *)malloc(s->block_size);
- unsigned int block = 0;
- int64_t remain = s->len;
- int64_t offset = 0;
- unsigned int to_read;
- unsigned int i;
- bool sparse_block;
+static int sparse_file_read_normal(struct sparse_file* s, int fd) {
+ int ret;
+ uint32_t* buf = (uint32_t*)malloc(s->block_size);
+ unsigned int block = 0;
+ int64_t remain = s->len;
+ int64_t offset = 0;
+ unsigned int to_read;
+ unsigned int i;
+ bool sparse_block;
- if (!buf) {
- return -ENOMEM;
- }
+ if (!buf) {
+ return -ENOMEM;
+ }
- while (remain > 0) {
- to_read = std::min(remain, (int64_t)(s->block_size));
- ret = read_all(fd, buf, to_read);
- if (ret < 0) {
- error("failed to read sparse file");
- free(buf);
- return ret;
- }
+ while (remain > 0) {
+ to_read = std::min(remain, (int64_t)(s->block_size));
+ ret = read_all(fd, buf, to_read);
+ if (ret < 0) {
+ error("failed to read sparse file");
+ free(buf);
+ return ret;
+ }
- if (to_read == s->block_size) {
- sparse_block = true;
- for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
- if (buf[0] != buf[i]) {
- sparse_block = false;
- break;
- }
- }
- } else {
- sparse_block = false;
- }
+ if (to_read == s->block_size) {
+ sparse_block = true;
+ for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
+ if (buf[0] != buf[i]) {
+ sparse_block = false;
+ break;
+ }
+ }
+ } else {
+ sparse_block = false;
+ }
- if (sparse_block) {
- /* TODO: add flag to use skip instead of fill for buf[0] == 0 */
- sparse_file_add_fill(s, buf[0], to_read, block);
- } else {
- sparse_file_add_fd(s, fd, offset, to_read, block);
- }
+ if (sparse_block) {
+ /* TODO: add flag to use skip instead of fill for buf[0] == 0 */
+ sparse_file_add_fill(s, buf[0], to_read, block);
+ } else {
+ sparse_file_add_fd(s, fd, offset, to_read, block);
+ }
- remain -= to_read;
- offset += to_read;
- block++;
- }
+ remain -= to_read;
+ offset += to_read;
+ block++;
+ }
- free(buf);
- return 0;
+ free(buf);
+ return 0;
}
-int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc)
-{
- if (crc && !sparse) {
- return -EINVAL;
- }
+int sparse_file_read(struct sparse_file* s, int fd, bool sparse, bool crc) {
+ if (crc && !sparse) {
+ return -EINVAL;
+ }
- if (sparse) {
- return sparse_file_read_sparse(s, fd, crc);
- } else {
- return sparse_file_read_normal(s, fd);
- }
+ if (sparse) {
+ SparseFileFdSource source(fd);
+ return sparse_file_read_sparse(s, &source, crc);
+ } else {
+ return sparse_file_read_normal(s, fd);
+ }
}
-struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc)
-{
- int ret;
- sparse_header_t sparse_header;
- int64_t len;
- struct sparse_file *s;
-
- ret = read_all(fd, &sparse_header, sizeof(sparse_header));
- if (ret < 0) {
- verbose_error(verbose, ret, "header");
- return NULL;
- }
-
- if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
- verbose_error(verbose, -EINVAL, "header magic");
- return NULL;
- }
-
- if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
- verbose_error(verbose, -EINVAL, "header major version");
- return NULL;
- }
-
- if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
- return NULL;
- }
-
- if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
- return NULL;
- }
-
- len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
- s = sparse_file_new(sparse_header.blk_sz, len);
- if (!s) {
- verbose_error(verbose, -EINVAL, NULL);
- return NULL;
- }
-
- ret = lseek64(fd, 0, SEEK_SET);
- if (ret < 0) {
- verbose_error(verbose, ret, "seeking");
- sparse_file_destroy(s);
- return NULL;
- }
-
- s->verbose = verbose;
-
- ret = sparse_file_read(s, fd, true, crc);
- if (ret < 0) {
- sparse_file_destroy(s);
- return NULL;
- }
-
- return s;
+int sparse_file_read_buf(struct sparse_file* s, char* buf, bool crc) {
+ SparseFileBufSource source(buf);
+ return sparse_file_read_sparse(s, &source, crc);
}
-struct sparse_file *sparse_file_import_auto(int fd, bool crc, bool verbose)
-{
- struct sparse_file *s;
- int64_t len;
- int ret;
+static struct sparse_file* sparse_file_import_source(SparseFileSource* source, bool verbose,
+ bool crc) {
+ int ret;
+ sparse_header_t sparse_header;
+ int64_t len;
+ struct sparse_file* s;
- s = sparse_file_import(fd, verbose, crc);
- if (s) {
- return s;
- }
+ ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
+ if (ret < 0) {
+ verbose_error(verbose, ret, "header");
+ return NULL;
+ }
- len = lseek64(fd, 0, SEEK_END);
- if (len < 0) {
- return NULL;
- }
+ if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
+ verbose_error(verbose, -EINVAL, "header magic");
+ return NULL;
+ }
- lseek64(fd, 0, SEEK_SET);
+ if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
+ verbose_error(verbose, -EINVAL, "header major version");
+ return NULL;
+ }
- s = sparse_file_new(4096, len);
- if (!s) {
- return NULL;
- }
+ if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
+ return NULL;
+ }
- ret = sparse_file_read_normal(s, fd);
- if (ret < 0) {
- sparse_file_destroy(s);
- return NULL;
- }
+ if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
+ return NULL;
+ }
- return s;
+ len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
+ s = sparse_file_new(sparse_header.blk_sz, len);
+ if (!s) {
+ verbose_error(verbose, -EINVAL, NULL);
+ return NULL;
+ }
+
+ ret = source->SetOffset(0);
+ if (ret < 0) {
+ verbose_error(verbose, ret, "seeking");
+ sparse_file_destroy(s);
+ return NULL;
+ }
+
+ s->verbose = verbose;
+
+ ret = sparse_file_read_sparse(s, source, crc);
+ if (ret < 0) {
+ sparse_file_destroy(s);
+ return NULL;
+ }
+
+ return s;
+}
+
+struct sparse_file* sparse_file_import(int fd, bool verbose, bool crc) {
+ SparseFileFdSource source(fd);
+ return sparse_file_import_source(&source, verbose, crc);
+}
+
+struct sparse_file* sparse_file_import_buf(char* buf, bool verbose, bool crc) {
+ SparseFileBufSource source(buf);
+ return sparse_file_import_source(&source, verbose, crc);
+}
+
+struct sparse_file* sparse_file_import_auto(int fd, bool crc, bool verbose) {
+ struct sparse_file* s;
+ int64_t len;
+ int ret;
+
+ s = sparse_file_import(fd, verbose, crc);
+ if (s) {
+ return s;
+ }
+
+ len = lseek64(fd, 0, SEEK_END);
+ if (len < 0) {
+ return NULL;
+ }
+
+ lseek64(fd, 0, SEEK_SET);
+
+ s = sparse_file_new(4096, len);
+ if (!s) {
+ return NULL;
+ }
+
+ ret = sparse_file_read_normal(s, fd);
+ if (ret < 0) {
+ sparse_file_destroy(s);
+ return NULL;
+ }
+
+ return s;
}
diff --git a/libsync/Android.bp b/libsync/Android.bp
index 3fae5e6..c95563d 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -40,13 +40,6 @@
}
cc_test {
- name: "sync_test",
- defaults: ["libsync_defaults"],
- gtest: false,
- srcs: ["sync_test.c"],
-}
-
-cc_test {
name: "sync-unit-tests",
shared_libs: ["libsync"],
srcs: ["tests/sync_test.cpp"],
diff --git a/libsync/OWNERS b/libsync/OWNERS
new file mode 100644
index 0000000..dc61733
--- /dev/null
+++ b/libsync/OWNERS
@@ -0,0 +1,3 @@
+ghackmann@google.com
+jessehall@google.com
+marissaw@google.com
diff --git a/libsync/include/android/sync.h b/libsync/include/android/sync.h
index 68f74a0..32bb878 100644
--- a/libsync/include/android/sync.h
+++ b/libsync/include/android/sync.h
@@ -41,28 +41,8 @@
__BEGIN_DECLS
-struct sync_fence_info_data {
- uint32_t len;
- char name[32];
- int32_t status;
- uint8_t pt_info[0];
-};
-
-struct sync_pt_info {
- uint32_t len;
- char obj_name[32];
- char driver_name[32];
- int32_t status;
- uint64_t timestamp_ns;
- uint8_t driver_data[0];
-};
-
/* timeout in msecs */
int sync_wait(int fd, int timeout);
-struct sync_fence_info_data *sync_fence_info(int fd);
-struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
- struct sync_pt_info *itr);
-void sync_fence_info_free(struct sync_fence_info_data *info);
__END_DECLS
diff --git a/libsync/include/ndk/sync.h b/libsync/include/ndk/sync.h
index a786d3e..49f01e1 100644
--- a/libsync/include/ndk/sync.h
+++ b/libsync/include/ndk/sync.h
@@ -32,8 +32,6 @@
__BEGIN_DECLS
-#if __ANDROID_API__ >= __ANDROID_API_O__
-
/* Fences indicate the status of an asynchronous task. They are initially
* in unsignaled state (0), and make a one-time transition to either signaled
* (1) or error (< 0) state. A sync file is a collection of one or more fences;
@@ -63,14 +61,14 @@
* The original fences remain valid, and the caller is responsible for closing
* them.
*/
-int32_t sync_merge(const char *name, int32_t fd1, int32_t fd2);
+int32_t sync_merge(const char* name, int32_t fd1, int32_t fd2) __INTRODUCED_IN(26);
/**
* Retrieve detailed information about a sync file and its fences.
*
* The returned sync_file_info must be freed by calling sync_file_info_free().
*/
-struct sync_file_info *sync_file_info(int32_t fd);
+struct sync_file_info* sync_file_info(int32_t fd) __INTRODUCED_IN(26);
/**
* Get the array of fence infos from the sync file's info.
@@ -88,9 +86,7 @@
}
/** Free a struct sync_file_info structure */
-void sync_file_info_free(struct sync_file_info *info);
-
-#endif // __ANDROID_API__ >= __ANDROID_API_O__
+void sync_file_info_free(struct sync_file_info* info) __INTRODUCED_IN(26);
__END_DECLS
diff --git a/libsync/sync.c b/libsync/sync.c
index 6b187fa..b8c48c7 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -30,6 +30,29 @@
#include <android/sync.h>
+/* Prototypes for deprecated functions that used to be declared in the legacy
+ * android/sync.h. They've been moved here to make sure new code does not use
+ * them, but the functions are still defined to avoid breaking existing
+ * binaries. Eventually they can be removed altogether.
+ */
+struct sync_fence_info_data {
+ uint32_t len;
+ char name[32];
+ int32_t status;
+ uint8_t pt_info[0];
+};
+struct sync_pt_info {
+ uint32_t len;
+ char obj_name[32];
+ char driver_name[32];
+ int32_t status;
+ uint64_t timestamp_ns;
+ uint8_t driver_data[0];
+};
+struct sync_fence_info_data* sync_fence_info(int fd);
+struct sync_pt_info* sync_pt_info(struct sync_fence_info_data* info, struct sync_pt_info* itr);
+void sync_fence_info_free(struct sync_fence_info_data* info);
+
/* Legacy Sync API */
struct sync_legacy_merge_data {
diff --git a/libsync/sync_test.c b/libsync/sync_test.c
deleted file mode 100644
index f1ffdcf..0000000
--- a/libsync/sync_test.c
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * sync_test.c
- *
- * Copyright 2012 Google, Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <android/sync.h>
-#include "sw_sync.h"
-
-pthread_mutex_t printf_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-struct sync_thread_data {
- int thread_no;
- int fd[2];
-};
-
-void *sync_thread(void *data)
-{
- struct sync_thread_data *sync_data = data;
- struct sync_fence_info_data *info;
- int err;
- int i;
-
- for (i = 0; i < 2; i++) {
- err = sync_wait(sync_data->fd[i], 10000);
-
- pthread_mutex_lock(&printf_mutex);
- if (err < 0) {
- printf("thread %d wait %d failed: %s\n", sync_data->thread_no,
- i, strerror(errno));
- } else {
- printf("thread %d wait %d done\n", sync_data->thread_no, i);
- }
- info = sync_fence_info(sync_data->fd[i]);
- if (info) {
- struct sync_pt_info *pt_info = NULL;
- printf(" fence %s %d\n", info->name, info->status);
-
- while ((pt_info = sync_pt_info(info, pt_info))) {
- int ts_sec = pt_info->timestamp_ns / 1000000000LL;
- int ts_usec = (pt_info->timestamp_ns % 1000000000LL) / 1000LL;
- printf(" pt %s %s %d %d.%06d", pt_info->obj_name,
- pt_info->driver_name, pt_info->status,
- ts_sec, ts_usec);
- if (!strcmp(pt_info->driver_name, "sw_sync"))
- printf(" val=%d\n", *(uint32_t *)pt_info->driver_data);
- else
- printf("\n");
- }
- sync_fence_info_free(info);
- }
- pthread_mutex_unlock(&printf_mutex);
- }
-
- return NULL;
-}
-
-int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
-{
- struct sync_thread_data sync_data[4];
- pthread_t threads[4];
- int sync_timeline_fd;
- int i, j;
- char str[256];
-
- sync_timeline_fd = sw_sync_timeline_create();
- if (sync_timeline_fd < 0) {
- perror("can't create sw_sync_timeline:");
- return 1;
- }
-
- for (i = 0; i < 3; i++) {
- sync_data[i].thread_no = i;
-
- for (j = 0; j < 2; j++) {
- unsigned val = i + j * 3 + 1;
- snprintf(str, sizeof(str), "test_fence%d-%d", i, j);
- int fd = sw_sync_fence_create(sync_timeline_fd, str, val);
- if (fd < 0) {
- printf("can't create sync pt %d: %s", val, strerror(errno));
- return 1;
- }
- sync_data[i].fd[j] = fd;
- printf("sync_data[%d].fd[%d] = %d;\n", i, j, fd);
-
- }
- }
-
- sync_data[3].thread_no = 3;
- for (j = 0; j < 2; j++) {
- snprintf(str, sizeof(str), "merged_fence%d", j);
- sync_data[3].fd[j] = sync_merge(str, sync_data[0].fd[j], sync_data[1].fd[j]);
- if (sync_data[3].fd[j] < 0) {
- printf("can't merge sync pts %d and %d: %s\n",
- sync_data[0].fd[j], sync_data[1].fd[j], strerror(errno));
- return 1;
- }
- }
-
- for (i = 0; i < 4; i++)
- pthread_create(&threads[i], NULL, sync_thread, &sync_data[i]);
-
-
- for (i = 0; i < 3; i++) {
- int err;
- printf("press enter to inc to %d\n", i+1);
- fgets(str, sizeof(str), stdin);
- err = sw_sync_timeline_inc(sync_timeline_fd, 1);
- if (err < 0) {
- perror("can't increment sync obj:");
- return 1;
- }
- }
-
- printf("press enter to close sync_timeline\n");
- fgets(str, sizeof(str), stdin);
-
- close(sync_timeline_fd);
-
- printf("press enter to end test\n");
- fgets(str, sizeof(str), stdin);
-
- for (i = 0; i < 3; i++) {
- void *val;
- pthread_join(threads[i], &val);
- }
-
- return 0;
-}
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index 0fb86d6..011b09d 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -15,6 +15,35 @@
#include <random>
#include <unordered_map>
+/* These deprecated declarations were in the legacy android/sync.h. They've been removed to
+ * encourage code to move to the modern equivalents. But they are still implemented in libsync.so
+ * to avoid breaking existing binaries; as long as that's true we should keep testing them here.
+ * That means making local copies of the declarations.
+ */
+extern "C" {
+
+struct sync_fence_info_data {
+ uint32_t len;
+ char name[32];
+ int32_t status;
+ uint8_t pt_info[0];
+};
+
+struct sync_pt_info {
+ uint32_t len;
+ char obj_name[32];
+ char driver_name[32];
+ int32_t status;
+ uint64_t timestamp_ns;
+ uint8_t driver_data[0];
+};
+
+struct sync_fence_info_data* sync_fence_info(int fd);
+struct sync_pt_info* sync_pt_info(struct sync_fence_info_data* info, struct sync_pt_info* itr);
+void sync_fence_info_free(struct sync_fence_info_data* info);
+
+} // extern "C"
+
// TODO: better stress tests?
// Handle more than 64 fd's simultaneously, i.e. fix sync_fence_info's 4k limit.
// Handle wraparound in timelines like nvidia.
diff --git a/libsysutils/include/sysutils/FrameworkClient.h b/libsysutils/include/sysutils/FrameworkClient.h
deleted file mode 100644
index 4a3f0de..0000000
--- a/libsysutils/include/sysutils/FrameworkClient.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef _FRAMEWORK_CLIENT_H
-#define _FRAMEWORK_CLIENT_H
-
-#include "List.h"
-
-#include <pthread.h>
-
-class FrameworkClient {
- int mSocket;
- pthread_mutex_t mWriteMutex;
-
-public:
- FrameworkClient(int sock);
- virtual ~FrameworkClient() {}
-
- int sendMsg(const char *msg);
- int sendMsg(const char *msg, const char *data);
-};
-
-typedef android::sysutils::List<FrameworkClient *> FrameworkClientCollection;
-#endif
diff --git a/libsysutils/include/sysutils/OWNERS b/libsysutils/include/sysutils/OWNERS
new file mode 100644
index 0000000..b78918e
--- /dev/null
+++ b/libsysutils/include/sysutils/OWNERS
@@ -0,0 +1,2 @@
+per-file Netlink* = ek@google.com
+per-file Netlink* = lorenzo@google.com
diff --git a/libsysutils/include/sysutils/SocketClient.h b/libsysutils/include/sysutils/SocketClient.h
index c657526..1004f06 100644
--- a/libsysutils/include/sysutils/SocketClient.h
+++ b/libsysutils/include/sysutils/SocketClient.h
@@ -1,6 +1,8 @@
#ifndef _SOCKET_CLIENT_H
#define _SOCKET_CLIENT_H
+#include "List.h"
+
#include <pthread.h>
#include <cutils/atomic.h>
#include <sys/types.h>
@@ -33,7 +35,7 @@
SocketClient(int sock, bool owned, bool useCmdNum);
virtual ~SocketClient();
- int getSocket() const { return mSocket; }
+ int getSocket() { return mSocket; }
pid_t getPid() const { return mPid; }
uid_t getUid() const { return mUid; }
gid_t getGid() const { return mGid; }
@@ -82,4 +84,5 @@
int sendDataLockedv(struct iovec *iov, int iovcnt);
};
+typedef android::sysutils::List<SocketClient *> SocketClientCollection;
#endif
diff --git a/libsysutils/include/sysutils/SocketListener.h b/libsysutils/include/sysutils/SocketListener.h
index 56f2478..bc93b86 100644
--- a/libsysutils/include/sysutils/SocketListener.h
+++ b/libsysutils/include/sysutils/SocketListener.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2008-2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,6 @@
#include <pthread.h>
-#include <unordered_map>
-
#include <sysutils/SocketClient.h>
#include "SocketClientCommand.h"
@@ -27,7 +25,7 @@
bool mListen;
const char *mSocketName;
int mSock;
- std::unordered_map<int, SocketClient*> mClients;
+ SocketClientCollection *mClients;
pthread_mutex_t mClientsLock;
int mCtrlPipe[2];
pthread_t mThread;
diff --git a/libsysutils/src/FrameworkClient.cpp b/libsysutils/src/FrameworkClient.cpp
deleted file mode 100644
index 72b3d0a..0000000
--- a/libsysutils/src/FrameworkClient.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2009-2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "FrameworkClient"
-
-#include <alloca.h>
-#include <errno.h>
-#include <pthread.h>
-#include <sys/types.h>
-
-#include <android/log.h>
-#include <sysutils/FrameworkClient.h>
-
-FrameworkClient::FrameworkClient(int socket) {
- mSocket = socket;
- pthread_mutex_init(&mWriteMutex, NULL);
-}
-
-int FrameworkClient::sendMsg(const char *msg) {
- int ret;
- if (mSocket < 0) {
- errno = EHOSTUNREACH;
- return -1;
- }
-
- pthread_mutex_lock(&mWriteMutex);
- ret = TEMP_FAILURE_RETRY(write(mSocket, msg, strlen(msg) +1));
- if (ret < 0) {
- SLOGW("Unable to send msg '%s' (%s)", msg, strerror(errno));
- }
- pthread_mutex_unlock(&mWriteMutex);
- return 0;
-}
-
-int FrameworkClient::sendMsg(const char *msg, const char *data) {
- size_t bufflen = strlen(msg) + strlen(data) + 1;
- char *buffer = (char *) alloca(bufflen);
- if (!buffer) {
- errno = -ENOMEM;
- return -1;
- }
- snprintf(buffer, bufflen, "%s%s", msg, data);
- return sendMsg(buffer);
-}
-
diff --git a/libsysutils/src/OWNERS b/libsysutils/src/OWNERS
new file mode 100644
index 0000000..b78918e
--- /dev/null
+++ b/libsysutils/src/OWNERS
@@ -0,0 +1,2 @@
+per-file Netlink* = ek@google.com
+per-file Netlink* = lorenzo@google.com
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
index 128a27a..3f8f3db 100644
--- a/libsysutils/src/SocketListener.cpp
+++ b/libsysutils/src/SocketListener.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2008-2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,15 +19,13 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
-#include <sys/poll.h>
+#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
-#include <vector>
-
#include <cutils/sockets.h>
#include <log/log.h>
#include <sysutils/SocketListener.h>
@@ -54,6 +52,7 @@
mSock = socketFd;
mUseCmdNum = useCmdNum;
pthread_mutex_init(&mClientsLock, NULL);
+ mClients = new SocketClientCollection();
}
SocketListener::~SocketListener() {
@@ -64,9 +63,12 @@
close(mCtrlPipe[0]);
close(mCtrlPipe[1]);
}
- for (auto pair : mClients) {
- pair.second->decRef();
+ SocketClientCollection::iterator it;
+ for (it = mClients->begin(); it != mClients->end();) {
+ (*it)->decRef();
+ it = mClients->erase(it);
}
+ delete mClients;
}
int SocketListener::startListener() {
@@ -93,7 +95,7 @@
SLOGE("Unable to listen on socket (%s)", strerror(errno));
return -1;
} else if (!mListen)
- mClients[mSock] = new SocketClient(mSock, false, mUseCmdNum);
+ mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));
if (pipe(mCtrlPipe)) {
SLOGE("pipe failed (%s)", strerror(errno));
@@ -133,10 +135,11 @@
mSock = -1;
}
- for (auto pair : mClients) {
- delete pair.second;
+ SocketClientCollection::iterator it;
+ for (it = mClients->begin(); it != mClients->end();) {
+ delete (*it);
+ it = mClients->erase(it);
}
- mClients.clear();
return 0;
}
@@ -149,30 +152,47 @@
}
void SocketListener::runListener() {
- while (true) {
- std::vector<pollfd> fds;
+
+ SocketClientCollection pendingList;
+
+ while(1) {
+ SocketClientCollection::iterator it;
+ fd_set read_fds;
+ int rc = 0;
+ int max = -1;
+
+ FD_ZERO(&read_fds);
+
+ if (mListen) {
+ max = mSock;
+ FD_SET(mSock, &read_fds);
+ }
+
+ FD_SET(mCtrlPipe[0], &read_fds);
+ if (mCtrlPipe[0] > max)
+ max = mCtrlPipe[0];
pthread_mutex_lock(&mClientsLock);
- fds.reserve(2 + mClients.size());
- fds.push_back({.fd = mCtrlPipe[0], .events = POLLIN});
- if (mListen) fds.push_back({.fd = mSock, .events = POLLIN});
- for (auto pair : mClients) {
+ for (it = mClients->begin(); it != mClients->end(); ++it) {
// NB: calling out to an other object with mClientsLock held (safe)
- const int fd = pair.second->getSocket();
- if (fd != pair.first) SLOGE("fd mismatch: %d != %d", fd, pair.first);
- fds.push_back({.fd = fd, .events = POLLIN});
+ int fd = (*it)->getSocket();
+ FD_SET(fd, &read_fds);
+ if (fd > max) {
+ max = fd;
+ }
}
pthread_mutex_unlock(&mClientsLock);
-
- SLOGV("mListen=%d, mSocketName=%s", mListen, mSocketName);
- int rc = TEMP_FAILURE_RETRY(poll(fds.data(), fds.size(), -1));
- if (rc < 0) {
- SLOGE("poll failed (%s) mListen=%d", strerror(errno), mListen);
+ SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);
+ if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
+ if (errno == EINTR)
+ continue;
+ SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);
sleep(1);
continue;
- }
+ } else if (!rc)
+ continue;
- if (fds[0].revents & (POLLIN | POLLERR)) {
+ if (FD_ISSET(mCtrlPipe[0], &read_fds)) {
char c = CtrlPipe_Shutdown;
TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));
if (c == CtrlPipe_Shutdown) {
@@ -180,7 +200,7 @@
}
continue;
}
- if (mListen && (fds[1].revents & (POLLIN | POLLERR))) {
+ if (mListen && FD_ISSET(mSock, &read_fds)) {
int c = TEMP_FAILURE_RETRY(accept4(mSock, nullptr, nullptr, SOCK_CLOEXEC));
if (c < 0) {
SLOGE("accept failed (%s)", strerror(errno));
@@ -188,33 +208,32 @@
continue;
}
pthread_mutex_lock(&mClientsLock);
- mClients[c] = new SocketClient(c, true, mUseCmdNum);
+ mClients->push_back(new SocketClient(c, true, mUseCmdNum));
pthread_mutex_unlock(&mClientsLock);
}
- // Add all active clients to the pending list first, so we can release
- // the lock before invoking the callbacks.
- std::vector<SocketClient*> pending;
+ /* Add all active clients to the pending list first */
+ pendingList.clear();
pthread_mutex_lock(&mClientsLock);
- const int size = fds.size();
- for (int i = mListen ? 2 : 1; i < size; ++i) {
- const struct pollfd& p = fds[i];
- if (p.events & (POLLIN | POLLERR)) {
- auto it = mClients.find(p.fd);
- if (it == mClients.end()) {
- SLOGE("fd vanished: %d", p.fd);
- continue;
- }
- SocketClient* c = it->second;
- pending.push_back(c);
+ for (it = mClients->begin(); it != mClients->end(); ++it) {
+ SocketClient* c = *it;
+ // NB: calling out to an other object with mClientsLock held (safe)
+ int fd = c->getSocket();
+ if (FD_ISSET(fd, &read_fds)) {
+ pendingList.push_back(c);
c->incRef();
}
}
pthread_mutex_unlock(&mClientsLock);
- for (SocketClient* c : pending) {
- // Process it, if false is returned, remove from the map
- SLOGV("processing fd %d", c->getSocket());
+ /* Process the pending list, since it is owned by the thread,
+ * there is no need to lock it */
+ while (!pendingList.empty()) {
+ /* Pop the first item from the list */
+ it = pendingList.begin();
+ SocketClient* c = *it;
+ pendingList.erase(it);
+ /* Process it, if false is returned, remove from list */
if (!onDataAvailable(c)) {
release(c, false);
}
@@ -227,10 +246,17 @@
bool ret = false;
/* if our sockets are connection-based, remove and destroy it */
if (mListen && c) {
- /* Remove the client from our map */
+ /* Remove the client from our array */
SLOGV("going to zap %d for %s", c->getSocket(), mSocketName);
pthread_mutex_lock(&mClientsLock);
- ret = (mClients.erase(c->getSocket()) != 0);
+ SocketClientCollection::iterator it;
+ for (it = mClients->begin(); it != mClients->end(); ++it) {
+ if (*it == c) {
+ mClients->erase(it);
+ ret = true;
+ break;
+ }
+ }
pthread_mutex_unlock(&mClientsLock);
if (ret) {
ret = c->decRef();
@@ -244,19 +270,25 @@
}
void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
- // Add all clients to a separate list first, so we don't have to hold
- // the lock while processing it.
- std::vector<SocketClient*> clients;
+ SocketClientCollection safeList;
+
+ /* Add all active clients to the safe list first */
+ safeList.clear();
pthread_mutex_lock(&mClientsLock);
- clients.reserve(mClients.size());
- for (auto pair : mClients) {
- SocketClient* c = pair.second;
+ SocketClientCollection::iterator i;
+
+ for (i = mClients->begin(); i != mClients->end(); ++i) {
+ SocketClient* c = *i;
c->incRef();
- clients.push_back(c);
+ safeList.push_back(c);
}
pthread_mutex_unlock(&mClientsLock);
- for (SocketClient* c : clients) {
+ while (!safeList.empty()) {
+ /* Pop the first item from the list */
+ i = safeList.begin();
+ SocketClient* c = *i;
+ safeList.erase(i);
// broadcasts are unsolicited and should not include a cmd number
if (c->sendMsg(code, msg, addErrno, false)) {
SLOGW("Error sending broadcast (%s)", strerror(errno));
@@ -266,19 +298,25 @@
}
void SocketListener::runOnEachSocket(SocketClientCommand *command) {
- // Add all clients to a separate list first, so we don't have to hold
- // the lock while processing it.
- std::vector<SocketClient*> clients;
+ SocketClientCollection safeList;
+
+ /* Add all active clients to the safe list first */
+ safeList.clear();
pthread_mutex_lock(&mClientsLock);
- clients.reserve(mClients.size());
- for (auto pair : mClients) {
- SocketClient* c = pair.second;
+ SocketClientCollection::iterator i;
+
+ for (i = mClients->begin(); i != mClients->end(); ++i) {
+ SocketClient* c = *i;
c->incRef();
- clients.push_back(c);
+ safeList.push_back(c);
}
pthread_mutex_unlock(&mClientsLock);
- for (SocketClient* c : clients) {
+ while (!safeList.empty()) {
+ /* Pop the first item from the list */
+ i = safeList.begin();
+ SocketClient* c = *i;
+ safeList.erase(i);
command->runSocketCommand(c);
c->decRef();
}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 26be64d..df5da65 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -223,6 +223,7 @@
"tests/files/offline/art_quick_osr_stub_arm/*",
"tests/files/offline/bad_eh_frame_hdr_arm64/*",
"tests/files/offline/debug_frame_first_x86/*",
+ "tests/files/offline/debug_frame_load_bias_arm/*",
"tests/files/offline/eh_frame_hdr_begin_x86_64/*",
"tests/files/offline/jit_debug_arm/*",
"tests/files/offline/jit_debug_x86/*",
@@ -232,6 +233,9 @@
"tests/files/offline/straddle_arm/*",
"tests/files/offline/straddle_arm64/*",
],
+ required: [
+ "libunwindstack_local",
+ ],
}
//-------------------------------------------------------------------------
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
index 6e397e3..818f5d1 100644
--- a/libunwindstack/ArmExidx.cpp
+++ b/libunwindstack/ArmExidx.cpp
@@ -31,6 +31,8 @@
namespace unwindstack {
+static constexpr uint8_t LOG_CFA_REG = 64;
+
void ArmExidx::LogRawData() {
std::string log_str("Raw Data:");
for (const uint8_t data : data_) {
@@ -63,8 +65,10 @@
if (data == 1) {
// This is a CANT UNWIND entry.
status_ = ARM_STATUS_NO_UNWIND;
- if (log_) {
- log(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
+ }
log(log_indent_, "[cantunwind]");
}
return false;
@@ -86,7 +90,7 @@
// If this didn't end with a finish op, add one.
data_.push_back(ARM_OP_FINISH);
}
- if (log_) {
+ if (log_type_ == ARM_LOG_FULL) {
LogRawData();
}
return true;
@@ -163,7 +167,7 @@
data_.push_back(ARM_OP_FINISH);
}
- if (log_) {
+ if (log_type_ == ARM_LOG_FULL) {
LogRawData();
}
return true;
@@ -190,32 +194,45 @@
registers |= byte;
if (registers == 0) {
// 10000000 00000000: Refuse to unwind
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "Refuse to unwind");
}
status_ = ARM_STATUS_NO_UNWIND;
return false;
}
// 1000iiii iiiiiiii: Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}
- if (log_) {
- bool add_comma = false;
- std::string msg = "pop {";
- for (size_t i = 0; i < 12; i++) {
- if (registers & (1 << i)) {
- if (add_comma) {
- msg += ", ";
+ registers <<= 4;
+
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ bool add_comma = false;
+ std::string msg = "pop {";
+ for (size_t reg = 4; reg < 16; reg++) {
+ if (registers & (1 << reg)) {
+ if (add_comma) {
+ msg += ", ";
+ }
+ msg += android::base::StringPrintf("r%zu", reg);
+ add_comma = true;
}
- msg += android::base::StringPrintf("r%zu", i + 4);
- add_comma = true;
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ uint32_t cfa_offset = __builtin_popcount(registers) * 4;
+ log_cfa_offset_ += cfa_offset;
+ for (size_t reg = 4; reg < 16; reg++) {
+ if (registers & (1 << reg)) {
+ log_regs_[reg] = cfa_offset;
+ cfa_offset -= 4;
+ }
}
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
}
- registers <<= 4;
for (size_t reg = 4; reg < 16; reg++) {
if (registers & (1 << reg)) {
if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
@@ -246,15 +263,20 @@
if (bits == 13 || bits == 15) {
// 10011101: Reserved as prefix for ARM register to register moves
// 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "[Reserved]");
}
status_ = ARM_STATUS_RESERVED;
return false;
}
// 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
- if (log_) {
- log(log_indent_, "vsp = r%d", bits);
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "vsp = r%d", bits);
+ } else {
+ log_regs_[LOG_CFA_REG] = bits;
+ }
+
if (log_skip_execution_) {
return true;
}
@@ -270,17 +292,36 @@
// 10100nnn: Pop r4-r[4+nnn]
// 10101nnn: Pop r4-r[4+nnn], r14
- if (log_) {
- std::string msg = "pop {r4";
+ if (log_type_ != ARM_LOG_NONE) {
uint8_t end_reg = byte & 0x7;
- if (end_reg) {
- msg += android::base::StringPrintf("-r%d", 4 + end_reg);
- }
- if (byte & 0x8) {
- log(log_indent_, "%s, r14}", msg.c_str());
+ if (log_type_ == ARM_LOG_FULL) {
+ std::string msg = "pop {r4";
+ if (end_reg) {
+ msg += android::base::StringPrintf("-r%d", 4 + end_reg);
+ }
+ if (byte & 0x8) {
+ log(log_indent_, "%s, r14}", msg.c_str());
+ } else {
+ log(log_indent_, "%s}", msg.c_str());
+ }
} else {
- log(log_indent_, "%s}", msg.c_str());
+ end_reg += 4;
+ uint32_t cfa_offset = (end_reg - 3) * 4;
+ if (byte & 0x8) {
+ cfa_offset += 4;
+ }
+ log_cfa_offset_ += cfa_offset;
+
+ for (uint8_t reg = 4; reg <= end_reg; reg++) {
+ log_regs_[reg] = cfa_offset;
+ cfa_offset -= 4;
+ }
+
+ if (byte & 0x8) {
+ log_regs_[14] = cfa_offset;
+ }
}
+
if (log_skip_execution_) {
return true;
}
@@ -307,8 +348,11 @@
inline bool ArmExidx::DecodePrefix_10_11_0000() {
// 10110000: Finish
- if (log_) {
- log(log_indent_, "finish");
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "finish");
+ }
+
if (log_skip_execution_) {
status_ = ARM_STATUS_FINISH;
return false;
@@ -326,7 +370,7 @@
if (byte == 0) {
// 10110001 00000000: Spare
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
@@ -334,7 +378,7 @@
}
if (byte >> 4) {
// 10110001 xxxxyyyy: Spare (xxxx != 0000)
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
@@ -342,19 +386,32 @@
}
// 10110001 0000iiii: Pop integer registers under mask {r3, r2, r1, r0}
- if (log_) {
- bool add_comma = false;
- std::string msg = "pop {";
- for (size_t i = 0; i < 4; i++) {
- if (byte & (1 << i)) {
- if (add_comma) {
- msg += ", ";
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ bool add_comma = false;
+ std::string msg = "pop {";
+ for (size_t i = 0; i < 4; i++) {
+ if (byte & (1 << i)) {
+ if (add_comma) {
+ msg += ", ";
+ }
+ msg += android::base::StringPrintf("r%zu", i);
+ add_comma = true;
}
- msg += android::base::StringPrintf("r%zu", i);
- add_comma = true;
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ byte &= 0xf;
+ uint32_t cfa_offset = __builtin_popcount(byte) * 4;
+ log_cfa_offset_ += cfa_offset;
+ for (size_t reg = 0; reg < 4; reg++) {
+ if (byte & (1 << reg)) {
+ log_regs_[reg] = cfa_offset;
+ cfa_offset -= 4;
+ }
}
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
@@ -373,6 +430,15 @@
return true;
}
+inline void ArmExidx::AdjustRegisters(int32_t offset) {
+ for (auto& entry : log_regs_) {
+ if (entry.first >= LOG_CFA_REG) {
+ break;
+ }
+ entry.second += offset;
+ }
+}
+
inline bool ArmExidx::DecodePrefix_10_11_0010() {
// 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
uint32_t result = 0;
@@ -387,8 +453,15 @@
shift += 7;
} while (byte & 0x80);
result <<= 2;
- if (log_) {
- log(log_indent_, "vsp = vsp + %d", 0x204 + result);
+ if (log_type_ != ARM_LOG_NONE) {
+ int32_t cfa_offset = 0x204 + result;
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "vsp = vsp + %d", cfa_offset);
+ } else {
+ log_cfa_offset_ += cfa_offset;
+ }
+ AdjustRegisters(cfa_offset);
+
if (log_skip_execution_) {
return true;
}
@@ -404,14 +477,20 @@
return false;
}
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
uint8_t start_reg = byte >> 4;
- std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
uint8_t end_reg = start_reg + (byte & 0xf);
- if (end_reg) {
- msg += android::base::StringPrintf("-d%d", end_reg);
+
+ if (log_type_ == ARM_LOG_FULL) {
+ std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
+ if (end_reg) {
+ msg += android::base::StringPrintf("-d%d", end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported DX register display");
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
@@ -422,7 +501,7 @@
inline bool ArmExidx::DecodePrefix_10_11_01nn() {
// 101101nn: Spare
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
@@ -433,13 +512,18 @@
CHECK((byte & ~0x07) == 0xb8);
// 10111nnn: Pop VFP double-precision registers D[8]-D[8+nnn] by FSTMFDX
- if (log_) {
- std::string msg = "pop {d8";
- uint8_t last_reg = (byte & 0x7);
- if (last_reg) {
- msg += android::base::StringPrintf("-d%d", last_reg + 8);
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ uint8_t last_reg = (byte & 0x7);
+ std::string msg = "pop {d8";
+ if (last_reg) {
+ msg += android::base::StringPrintf("-d%d", last_reg + 8);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported DX register display");
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
@@ -489,14 +573,19 @@
}
// 11000110 sssscccc: Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]
- if (log_) {
- uint8_t start_reg = byte >> 4;
- std::string msg = android::base::StringPrintf("pop {wR%d", start_reg);
- uint8_t end_reg = byte & 0xf;
- if (end_reg) {
- msg += android::base::StringPrintf("-wR%d", start_reg + end_reg);
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ uint8_t start_reg = byte >> 4;
+ std::string msg = android::base::StringPrintf("pop {wR%d", start_reg);
+ uint8_t end_reg = byte & 0xf;
+ if (end_reg) {
+ msg += android::base::StringPrintf("-wR%d", start_reg + end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported wRX register display");
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
@@ -510,32 +599,40 @@
if (byte == 0) {
// 11000111 00000000: Spare
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
return false;
} else if ((byte >> 4) == 0) {
// 11000111 0000iiii: Intel Wireless MMX pop wCGR registers {wCGR0,1,2,3}
- if (log_) {
- bool add_comma = false;
- std::string msg = "pop {";
- for (size_t i = 0; i < 4; i++) {
- if (byte & (1 << i)) {
- if (add_comma) {
- msg += ", ";
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ bool add_comma = false;
+ std::string msg = "pop {";
+ for (size_t i = 0; i < 4; i++) {
+ if (byte & (1 << i)) {
+ if (add_comma) {
+ msg += ", ";
+ }
+ msg += android::base::StringPrintf("wCGR%zu", i);
+ add_comma = true;
}
- msg += android::base::StringPrintf("wCGR%zu", i);
- add_comma = true;
}
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported wCGR register display");
}
- log(log_indent_, "%s}", msg.c_str());
+
+ if (log_skip_execution_) {
+ return true;
+ }
}
// Only update the cfa.
cfa_ += __builtin_popcount(byte) * 4;
} else {
// 11000111 xxxxyyyy: Spare (xxxx != 0000)
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
@@ -543,13 +640,18 @@
}
} else {
// 11000nnn: Intel Wireless MMX pop wR[10]-wR[10+nnn] (nnn != 6, 7)
- if (log_) {
- std::string msg = "pop {wR10";
- uint8_t nnn = byte & 0x7;
- if (nnn) {
- msg += android::base::StringPrintf("-wR%d", 10 + nnn);
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ std::string msg = "pop {wR10";
+ uint8_t nnn = byte & 0x7;
+ if (nnn) {
+ msg += android::base::StringPrintf("-wR%d", 10 + nnn);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported wRX register display");
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
@@ -570,14 +672,19 @@
return false;
}
- if (log_) {
- uint8_t start_reg = byte >> 4;
- std::string msg = android::base::StringPrintf("pop {d%d", 16 + start_reg);
- uint8_t end_reg = byte & 0xf;
- if (end_reg) {
- msg += android::base::StringPrintf("-d%d", 16 + start_reg + end_reg);
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ uint8_t start_reg = byte >> 4;
+ std::string msg = android::base::StringPrintf("pop {d%d", 16 + start_reg);
+ uint8_t end_reg = byte & 0xf;
+ if (end_reg) {
+ msg += android::base::StringPrintf("-d%d", 16 + start_reg + end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported DX register display");
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
@@ -590,14 +697,19 @@
return false;
}
- if (log_) {
- uint8_t start_reg = byte >> 4;
- std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
- uint8_t end_reg = byte & 0xf;
- if (end_reg) {
- msg += android::base::StringPrintf("-d%d", start_reg + end_reg);
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ uint8_t start_reg = byte >> 4;
+ std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
+ uint8_t end_reg = byte & 0xf;
+ if (end_reg) {
+ msg += android::base::StringPrintf("-d%d", start_reg + end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported DX register display");
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
@@ -606,7 +718,7 @@
cfa_ += (byte & 0xf) * 8 + 8;
} else {
// 11001yyy: Spare (yyy != 000, 001)
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
@@ -619,13 +731,18 @@
CHECK((byte & ~0x07) == 0xd0);
// 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
- if (log_) {
- std::string msg = "pop {d8";
- uint8_t end_reg = byte & 0x7;
- if (end_reg) {
- msg += android::base::StringPrintf("-d%d", 8 + end_reg);
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ std::string msg = "pop {d8";
+ uint8_t end_reg = byte & 0x7;
+ if (end_reg) {
+ msg += android::base::StringPrintf("-d%d", 8 + end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported DX register display");
}
- log(log_indent_, "%s}", msg.c_str());
+
if (log_skip_execution_) {
return true;
}
@@ -646,7 +763,7 @@
return DecodePrefix_11_010(byte);
default:
// 11xxxyyy: Spare (xxx != 000, 001, 010)
- if (log_) {
+ if (log_type_ != ARM_LOG_NONE) {
log(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
@@ -664,8 +781,15 @@
switch (byte >> 6) {
case 0:
// 00xxxxxx: vsp = vsp + (xxxxxxx << 2) + 4
- if (log_) {
- log(log_indent_, "vsp = vsp + %d", ((byte & 0x3f) << 2) + 4);
+ if (log_type_ != ARM_LOG_NONE) {
+ int32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "vsp = vsp + %d", cfa_offset);
+ } else {
+ log_cfa_offset_ += cfa_offset;
+ }
+ AdjustRegisters(cfa_offset);
+
if (log_skip_execution_) {
break;
}
@@ -674,8 +798,15 @@
break;
case 1:
// 01xxxxxx: vsp = vsp - (xxxxxxx << 2) + 4
- if (log_) {
- log(log_indent_, "vsp = vsp - %d", ((byte & 0x3f) << 2) + 4);
+ if (log_type_ != ARM_LOG_NONE) {
+ uint32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "vsp = vsp - %d", cfa_offset);
+ } else {
+ log_cfa_offset_ -= cfa_offset;
+ }
+ AdjustRegisters(-cfa_offset);
+
if (log_skip_execution_) {
break;
}
@@ -696,4 +827,36 @@
return status_ == ARM_STATUS_FINISH;
}
+void ArmExidx::LogByReg() {
+ if (log_type_ != ARM_LOG_BY_REG) {
+ return;
+ }
+
+ uint8_t cfa_reg;
+ if (log_regs_.count(LOG_CFA_REG) == 0) {
+ cfa_reg = 13;
+ } else {
+ cfa_reg = log_regs_[LOG_CFA_REG];
+ }
+
+ if (log_cfa_offset_ != 0) {
+ char sign = (log_cfa_offset_ > 0) ? '+' : '-';
+ log(log_indent_, "cfa = r%zu %c %d", cfa_reg, sign, abs(log_cfa_offset_));
+ } else {
+ log(log_indent_, "cfa = r%zu", cfa_reg);
+ }
+
+ for (const auto& entry : log_regs_) {
+ if (entry.first >= LOG_CFA_REG) {
+ break;
+ }
+ if (entry.second == 0) {
+ log(log_indent_, "r%zu = [cfa]", entry.first);
+ } else {
+ char sign = (entry.second > 0) ? '-' : '+';
+ log(log_indent_, "r%zu = [cfa %c %d]", entry.first, sign, abs(entry.second));
+ }
+ }
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/ArmExidx.h b/libunwindstack/ArmExidx.h
index 96756a0..d9fc371 100644
--- a/libunwindstack/ArmExidx.h
+++ b/libunwindstack/ArmExidx.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <deque>
+#include <map>
namespace unwindstack {
@@ -44,6 +45,12 @@
ARM_OP_FINISH = 0xb0,
};
+enum ArmLogType : uint8_t {
+ ARM_LOG_NONE,
+ ARM_LOG_FULL,
+ ARM_LOG_BY_REG,
+};
+
class ArmExidx {
public:
ArmExidx(RegsArm* regs, Memory* elf_memory, Memory* process_memory)
@@ -52,6 +59,8 @@
void LogRawData();
+ void LogByReg();
+
bool ExtractEntryData(uint32_t entry_offset);
bool Eval();
@@ -71,12 +80,13 @@
bool pc_set() { return pc_set_; }
void set_pc_set(bool pc_set) { pc_set_ = pc_set; }
- void set_log(bool log) { log_ = log; }
+ void set_log(ArmLogType log_type) { log_type_ = log_type; }
void set_log_skip_execution(bool skip_execution) { log_skip_execution_ = skip_execution; }
void set_log_indent(uint8_t indent) { log_indent_ = indent; }
private:
bool GetByte(uint8_t* byte);
+ void AdjustRegisters(int32_t offset);
bool DecodePrefix_10_00(uint8_t byte);
bool DecodePrefix_10_01(uint8_t byte);
@@ -103,10 +113,12 @@
Memory* elf_memory_;
Memory* process_memory_;
- bool log_ = false;
+ ArmLogType log_type_ = ARM_LOG_NONE;
uint8_t log_indent_ = 0;
bool log_skip_execution_ = false;
bool pc_set_ = false;
+ int32_t log_cfa_offset_ = 0;
+ std::map<uint8_t, int32_t> log_regs_;
};
} // namespace unwindstack
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 6ecedce..cd9ef61 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -264,8 +264,8 @@
}
template <typename AddressType>
-bool DwarfCfa<AddressType>::Log(uint32_t indent, uint64_t pc, uint64_t load_bias,
- uint64_t start_offset, uint64_t end_offset) {
+bool DwarfCfa<AddressType>::Log(uint32_t indent, uint64_t pc, uint64_t start_offset,
+ uint64_t end_offset) {
memory_->set_cur_offset(start_offset);
uint64_t cfa_offset;
uint64_t cur_pc = fde_->pc_start;
@@ -301,8 +301,8 @@
break;
}
if (cur_pc != old_pc) {
- log(indent, "");
- log(indent, "PC 0x%" PRIx64, cur_pc + load_bias);
+ log(0, "");
+ log(indent, "PC 0x%" PRIx64, cur_pc);
}
old_pc = cur_pc;
}
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index 16c66e2..c5ffb8e 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -71,8 +71,7 @@
bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
dwarf_loc_regs_t* loc_regs);
- bool Log(uint32_t indent, uint64_t pc, uint64_t load_bias, uint64_t start_offset,
- uint64_t end_offset);
+ bool Log(uint32_t indent, uint64_t pc, uint64_t start_offset, uint64_t end_offset);
const DwarfErrorData& last_error() { return last_error_; }
DwarfErrorCode LastErrorCode() { return last_error_.code; }
diff --git a/libunwindstack/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
index 9a49013..fd6a457 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -22,12 +22,18 @@
#include "Check.h"
#include "DwarfEhFrameWithHdr.h"
+#include "DwarfEncoding.h"
namespace unwindstack {
+static inline bool IsEncodingRelative(uint8_t encoding) {
+ encoding >>= 4;
+ return encoding > 0 && encoding <= DW_EH_PE_funcrel;
+}
+
template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size) {
- uint8_t data[4];
+bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
+ load_bias_ = load_bias;
memory_.clear_func_offset();
memory_.clear_text_offset();
@@ -35,6 +41,7 @@
memory_.set_cur_offset(offset);
// Read the first four bytes all at once.
+ uint8_t data[4];
if (!memory_.ReadBytes(data, 4)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
@@ -100,7 +107,7 @@
memory_.set_data_offset(entries_data_offset_);
memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
- memory_.set_pc_offset(memory_.cur_offset());
+ memory_.set_pc_offset(0);
uint64_t value;
if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
@@ -109,6 +116,11 @@
fde_info_.erase(index);
return nullptr;
}
+
+ // Relative encodings require adding in the load bias.
+ if (IsEncodingRelative(table_encoding_)) {
+ value += load_bias_;
+ }
info->pc = value;
return info;
}
@@ -174,27 +186,27 @@
memory_.set_data_offset(entries_data_offset_);
memory_.set_cur_offset(cur_entries_offset_);
+ memory_.set_pc_offset(0);
cur_entries_offset_ = 0;
FdeInfo* prev_info = nullptr;
for (size_t current = fde_info_.size();
current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
- memory_.set_pc_offset(memory_.cur_offset());
- uint64_t value;
- if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value)) {
- last_error_.code = DWARF_ERROR_MEMORY_INVALID;
- last_error_.address = memory_.cur_offset();
- return false;
- }
-
FdeInfo* info = &fde_info_[current];
- if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+ uint64_t value;
+ if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
+ !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
fde_info_.erase(current);
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
return false;
}
- info->pc = value + 4;
+
+ // Relative encodings require adding in the load bias.
+ if (IsEncodingRelative(table_encoding_)) {
+ value += load_bias_;
+ }
+ info->pc = value;
if (pc < info->pc) {
if (prev_info == nullptr) {
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
index 3571166..d16dd10 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.h
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -38,6 +38,7 @@
using DwarfSectionImpl<AddressType>::entries_offset_;
using DwarfSectionImpl<AddressType>::entries_end_;
using DwarfSectionImpl<AddressType>::last_error_;
+ using DwarfSectionImpl<AddressType>::load_bias_;
struct FdeInfo {
AddressType pc;
@@ -47,7 +48,7 @@
DwarfEhFrameWithHdr(Memory* memory) : DwarfEhFrame<AddressType>(memory) {}
virtual ~DwarfEhFrameWithHdr() = default;
- bool Init(uint64_t offset, uint64_t size) override;
+ bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 65eec65..eb83949 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -420,6 +420,7 @@
last_error_.address = memory_.cur_offset();
return false;
}
+ memory_.set_pc_offset(pc_offset_);
if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
@@ -521,19 +522,19 @@
}
memory_.set_cur_offset(cur_offset);
- if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_start)) {
- last_error_.code = DWARF_ERROR_MEMORY_INVALID;
- last_error_.address = memory_.cur_offset();
- return false;
- }
+ // The load bias only applies to the start.
+ memory_.set_pc_offset(load_bias_);
+ bool valid = memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_start);
fde->pc_start = AdjustPcFromFde(fde->pc_start);
- if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_end)) {
+ memory_.set_pc_offset(0);
+ if (!valid || !memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_end)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
return false;
}
fde->pc_end += fde->pc_start;
+
if (cie->augmentation_string.size() > 0 && cie->augmentation_string[0] == 'z') {
// Augmentation Size
uint64_t aug_length;
@@ -544,6 +545,7 @@
}
uint64_t cur_offset = memory_.cur_offset();
+ memory_.set_pc_offset(pc_offset_);
if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
@@ -582,17 +584,16 @@
}
template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, uint64_t load_bias,
- const DwarfFde* fde) {
+bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) {
DwarfCfa<AddressType> cfa(&memory_, fde);
// Always print the cie information.
const DwarfCie* cie = fde->cie;
- if (!cfa.Log(indent, pc, load_bias, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
+ if (!cfa.Log(indent, pc, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
last_error_ = cfa.last_error();
return false;
}
- if (!cfa.Log(indent, pc, load_bias, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
+ if (!cfa.Log(indent, pc, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
last_error_ = cfa.last_error();
return false;
}
@@ -600,15 +601,16 @@
}
template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::Init(uint64_t offset, uint64_t size) {
+bool DwarfSectionImpl<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
+ load_bias_ = load_bias;
entries_offset_ = offset;
entries_end_ = offset + size;
memory_.clear_func_offset();
memory_.clear_text_offset();
- memory_.set_data_offset(offset);
memory_.set_cur_offset(offset);
- memory_.set_pc_offset(offset);
+ memory_.set_data_offset(offset);
+ pc_offset_ = offset;
return CreateSortedFdeList();
}
@@ -717,6 +719,7 @@
return false;
}
uint64_t value;
+ memory_.set_pc_offset(pc_offset_);
if (!memory_.template ReadEncodedValue<AddressType>(encoding, &value)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
@@ -737,15 +740,13 @@
}
uint64_t start;
- if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &start)) {
- last_error_.code = DWARF_ERROR_MEMORY_INVALID;
- last_error_.address = memory_.cur_offset();
- return false;
- }
+ memory_.set_pc_offset(load_bias_);
+ bool valid = memory_.template ReadEncodedValue<AddressType>(encoding, &start);
start = AdjustPcFromFde(start);
uint64_t length;
- if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &length)) {
+ memory_.set_pc_offset(0);
+ if (!valid || !memory_.template ReadEncodedValue<AddressType>(encoding, &length)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
last_error_.address = memory_.cur_offset();
return false;
@@ -877,7 +878,7 @@
while (first < last) {
size_t current = (first + last) / 2;
const FdeInfo* info = &fdes_[current];
- if (pc >= info->start && pc <= info->end) {
+ if (pc >= info->start && pc < info->end) {
*fde_offset = info->offset;
return true;
}
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 3762107..4723606 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -53,7 +53,7 @@
valid_ = interface_->Init(&load_bias_);
if (valid_) {
- interface_->InitHeaders();
+ interface_->InitHeaders(load_bias_);
if (init_gnu_debugdata) {
InitGnuDebugdata();
} else {
@@ -83,7 +83,7 @@
// is in the uncompressed data.
uint64_t load_bias;
if (gnu->Init(&load_bias)) {
- gnu->InitHeaders();
+ gnu->InitHeaders(load_bias);
interface_->SetGnuDebugdataInterface(gnu);
} else {
// Free all of the memory associated with the gnu_debugdata section.
@@ -103,9 +103,9 @@
bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
std::lock_guard<std::mutex> guard(lock_);
- return valid_ && (interface_->GetFunctionName(addr, load_bias_, name, func_offset) ||
- (gnu_debugdata_interface_ && gnu_debugdata_interface_->GetFunctionName(
- addr, load_bias_, name, func_offset)));
+ return valid_ && (interface_->GetFunctionName(addr, name, func_offset) ||
+ (gnu_debugdata_interface_ &&
+ gnu_debugdata_interface_->GetFunctionName(addr, name, func_offset)));
}
bool Elf::GetGlobalVariable(const std::string& name, uint64_t* memory_address) {
@@ -174,7 +174,7 @@
// Lock during the step which can update information in the object.
std::lock_guard<std::mutex> guard(lock_);
- return interface_->Step(adjusted_rel_pc, load_bias_, regs, process_memory, finished);
+ return interface_->Step(adjusted_rel_pc, regs, process_memory, finished);
}
bool Elf::IsValidElf(Memory* memory) {
@@ -220,7 +220,6 @@
if (!valid_ || pc < load_bias_) {
return false;
}
- pc -= load_bias_;
if (interface_->IsValidPc(pc)) {
return true;
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 4c05a1b..915cddb 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -124,10 +124,10 @@
}
template <typename AddressType>
-void ElfInterface::InitHeadersWithTemplate() {
+void ElfInterface::InitHeadersWithTemplate(uint64_t load_bias) {
if (eh_frame_hdr_offset_ != 0) {
eh_frame_.reset(new DwarfEhFrameWithHdr<AddressType>(memory_));
- if (!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_)) {
+ if (!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_, load_bias)) {
eh_frame_.reset(nullptr);
}
}
@@ -136,7 +136,7 @@
// If there is an eh_frame section without an eh_frame_hdr section,
// or using the frame hdr object failed to init.
eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
- if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_)) {
+ if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_, load_bias)) {
eh_frame_.reset(nullptr);
}
}
@@ -150,7 +150,7 @@
if (debug_frame_offset_ != 0) {
debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_));
- if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_)) {
+ if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_, load_bias)) {
debug_frame_.reset(nullptr);
debug_frame_offset_ = 0;
debug_frame_size_ = static_cast<uint64_t>(-1);
@@ -211,7 +211,7 @@
return false;
}
- if (HandleType(offset, phdr.p_type, *load_bias)) {
+ if (HandleType(offset, phdr.p_type)) {
continue;
}
@@ -441,14 +441,14 @@
}
template <typename SymType>
-bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias, std::string* name,
+bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, std::string* name,
uint64_t* func_offset) {
if (symbols_.empty()) {
return false;
}
for (const auto symbol : symbols_) {
- if (symbol->GetName<SymType>(addr, load_bias, memory_, name, func_offset)) {
+ if (symbol->GetName<SymType>(addr, memory_, name, func_offset)) {
return true;
}
}
@@ -469,34 +469,25 @@
return false;
}
-bool ElfInterface::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
- bool* finished) {
+bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
last_error_.code = ERROR_NONE;
last_error_.address = 0;
- // Adjust the load bias to get the real relative pc.
- if (pc < load_bias) {
- last_error_.code = ERROR_UNWIND_INFO;
- return false;
- }
- uint64_t adjusted_pc = pc - load_bias;
-
// Try the debug_frame first since it contains the most specific unwind
// information.
DwarfSection* debug_frame = debug_frame_.get();
- if (debug_frame != nullptr && debug_frame->Step(adjusted_pc, regs, process_memory, finished)) {
+ if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory, finished)) {
return true;
}
// Try the eh_frame next.
DwarfSection* eh_frame = eh_frame_.get();
- if (eh_frame != nullptr && eh_frame->Step(adjusted_pc, regs, process_memory, finished)) {
+ if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished)) {
return true;
}
- // Finally try the gnu_debugdata interface, but always use a zero load bias.
if (gnu_debugdata_interface_ != nullptr &&
- gnu_debugdata_interface_->Step(pc, 0, regs, process_memory, finished)) {
+ gnu_debugdata_interface_->Step(pc, regs, process_memory, finished)) {
return true;
}
@@ -559,8 +550,8 @@
}
// Instantiate all of the needed template functions.
-template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
-template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
+template void ElfInterface::InitHeadersWithTemplate<uint32_t>(uint64_t);
+template void ElfInterface::InitHeadersWithTemplate<uint64_t>(uint64_t);
template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(uint64_t*);
template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(uint64_t*);
@@ -574,9 +565,9 @@
template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
-template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, uint64_t, std::string*,
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*,
uint64_t*);
-template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, uint64_t, std::string*,
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
uint64_t*);
template bool ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(const std::string&, uint64_t*);
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index f93baeb..a3244e8 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -26,6 +26,14 @@
namespace unwindstack {
+bool ElfInterfaceArm::Init(uint64_t* load_bias) {
+ if (!ElfInterface32::Init(load_bias)) {
+ return false;
+ }
+ load_bias_ = *load_bias;
+ return true;
+}
+
bool ElfInterfaceArm::FindEntry(uint32_t pc, uint64_t* entry_offset) {
if (start_offset_ == 0 || total_entries_ == 0) {
last_error_.code = ERROR_UNWIND_INFO;
@@ -79,41 +87,41 @@
#define PT_ARM_EXIDX 0x70000001
#endif
-bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type, uint64_t load_bias) {
+bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) {
if (type != PT_ARM_EXIDX) {
return false;
}
Elf32_Phdr phdr;
- if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
+ if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
return true;
}
- if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
- return true;
- }
- start_offset_ = phdr.p_vaddr - load_bias;
- total_entries_ = phdr.p_memsz / 8;
+
+ // The offset already takes into account the load bias.
+ start_offset_ = phdr.p_offset;
+
+ // Always use filesz instead of memsz. In most cases they are the same,
+ // but some shared libraries wind up setting one correctly and not the other.
+ total_entries_ = phdr.p_filesz / 8;
return true;
}
-bool ElfInterfaceArm::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
- bool* finished) {
+bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
// Dwarf unwind information is precise about whether a pc is covered or not,
// but arm unwind information only has ranges of pc. In order to avoid
// incorrectly doing a bad unwind using arm unwind information for a
// different function, always try and unwind with the dwarf information first.
- return ElfInterface32::Step(pc, load_bias, regs, process_memory, finished) ||
- StepExidx(pc, load_bias, regs, process_memory, finished);
+ return ElfInterface32::Step(pc, regs, process_memory, finished) ||
+ StepExidx(pc, regs, process_memory, finished);
}
-bool ElfInterfaceArm::StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
- bool* finished) {
+bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
// Adjust the load bias to get the real relative pc.
- if (pc < load_bias) {
+ if (pc < load_bias_) {
last_error_.code = ERROR_UNWIND_INFO;
return false;
}
- pc -= load_bias;
+ pc -= load_bias_;
RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
uint64_t entry_offset;
@@ -167,13 +175,12 @@
return return_value;
}
-bool ElfInterfaceArm::GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
- uint64_t* offset) {
+bool ElfInterfaceArm::GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) {
// For ARM, thumb function symbols have bit 0 set, but the address passed
// in here might not have this bit set and result in a failure to find
// the thumb function names. Adjust the address and offset to account
// for this possible case.
- if (ElfInterface32::GetFunctionName(addr | 1, load_bias, name, offset)) {
+ if (ElfInterface32::GetFunctionName(addr | 1, name, offset)) {
*offset &= ~1;
return true;
}
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index c1597ce..3bee9cf 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -64,28 +64,30 @@
iterator begin() { return iterator(this, 0); }
iterator end() { return iterator(this, total_entries_); }
+ bool Init(uint64_t* load_bias) override;
+
bool GetPrel31Addr(uint32_t offset, uint32_t* addr);
bool FindEntry(uint32_t pc, uint64_t* entry_offset);
- bool HandleType(uint64_t offset, uint32_t type, uint64_t load_bias) override;
+ bool HandleType(uint64_t offset, uint32_t type) override;
- bool Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
- bool* finished) override;
+ bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
- bool StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
- bool* finished);
+ bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
- bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
- uint64_t* offset) override;
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) override;
uint64_t start_offset() { return start_offset_; }
size_t total_entries() { return total_entries_; }
+ void set_load_bias(uint64_t load_bias) { load_bias_ = load_bias; }
+
protected:
uint64_t start_offset_ = 0;
size_t total_entries_ = 0;
+ uint64_t load_bias_ = 0;
std::unordered_map<size_t, uint32_t> addrs_;
};
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index 25def40..14ebdbb 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -54,10 +54,7 @@
}
template <typename SymType>
-bool Symbols::GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
- uint64_t* func_offset) {
- addr += load_bias;
-
+bool Symbols::GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset) {
if (symbols_.size() != 0) {
const Info* info = GetInfoFromCache(addr);
if (info) {
@@ -81,9 +78,6 @@
if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_FUNC) {
// Treat st_value as virtual address.
uint64_t start_offset = entry.st_value;
- if (entry.st_shndx != SHN_ABS) {
- start_offset += load_bias;
- }
uint64_t end_offset = start_offset + entry.st_size;
// Cache the value.
@@ -134,8 +128,8 @@
}
// Instantiate all of the needed template functions.
-template bool Symbols::GetName<Elf32_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
-template bool Symbols::GetName<Elf64_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
+template bool Symbols::GetName<Elf32_Sym>(uint64_t, Memory*, std::string*, uint64_t*);
+template bool Symbols::GetName<Elf64_Sym>(uint64_t, Memory*, std::string*, uint64_t*);
template bool Symbols::GetGlobal<Elf32_Sym>(Memory*, const std::string&, uint64_t*);
template bool Symbols::GetGlobal<Elf64_Sym>(Memory*, const std::string&, uint64_t*);
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
index 7d239c1..7fcd067 100644
--- a/libunwindstack/Symbols.h
+++ b/libunwindstack/Symbols.h
@@ -44,8 +44,7 @@
const Info* GetInfoFromCache(uint64_t addr);
template <typename SymType>
- bool GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
- uint64_t* func_offset);
+ bool GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset);
template <typename SymType>
bool GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address);
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index 209c54a..847f382 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -78,13 +78,13 @@
DwarfErrorCode LastErrorCode() { return last_error_.code; }
uint64_t LastErrorAddress() { return last_error_.address; }
- virtual bool Init(uint64_t offset, uint64_t size) = 0;
+ virtual bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) = 0;
virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*) = 0;
virtual bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) = 0;
- virtual bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) = 0;
+ virtual bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) = 0;
virtual const DwarfFde* GetFdeFromIndex(size_t index) = 0;
@@ -131,7 +131,7 @@
DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
virtual ~DwarfSectionImpl() = default;
- bool Init(uint64_t offset, uint64_t size) override;
+ bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
@@ -150,7 +150,7 @@
bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) override;
- bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) override;
+ bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) override;
protected:
bool EvalExpression(const DwarfLocation& loc, Memory* regular_memory, AddressType* value,
@@ -162,6 +162,9 @@
bool CreateSortedFdeList();
+ uint64_t load_bias_ = 0;
+ uint64_t pc_offset_ = 0;
+
std::vector<FdeInfo> fdes_;
uint64_t entries_offset_;
uint64_t entries_end_;
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 3a221bc..0c588da 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -54,17 +54,15 @@
virtual bool Init(uint64_t* load_bias) = 0;
- virtual void InitHeaders() = 0;
+ virtual void InitHeaders(uint64_t load_bias) = 0;
virtual bool GetSoname(std::string* name) = 0;
- virtual bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
- uint64_t* offset) = 0;
+ virtual bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) = 0;
virtual bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) = 0;
- virtual bool Step(uint64_t rel_pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
- bool* finished);
+ virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
virtual bool IsValidPc(uint64_t pc);
@@ -100,7 +98,7 @@
protected:
template <typename AddressType>
- void InitHeadersWithTemplate();
+ void InitHeadersWithTemplate(uint64_t load_bias);
template <typename EhdrType, typename PhdrType, typename ShdrType>
bool ReadAllHeaders(uint64_t* load_bias);
@@ -115,13 +113,12 @@
bool GetSonameWithTemplate(std::string* soname);
template <typename SymType>
- bool GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias, std::string* name,
- uint64_t* func_offset);
+ bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
template <typename SymType>
bool GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address);
- virtual bool HandleType(uint64_t, uint32_t, uint64_t) { return false; }
+ virtual bool HandleType(uint64_t, uint32_t) { return false; }
template <typename EhdrType>
static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
@@ -169,15 +166,16 @@
return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(load_bias);
}
- void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint32_t>(); }
+ void InitHeaders(uint64_t load_bias) override {
+ ElfInterface::InitHeadersWithTemplate<uint32_t>(load_bias);
+ }
bool GetSoname(std::string* soname) override {
return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
}
- bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
- uint64_t* func_offset) override {
- return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, load_bias, name, func_offset);
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+ return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
}
bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
@@ -198,15 +196,16 @@
return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(load_bias);
}
- void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint64_t>(); }
+ void InitHeaders(uint64_t load_bias) override {
+ ElfInterface::InitHeadersWithTemplate<uint64_t>(load_bias);
+ }
bool GetSoname(std::string* soname) override {
return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
}
- bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
- uint64_t* func_offset) override {
- return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, load_bias, name, func_offset);
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+ return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
}
bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
index 8d6d00d..5f3d1ea 100644
--- a/libunwindstack/tests/ArmExidxDecodeTest.cpp
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -36,8 +36,6 @@
class ArmExidxDecodeTest : public ::testing::TestWithParam<std::string> {
protected:
void Init(Memory* process_memory = nullptr) {
- TearDown();
-
if (process_memory == nullptr) {
process_memory = &process_memory_;
}
@@ -50,8 +48,8 @@
regs_arm_->set_sp(0);
exidx_.reset(new ArmExidx(regs_arm_.get(), &elf_memory_, process_memory));
- if (log_) {
- exidx_->set_log(true);
+ if (log_ != ARM_LOG_NONE) {
+ exidx_->set_log(log_);
exidx_->set_log_indent(0);
exidx_->set_log_skip_execution(false);
}
@@ -60,14 +58,20 @@
}
void SetUp() override {
- if (GetParam() != "no_logging") {
- log_ = false;
+ if (GetParam() == "no_logging") {
+ log_ = ARM_LOG_NONE;
+ } else if (GetParam() == "register_logging") {
+ log_ = ARM_LOG_BY_REG;
} else {
- log_ = true;
+ log_ = ARM_LOG_FULL;
}
- ResetLogs();
elf_memory_.Clear();
process_memory_.Clear();
+ ResetExidx();
+ }
+
+ void ResetExidx() {
+ ResetLogs();
Init();
}
@@ -77,7 +81,7 @@
MemoryFake elf_memory_;
MemoryFake process_memory_;
- bool log_;
+ ArmLogType log_;
};
TEST_P(ArmExidxDecodeTest, vsp_incr) {
@@ -86,38 +90,59 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp + 4\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 4\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 4\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10004U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->clear();
data_->push_back(0x01);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp + 8\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 8\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 8\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x1000cU, exidx_->cfa());
+ ASSERT_EQ(0x10008U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->clear();
data_->push_back(0x3f);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp + 256\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 256\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 256\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x1010cU, exidx_->cfa());
+ ASSERT_EQ(0x10100U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, vsp_decr) {
@@ -126,38 +151,59 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp - 4\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp - 4\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 - 4\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0xfffcU, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->clear();
data_->push_back(0x41);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp - 8\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp - 8\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 - 8\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0xfff4U, exidx_->cfa());
+ ASSERT_EQ(0xfff8U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->clear();
data_->push_back(0x7f);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp - 256\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp - 256\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 - 256\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0xfef4U, exidx_->cfa());
+ ASSERT_EQ(0xff00U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, refuse_unwind) {
@@ -166,10 +212,14 @@
data_->push_back(0x00);
ASSERT_FALSE(exidx_->Decode());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind Refuse to unwind\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Refuse to unwind\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
}
@@ -182,29 +232,60 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_TRUE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r15}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r15}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 4\n"
+ "4 unwind r15 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10004U, exidx_->cfa());
ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
- ResetLogs();
+ ResetExidx();
data_->push_back(0x8f);
data_->push_back(0xff);
for (size_t i = 0; i < 12; i++) {
- process_memory_.SetData32(0x10004 + i * 4, i + 0x20);
+ process_memory_.SetData32(0x10000 + i * 4, i + 0x20);
}
exidx_->set_pc_set(false);
ASSERT_TRUE(exidx_->Decode());
ASSERT_TRUE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}\n",
- GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 48\n"
+ "4 unwind r4 = [cfa - 48]\n"
+ "4 unwind r5 = [cfa - 44]\n"
+ "4 unwind r6 = [cfa - 40]\n"
+ "4 unwind r7 = [cfa - 36]\n"
+ "4 unwind r8 = [cfa - 32]\n"
+ "4 unwind r9 = [cfa - 28]\n"
+ "4 unwind r10 = [cfa - 24]\n"
+ "4 unwind r11 = [cfa - 20]\n"
+ "4 unwind r12 = [cfa - 16]\n"
+ "4 unwind r13 = [cfa - 12]\n"
+ "4 unwind r14 = [cfa - 8]\n"
+ "4 unwind r15 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
// Popping r13 results in a modified cfa.
ASSERT_EQ(0x29U, exidx_->cfa());
@@ -222,7 +303,7 @@
ASSERT_EQ(0x2aU, (*exidx_->regs())[14]);
ASSERT_EQ(0x2bU, (*exidx_->regs())[15]);
- ResetLogs();
+ ResetExidx();
exidx_->set_cfa(0x10034);
data_->push_back(0x81);
data_->push_back(0x28);
@@ -233,10 +314,22 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r7, r9, r12}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r7, r9, r12}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 12\n"
+ "4 unwind r7 = [cfa - 12]\n"
+ "4 unwind r9 = [cfa - 8]\n"
+ "4 unwind r12 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10040U, exidx_->cfa());
ASSERT_EQ(0x11U, (*exidx_->regs())[7]);
@@ -255,34 +348,63 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = r0\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = r0\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r0\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(1U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
+ exidx_->set_cfa(0x100);
+ for (size_t i = 0; i < 15; i++) {
+ (*regs_arm_)[i] = i + 1;
+ }
data_->push_back(0x93);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = r3\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = r3\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r3\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(4U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
+ exidx_->set_cfa(0x100);
+ for (size_t i = 0; i < 15; i++) {
+ (*regs_arm_)[i] = i + 1;
+ }
data_->push_back(0x9e);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = r14\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = r14\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r14\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(15U, exidx_->cfa());
}
@@ -292,22 +414,30 @@
data_->push_back(0x9d);
ASSERT_FALSE(exidx_->Decode());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
// 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
- ResetLogs();
+ ResetExidx();
data_->push_back(0x9f);
ASSERT_FALSE(exidx_->Decode());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
}
@@ -319,53 +449,93 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 4\n"
+ "4 unwind r4 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10004U, exidx_->cfa());
ASSERT_EQ(0x14U, (*exidx_->regs())[4]);
- ResetLogs();
+ ResetExidx();
data_->push_back(0xa3);
- process_memory_.SetData32(0x10004, 0x20);
- process_memory_.SetData32(0x10008, 0x30);
- process_memory_.SetData32(0x1000c, 0x40);
- process_memory_.SetData32(0x10010, 0x50);
+ process_memory_.SetData32(0x10000, 0x20);
+ process_memory_.SetData32(0x10004, 0x30);
+ process_memory_.SetData32(0x10008, 0x40);
+ process_memory_.SetData32(0x1000c, 0x50);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r4-r7}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4-r7}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 16\n"
+ "4 unwind r4 = [cfa - 16]\n"
+ "4 unwind r5 = [cfa - 12]\n"
+ "4 unwind r6 = [cfa - 8]\n"
+ "4 unwind r7 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10014U, exidx_->cfa());
+ ASSERT_EQ(0x10010U, exidx_->cfa());
ASSERT_EQ(0x20U, (*exidx_->regs())[4]);
ASSERT_EQ(0x30U, (*exidx_->regs())[5]);
ASSERT_EQ(0x40U, (*exidx_->regs())[6]);
ASSERT_EQ(0x50U, (*exidx_->regs())[7]);
- ResetLogs();
+ ResetExidx();
data_->push_back(0xa7);
- process_memory_.SetData32(0x10014, 0x41);
- process_memory_.SetData32(0x10018, 0x51);
- process_memory_.SetData32(0x1001c, 0x61);
- process_memory_.SetData32(0x10020, 0x71);
- process_memory_.SetData32(0x10024, 0x81);
- process_memory_.SetData32(0x10028, 0x91);
- process_memory_.SetData32(0x1002c, 0xa1);
- process_memory_.SetData32(0x10030, 0xb1);
+ process_memory_.SetData32(0x10000, 0x41);
+ process_memory_.SetData32(0x10004, 0x51);
+ process_memory_.SetData32(0x10008, 0x61);
+ process_memory_.SetData32(0x1000c, 0x71);
+ process_memory_.SetData32(0x10010, 0x81);
+ process_memory_.SetData32(0x10014, 0x91);
+ process_memory_.SetData32(0x10018, 0xa1);
+ process_memory_.SetData32(0x1001c, 0xb1);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r4-r11}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4-r11}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 32\n"
+ "4 unwind r4 = [cfa - 32]\n"
+ "4 unwind r5 = [cfa - 28]\n"
+ "4 unwind r6 = [cfa - 24]\n"
+ "4 unwind r7 = [cfa - 20]\n"
+ "4 unwind r8 = [cfa - 16]\n"
+ "4 unwind r9 = [cfa - 12]\n"
+ "4 unwind r10 = [cfa - 8]\n"
+ "4 unwind r11 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10034U, exidx_->cfa());
+ ASSERT_EQ(0x10020U, exidx_->cfa());
ASSERT_EQ(0x41U, (*exidx_->regs())[4]);
ASSERT_EQ(0x51U, (*exidx_->regs())[5]);
ASSERT_EQ(0x61U, (*exidx_->regs())[6]);
@@ -384,57 +554,100 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r4, r14}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4, r14}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 8\n"
+ "4 unwind r4 = [cfa - 8]\n"
+ "4 unwind r14 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10008U, exidx_->cfa());
ASSERT_EQ(0x12U, (*exidx_->regs())[4]);
ASSERT_EQ(0x22U, (*exidx_->regs())[14]);
- ResetLogs();
+ ResetExidx();
data_->push_back(0xab);
- process_memory_.SetData32(0x10008, 0x1);
- process_memory_.SetData32(0x1000c, 0x2);
- process_memory_.SetData32(0x10010, 0x3);
- process_memory_.SetData32(0x10014, 0x4);
- process_memory_.SetData32(0x10018, 0x5);
+ process_memory_.SetData32(0x10000, 0x1);
+ process_memory_.SetData32(0x10004, 0x2);
+ process_memory_.SetData32(0x10008, 0x3);
+ process_memory_.SetData32(0x1000c, 0x4);
+ process_memory_.SetData32(0x10010, 0x5);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r4-r7, r14}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4-r7, r14}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 20\n"
+ "4 unwind r4 = [cfa - 20]\n"
+ "4 unwind r5 = [cfa - 16]\n"
+ "4 unwind r6 = [cfa - 12]\n"
+ "4 unwind r7 = [cfa - 8]\n"
+ "4 unwind r14 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x1001cU, exidx_->cfa());
+ ASSERT_EQ(0x10014U, exidx_->cfa());
ASSERT_EQ(0x1U, (*exidx_->regs())[4]);
ASSERT_EQ(0x2U, (*exidx_->regs())[5]);
ASSERT_EQ(0x3U, (*exidx_->regs())[6]);
ASSERT_EQ(0x4U, (*exidx_->regs())[7]);
ASSERT_EQ(0x5U, (*exidx_->regs())[14]);
- ResetLogs();
+ ResetExidx();
data_->push_back(0xaf);
- process_memory_.SetData32(0x1001c, 0x1a);
- process_memory_.SetData32(0x10020, 0x2a);
- process_memory_.SetData32(0x10024, 0x3a);
- process_memory_.SetData32(0x10028, 0x4a);
- process_memory_.SetData32(0x1002c, 0x5a);
- process_memory_.SetData32(0x10030, 0x6a);
- process_memory_.SetData32(0x10034, 0x7a);
- process_memory_.SetData32(0x10038, 0x8a);
- process_memory_.SetData32(0x1003c, 0x9a);
+ process_memory_.SetData32(0x10000, 0x1a);
+ process_memory_.SetData32(0x10004, 0x2a);
+ process_memory_.SetData32(0x10008, 0x3a);
+ process_memory_.SetData32(0x1000c, 0x4a);
+ process_memory_.SetData32(0x10010, 0x5a);
+ process_memory_.SetData32(0x10014, 0x6a);
+ process_memory_.SetData32(0x10018, 0x7a);
+ process_memory_.SetData32(0x1001c, 0x8a);
+ process_memory_.SetData32(0x10020, 0x9a);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r4-r11, r14}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4-r11, r14}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 36\n"
+ "4 unwind r4 = [cfa - 36]\n"
+ "4 unwind r5 = [cfa - 32]\n"
+ "4 unwind r6 = [cfa - 28]\n"
+ "4 unwind r7 = [cfa - 24]\n"
+ "4 unwind r8 = [cfa - 20]\n"
+ "4 unwind r9 = [cfa - 16]\n"
+ "4 unwind r10 = [cfa - 12]\n"
+ "4 unwind r11 = [cfa - 8]\n"
+ "4 unwind r14 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10040U, exidx_->cfa());
+ ASSERT_EQ(0x10024U, exidx_->cfa());
ASSERT_EQ(0x1aU, (*exidx_->regs())[4]);
ASSERT_EQ(0x2aU, (*exidx_->regs())[5]);
ASSERT_EQ(0x3aU, (*exidx_->regs())[6]);
@@ -451,10 +664,17 @@
data_->push_back(0xb0);
ASSERT_FALSE(exidx_->Decode());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind finish\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind finish\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10000U, exidx_->cfa());
ASSERT_EQ(ARM_STATUS_FINISH, exidx_->status());
@@ -466,10 +686,14 @@
data_->push_back(0x00);
ASSERT_FALSE(exidx_->Decode());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10000U, exidx_->cfa());
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -477,15 +701,19 @@
// 10110001 xxxxyyyy: Spare (xxxx != 0000)
for (size_t x = 1; x < 16; x++) {
for (size_t y = 0; y < 16; y++) {
- ResetLogs();
+ ResetExidx();
data_->push_back(0xb1);
data_->push_back((x << 4) | y);
ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
- if (log_) {
- ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+ break;
}
ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -494,29 +722,37 @@
// 101101nn: Spare
for (size_t n = 0; n < 4; n++) {
- ResetLogs();
+ ResetExidx();
data_->push_back(0xb4 | n);
ASSERT_FALSE(exidx_->Decode()) << "n = " << n;
ASSERT_EQ("", GetFakeLogBuf()) << "n = " << n;
- if (log_) {
- ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "n = " << n;
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "n = " << n;
+ break;
}
ASSERT_EQ(0x10000U, exidx_->cfa()) << "n = " << n;
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
}
// 11000111 00000000: Spare
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc7);
data_->push_back(0x00);
ASSERT_FALSE(exidx_->Decode());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10000U, exidx_->cfa());
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -524,15 +760,19 @@
// 11000111 xxxxyyyy: Spare (xxxx != 0000)
for (size_t x = 1; x < 16; x++) {
for (size_t y = 0; y < 16; y++) {
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc7);
data_->push_back(0x10);
ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
- if (log_) {
- ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+ break;
}
ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -541,14 +781,18 @@
// 11001yyy: Spare (yyy != 000, 001)
for (size_t y = 2; y < 8; y++) {
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc8 | y);
ASSERT_FALSE(exidx_->Decode()) << "y = " << y;
ASSERT_EQ("", GetFakeLogBuf()) << "y = " << y;
- if (log_) {
- ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "y = " << y;
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "y = " << y;
+ break;
}
ASSERT_EQ(0x10000U, exidx_->cfa()) << "y = " << y;
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -557,14 +801,18 @@
// 11xxxyyy: Spare (xxx != 000, 001, 010)
for (size_t x = 3; x < 8; x++) {
for (size_t y = 0; y < 8; y++) {
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc0 | (x << 3) | y);
ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
- if (log_) {
- ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+ break;
}
ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -580,47 +828,81 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r0}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r0}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 4\n"
+ "4 unwind r0 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10004U, exidx_->cfa());
ASSERT_EQ(0x45U, (*exidx_->regs())[0]);
- ResetLogs();
+ ResetExidx();
data_->push_back(0xb1);
data_->push_back(0x0a);
- process_memory_.SetData32(0x10004, 0x23);
- process_memory_.SetData32(0x10008, 0x24);
+ process_memory_.SetData32(0x10000, 0x23);
+ process_memory_.SetData32(0x10004, 0x24);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r1, r3}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r1, r3}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 8\n"
+ "4 unwind r1 = [cfa - 8]\n"
+ "4 unwind r3 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x1000cU, exidx_->cfa());
+ ASSERT_EQ(0x10008U, exidx_->cfa());
ASSERT_EQ(0x23U, (*exidx_->regs())[1]);
ASSERT_EQ(0x24U, (*exidx_->regs())[3]);
- ResetLogs();
+ ResetExidx();
data_->push_back(0xb1);
data_->push_back(0x0f);
- process_memory_.SetData32(0x1000c, 0x65);
- process_memory_.SetData32(0x10010, 0x54);
- process_memory_.SetData32(0x10014, 0x43);
- process_memory_.SetData32(0x10018, 0x32);
+ process_memory_.SetData32(0x10000, 0x65);
+ process_memory_.SetData32(0x10004, 0x54);
+ process_memory_.SetData32(0x10008, 0x43);
+ process_memory_.SetData32(0x1000c, 0x32);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {r0, r1, r2, r3}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r0, r1, r2, r3}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 16\n"
+ "4 unwind r0 = [cfa - 16]\n"
+ "4 unwind r1 = [cfa - 12]\n"
+ "4 unwind r2 = [cfa - 8]\n"
+ "4 unwind r3 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x1001cU, exidx_->cfa());
+ ASSERT_EQ(0x10010U, exidx_->cfa());
ASSERT_EQ(0x65U, (*exidx_->regs())[0]);
ASSERT_EQ(0x54U, (*exidx_->regs())[1]);
ASSERT_EQ(0x43U, (*exidx_->regs())[2]);
@@ -634,28 +916,42 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp + 1024\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 1024\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 1024\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10400U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xb2);
data_->push_back(0xff);
data_->push_back(0x02);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp + 2048\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 2048\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 2048\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10c00U, exidx_->cfa());
+ ASSERT_EQ(0x10800U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xb2);
data_->push_back(0xff);
data_->push_back(0x82);
@@ -663,12 +959,19 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp + 3147776\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 3147776\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 3147776\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x311400U, exidx_->cfa());
+ ASSERT_EQ(0x310800U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, pop_vfp_fstmfdx) {
@@ -678,25 +981,37 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x1000cU, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xb3);
data_->push_back(0x48);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d4-d12}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d4-d12}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10058U, exidx_->cfa());
+ ASSERT_EQ(0x1004cU, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, pop_vfp8_fstmfdx) {
@@ -705,36 +1020,54 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x1000cU, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xbb);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d8-d11}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8-d11}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10030U, exidx_->cfa());
+ ASSERT_EQ(0x10024U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xbf);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10074U, exidx_->cfa());
+ ASSERT_EQ(0x10044U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, pop_mmx_wr10) {
@@ -743,36 +1076,54 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wR10}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR10}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10008U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc2);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wR10-wR12}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR10-wR12}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10020U, exidx_->cfa());
+ ASSERT_EQ(0x10018U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc5);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wR10-wR15}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR10-wR15}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10050U, exidx_->cfa());
+ ASSERT_EQ(0x10030U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, pop_mmx_wr) {
@@ -782,38 +1133,56 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wR0}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR0}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10008U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc6);
data_->push_back(0x25);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wR2-wR7}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR2-wR7}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10038U, exidx_->cfa());
+ ASSERT_EQ(0x10030U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc6);
data_->push_back(0xff);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wR15-wR30}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR15-wR30}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x100b8U, exidx_->cfa());
+ ASSERT_EQ(0x10080U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, pop_mmx_wcgr) {
@@ -823,38 +1192,56 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wCGR0}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wCGR0}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10004U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc7);
data_->push_back(0x0a);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wCGR1, wCGR3}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wCGR1, wCGR3}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x1000cU, exidx_->cfa());
+ ASSERT_EQ(0x10008U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc7);
data_->push_back(0x0f);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {wCGR0, wCGR1, wCGR2, wCGR3}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wCGR0, wCGR1, wCGR2, wCGR3}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x1001cU, exidx_->cfa());
+ ASSERT_EQ(0x10010U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, pop_vfp16_vpush) {
@@ -864,38 +1251,56 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d16}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d16}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10008U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc8);
data_->push_back(0x14);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d17-d21}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d17-d21}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10030U, exidx_->cfa());
+ ASSERT_EQ(0x10028U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc8);
data_->push_back(0xff);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d31-d46}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d31-d46}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x100b0U, exidx_->cfa());
+ ASSERT_EQ(0x10080U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, pop_vfp_vpush) {
@@ -905,38 +1310,56 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10008U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc9);
data_->push_back(0x23);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d2-d5}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d2-d5}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10028U, exidx_->cfa());
+ ASSERT_EQ(0x10020U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xc9);
data_->push_back(0xff);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d15-d30}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d15-d30}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x100a8U, exidx_->cfa());
+ ASSERT_EQ(0x10080U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, pop_vfp8_vpush) {
@@ -945,36 +1368,54 @@
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10008U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xd2);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d8-d10}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8-d10}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10020U, exidx_->cfa());
+ ASSERT_EQ(0x10018U, exidx_->cfa());
- ResetLogs();
+ ResetExidx();
data_->push_back(0xd7);
ASSERT_TRUE(exidx_->Decode());
ASSERT_FALSE(exidx_->pc_set());
ASSERT_EQ("", GetFakeLogBuf());
- if (log_) {
- ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
}
- ASSERT_EQ(0x10060U, exidx_->cfa());
+ ASSERT_EQ(0x10040U, exidx_->cfa());
}
TEST_P(ArmExidxDecodeTest, expect_truncated) {
@@ -1047,32 +1488,147 @@
TEST_P(ArmExidxDecodeTest, eval_multiple_decodes) {
// vsp = vsp + 4
data_->push_back(0x00);
- // vsp = vsp + 8
+ // vsp = vsp + 12
data_->push_back(0x02);
// Finish
data_->push_back(0xb0);
ASSERT_TRUE(exidx_->Eval());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp + 4\n"
- "4 unwind vsp = vsp + 12\n"
- "4 unwind finish\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ(
+ "4 unwind vsp = vsp + 4\n"
+ "4 unwind vsp = vsp + 12\n"
+ "4 unwind finish\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 16\n", GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10010U, exidx_->cfa());
ASSERT_FALSE(exidx_->pc_set());
}
+TEST_P(ArmExidxDecodeTest, eval_vsp_add_after_pop) {
+ // Pop {r15}
+ data_->push_back(0x88);
+ data_->push_back(0x00);
+ // vsp = vsp + 12
+ data_->push_back(0x02);
+ // Finish
+ data_->push_back(0xb0);
+ process_memory_.SetData32(0x10000, 0x10);
+
+ ASSERT_TRUE(exidx_->Eval());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ(
+ "4 unwind pop {r15}\n"
+ "4 unwind vsp = vsp + 12\n"
+ "4 unwind finish\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 16\n"
+ "4 unwind r15 = [cfa - 16]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10010U, exidx_->cfa());
+ ASSERT_TRUE(exidx_->pc_set());
+ ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
+TEST_P(ArmExidxDecodeTest, eval_vsp_add_large_after_pop) {
+ // Pop {r15}
+ data_->push_back(0x88);
+ data_->push_back(0x00);
+ // vsp = vsp + 1024
+ data_->push_back(0xb2);
+ data_->push_back(0x7f);
+ // Finish
+ data_->push_back(0xb0);
+ process_memory_.SetData32(0x10000, 0x10);
+
+ ASSERT_TRUE(exidx_->Eval());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ(
+ "4 unwind pop {r15}\n"
+ "4 unwind vsp = vsp + 1024\n"
+ "4 unwind finish\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 1028\n"
+ "4 unwind r15 = [cfa - 1028]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10404U, exidx_->cfa());
+ ASSERT_TRUE(exidx_->pc_set());
+ ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
+TEST_P(ArmExidxDecodeTest, eval_vsp_sub_after_pop) {
+ // Pop {r15}
+ data_->push_back(0x88);
+ data_->push_back(0x00);
+ // vsp = vsp - 4
+ data_->push_back(0x41);
+ // Finish
+ data_->push_back(0xb0);
+ process_memory_.SetData32(0x10000, 0x10);
+
+ ASSERT_TRUE(exidx_->Eval());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ(
+ "4 unwind pop {r15}\n"
+ "4 unwind vsp = vsp - 8\n"
+ "4 unwind finish\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 - 4\n"
+ "4 unwind r15 = [cfa + 4]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0xfffcU, exidx_->cfa());
+ ASSERT_TRUE(exidx_->pc_set());
+ ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
TEST_P(ArmExidxDecodeTest, eval_pc_set) {
// vsp = vsp + 4
data_->push_back(0x00);
- // vsp = vsp + 8
+ // vsp = vsp + 12
data_->push_back(0x02);
// Pop {r15}
data_->push_back(0x88);
data_->push_back(0x00);
- // vsp = vsp + 8
+ // vsp = vsp + 12
data_->push_back(0x02);
// Finish
data_->push_back(0xb0);
@@ -1080,20 +1636,33 @@
process_memory_.SetData32(0x10010, 0x10);
ASSERT_TRUE(exidx_->Eval());
- if (log_) {
- ASSERT_EQ("4 unwind vsp = vsp + 4\n"
- "4 unwind vsp = vsp + 12\n"
- "4 unwind pop {r15}\n"
- "4 unwind vsp = vsp + 12\n"
- "4 unwind finish\n", GetFakeLogPrint());
- } else {
- ASSERT_EQ("", GetFakeLogPrint());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ(
+ "4 unwind vsp = vsp + 4\n"
+ "4 unwind vsp = vsp + 12\n"
+ "4 unwind pop {r15}\n"
+ "4 unwind vsp = vsp + 12\n"
+ "4 unwind finish\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 32\n"
+ "4 unwind r15 = [cfa - 16]\n",
+ GetFakeLogPrint());
+ break;
}
ASSERT_EQ(0x10020U, exidx_->cfa());
ASSERT_TRUE(exidx_->pc_set());
ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
}
-INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest, ::testing::Values("logging", "no_logging"));
+INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest,
+ ::testing::Values("logging", "register_logging", "no_logging"));
} // namespace unwindstack
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp
index 8d0f0e5..79c799c 100644
--- a/libunwindstack/tests/ArmExidxExtractTest.cpp
+++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -301,7 +301,7 @@
elf_memory_.SetData32(0x1000, 0x7fff2340);
elf_memory_.SetData32(0x1004, 1);
- exidx_->set_log(true);
+ exidx_->set_log(ARM_LOG_FULL);
exidx_->set_log_indent(0);
exidx_->set_log_skip_execution(false);
@@ -316,7 +316,7 @@
elf_memory_.SetData32(0x4000, 0x7ffa3000);
elf_memory_.SetData32(0x4004, 0x80a8b0b0);
- exidx_->set_log(true);
+ exidx_->set_log(ARM_LOG_FULL);
exidx_->set_log_indent(0);
exidx_->set_log_skip_execution(false);
@@ -330,7 +330,7 @@
elf_memory_.SetData32(0x6234, 0x2);
elf_memory_.SetData32(0x6238, 0x00112233);
- exidx_->set_log(true);
+ exidx_->set_log(ARM_LOG_FULL);
exidx_->set_log_indent(0);
exidx_->set_log_skip_execution(false);
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index b17ca33..bb2e8f0 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -79,7 +79,7 @@
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
ResetLogs();
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
std::string expected = "4 unwind Illegal\n";
expected += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", i);
ASSERT_EQ(expected, GetFakeLogPrint());
@@ -90,7 +90,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_nop) {
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x00});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
std::string expected =
"4 unwind DW_CFA_nop\n"
"4 unwind Raw Data: 0x00\n";
@@ -101,7 +101,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_offset) {
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x83, 0x04});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2002));
std::string expected =
"4 unwind DW_CFA_offset register(3) 4\n"
"4 unwind Raw Data: 0x83 0x04\n";
@@ -111,7 +111,7 @@
ResetLogs();
this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x83, 0x84, 0x01});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2100, 0x2103));
expected =
"4 unwind DW_CFA_offset register(3) 132\n"
"4 unwind Raw Data: 0x83 0x84 0x01\n";
@@ -122,7 +122,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended) {
this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x05, 0x03, 0x02});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
std::string expected =
"4 unwind DW_CFA_offset_extended register(3) 2\n"
"4 unwind Raw Data: 0x05 0x03 0x02\n";
@@ -132,7 +132,7 @@
ResetLogs();
this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x05, 0x81, 0x01, 0x82, 0x12});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
expected =
"4 unwind DW_CFA_offset_extended register(129) 2306\n"
"4 unwind Raw Data: 0x05 0x81 0x01 0x82 0x12\n";
@@ -143,7 +143,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended_sf) {
this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x11, 0x05, 0x10});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
std::string expected =
"4 unwind DW_CFA_offset_extended_sf register(5) 16\n"
"4 unwind Raw Data: 0x11 0x05 0x10\n";
@@ -154,7 +154,7 @@
ResetLogs();
this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x11, 0x86, 0x01, 0xff, 0x7f});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
expected =
"4 unwind DW_CFA_offset_extended_sf register(134) -1\n"
"4 unwind Raw Data: 0x11 0x86 0x01 0xff 0x7f\n";
@@ -165,7 +165,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_restore) {
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0xc2});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
std::string expected =
"4 unwind DW_CFA_restore register(2)\n"
"4 unwind Raw Data: 0xc2\n";
@@ -175,7 +175,7 @@
ResetLogs();
this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x82, 0x04, 0xc2});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3003));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x3000, 0x3003));
expected =
"4 unwind DW_CFA_offset register(2) 4\n"
"4 unwind Raw Data: 0x82 0x04\n"
@@ -188,7 +188,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_restore_extended) {
this->memory_.SetMemory(0x4000, std::vector<uint8_t>{0x06, 0x08});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4000, 0x4002));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4000, 0x4002));
std::string expected =
"4 unwind DW_CFA_restore_extended register(8)\n"
"4 unwind Raw Data: 0x06 0x08\n";
@@ -198,7 +198,7 @@
ResetLogs();
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x05, 0x82, 0x02, 0x04, 0x06, 0x82, 0x02});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5007));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x5000, 0x5007));
expected =
"4 unwind DW_CFA_offset_extended register(258) 4\n"
"4 unwind Raw Data: 0x05 0x82 0x02 0x04\n"
@@ -228,7 +228,7 @@
this->memory_.SetMemory(0x50, buffer, sizeof(buffer));
ResetLogs();
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam)));
std::string expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
expected += "4 unwind " + raw_data + "\n";
expected += "4 unwind \n";
@@ -240,7 +240,7 @@
ResetLogs();
this->fde_.pc_start = address + 0x10;
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam)));
expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
expected += "4 unwind " + raw_data + "\n";
expected += "4 unwind \n";
@@ -252,7 +252,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc) {
this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x44});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x201));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x201));
std::string expected =
"4 unwind DW_CFA_advance_loc 4\n"
"4 unwind Raw Data: 0x44\n"
@@ -260,22 +260,12 @@
"4 unwind PC 0x2010\n";
ASSERT_EQ(expected, GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
-
- ResetLogs();
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x200, 0x201));
- expected =
- "4 unwind DW_CFA_advance_loc 4\n"
- "4 unwind Raw Data: 0x44\n"
- "4 unwind \n"
- "4 unwind PC 0x2110\n";
- ASSERT_EQ(expected, GetFakeLogPrint());
- ASSERT_EQ("", GetFakeLogBuf());
}
TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc1) {
this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x02, 0x04});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x202));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x202));
std::string expected =
"4 unwind DW_CFA_advance_loc1 4\n"
"4 unwind Raw Data: 0x02 0x04\n"
@@ -283,22 +273,12 @@
"4 unwind PC 0x2004\n";
ASSERT_EQ(expected, GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
-
- ResetLogs();
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x10, 0x200, 0x202));
- expected =
- "4 unwind DW_CFA_advance_loc1 4\n"
- "4 unwind Raw Data: 0x02 0x04\n"
- "4 unwind \n"
- "4 unwind PC 0x2014\n";
- ASSERT_EQ(expected, GetFakeLogPrint());
- ASSERT_EQ("", GetFakeLogBuf());
}
TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc2) {
this->memory_.SetMemory(0x600, std::vector<uint8_t>{0x03, 0x04, 0x03});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x600, 0x603));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x600, 0x603));
std::string expected =
"4 unwind DW_CFA_advance_loc2 772\n"
"4 unwind Raw Data: 0x03 0x04 0x03\n"
@@ -306,22 +286,12 @@
"4 unwind PC 0x2304\n";
ASSERT_EQ(expected, GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
-
- ResetLogs();
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1000, 0x600, 0x603));
- expected =
- "4 unwind DW_CFA_advance_loc2 772\n"
- "4 unwind Raw Data: 0x03 0x04 0x03\n"
- "4 unwind \n"
- "4 unwind PC 0x3304\n";
- ASSERT_EQ(expected, GetFakeLogPrint());
- ASSERT_EQ("", GetFakeLogBuf());
}
TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc4) {
this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x04, 0x04, 0x03, 0x02, 0x01});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x505));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x505));
std::string expected =
"4 unwind DW_CFA_advance_loc4 16909060\n"
"4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
@@ -329,22 +299,12 @@
"4 unwind PC 0x1022304\n";
ASSERT_EQ(expected, GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
-
- ResetLogs();
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x500, 0x505));
- expected =
- "4 unwind DW_CFA_advance_loc4 16909060\n"
- "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
- "4 unwind \n"
- "4 unwind PC 0x1024304\n";
- ASSERT_EQ(expected, GetFakeLogPrint());
- ASSERT_EQ("", GetFakeLogBuf());
}
TYPED_TEST_P(DwarfCfaLogTest, cfa_undefined) {
this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x07, 0x09});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa02));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xa02));
std::string expected =
"4 unwind DW_CFA_undefined register(9)\n"
"4 unwind Raw Data: 0x07 0x09\n";
@@ -355,7 +315,7 @@
dwarf_loc_regs_t cie_loc_regs;
this->memory_.SetMemory(0x1a00, std::vector<uint8_t>{0x07, 0x81, 0x01});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1a00, 0x1a03));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1a00, 0x1a03));
expected =
"4 unwind DW_CFA_undefined register(129)\n"
"4 unwind Raw Data: 0x07 0x81 0x01\n";
@@ -366,7 +326,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_same) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x08, 0x7f});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
std::string expected =
"4 unwind DW_CFA_same_value register(127)\n"
"4 unwind Raw Data: 0x08 0x7f\n";
@@ -376,7 +336,7 @@
ResetLogs();
this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x08, 0xff, 0x01});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2100, 0x2103));
expected =
"4 unwind DW_CFA_same_value register(255)\n"
"4 unwind Raw Data: 0x08 0xff 0x01\n";
@@ -387,7 +347,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_register) {
this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x303));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x303));
std::string expected =
"4 unwind DW_CFA_register register(2) register(1)\n"
"4 unwind Raw Data: 0x09 0x02 0x01\n";
@@ -397,7 +357,7 @@
ResetLogs();
this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x09, 0xff, 0x01, 0xff, 0x03});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4305));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4300, 0x4305));
expected =
"4 unwind DW_CFA_register register(255) register(511)\n"
"4 unwind Raw Data: 0x09 0xff 0x01 0xff 0x03\n";
@@ -408,7 +368,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_state) {
this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x0a});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x301));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x301));
std::string expected =
"4 unwind DW_CFA_remember_state\n"
@@ -419,7 +379,7 @@
ResetLogs();
this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x0b});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4301));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4300, 0x4301));
expected =
"4 unwind DW_CFA_restore_state\n"
@@ -431,7 +391,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_state_cfa_offset_restore) {
this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x0a, 0x0e, 0x40, 0x0b});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3004));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x3000, 0x3004));
std::string expected =
"4 unwind DW_CFA_remember_state\n"
@@ -447,7 +407,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0c, 0x7f, 0x74});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
std::string expected =
"4 unwind DW_CFA_def_cfa register(127) 116\n"
@@ -458,7 +418,7 @@
ResetLogs();
this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0c, 0xff, 0x02, 0xf4, 0x04});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x205));
expected =
"4 unwind DW_CFA_def_cfa register(383) 628\n"
@@ -470,7 +430,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_sf) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x12, 0x30, 0x25});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
std::string expected =
"4 unwind DW_CFA_def_cfa_sf register(48) 37\n"
@@ -482,7 +442,7 @@
ResetLogs();
this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x12, 0xa3, 0x01, 0xfa, 0x7f});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x205));
expected =
"4 unwind DW_CFA_def_cfa_sf register(163) -6\n"
@@ -494,7 +454,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_register) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0d, 0x72});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
std::string expected =
"4 unwind DW_CFA_def_cfa_register register(114)\n"
@@ -505,7 +465,7 @@
ResetLogs();
this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0d, 0xf9, 0x20});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
expected =
"4 unwind DW_CFA_def_cfa_register register(4217)\n"
@@ -517,7 +477,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0e, 0x59});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
std::string expected =
"4 unwind DW_CFA_def_cfa_offset 89\n"
@@ -526,7 +486,7 @@
ASSERT_EQ("", GetFakeLogBuf());
ResetLogs();
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
expected =
"4 unwind DW_CFA_def_cfa_offset 89\n"
@@ -537,7 +497,7 @@
ResetLogs();
this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0e, 0xd4, 0x0a});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
expected =
"4 unwind DW_CFA_def_cfa_offset 1364\n"
@@ -549,7 +509,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset_sf) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x13, 0x23});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
std::string expected =
"4 unwind DW_CFA_def_cfa_offset_sf 35\n"
@@ -558,7 +518,7 @@
ASSERT_EQ("", GetFakeLogBuf());
ResetLogs();
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
expected =
"4 unwind DW_CFA_def_cfa_offset_sf 35\n"
@@ -570,7 +530,7 @@
ResetLogs();
this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x13, 0xf6, 0x7f});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
expected =
"4 unwind DW_CFA_def_cfa_offset_sf -10\n"
@@ -582,7 +542,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_expression) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0f, 0x04, 0x01, 0x02, 0x04, 0x05});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x106));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x106));
std::string expected =
"4 unwind DW_CFA_def_cfa_expression 4\n"
@@ -614,7 +574,7 @@
}
expected += '\n';
this->memory_.SetMemory(0x200, ops);
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x284));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x284));
expected = "4 unwind DW_CFA_def_cfa_expression 129\n" + expected;
ASSERT_EQ(expected + op_string, GetFakeLogPrint());
@@ -624,7 +584,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_expression) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x10, 0x04, 0x02, 0xc0, 0xc1});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x105));
std::string expected =
"4 unwind DW_CFA_expression register(4) 2\n"
@@ -652,7 +612,7 @@
expected = "4 unwind DW_CFA_expression register(255) 130\n" + expected + "\n";
this->memory_.SetMemory(0x200, ops);
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x287));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x287));
ASSERT_EQ(expected + op_string, GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
@@ -661,7 +621,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x14, 0x45, 0x54});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
std::string expected =
"4 unwind DW_CFA_val_offset register(69) 84\n"
@@ -672,7 +632,7 @@
ResetLogs();
this->memory_.SetMemory(0x400, std::vector<uint8_t>{0x14, 0xa2, 0x02, 0xb4, 0x05});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x400, 0x405));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x400, 0x405));
expected =
"4 unwind DW_CFA_val_offset register(290) 692\n"
@@ -684,7 +644,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset_sf) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x15, 0x56, 0x12});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
std::string expected =
"4 unwind DW_CFA_val_offset_sf register(86) 18\n"
@@ -696,7 +656,7 @@
ResetLogs();
this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x15, 0xff, 0x01, 0xc0, 0x7f});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa05));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xa05));
expected =
"4 unwind DW_CFA_val_offset_sf register(255) -64\n"
@@ -708,7 +668,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_val_expression) {
this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x16, 0x05, 0x02, 0xb0, 0xb1});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x105));
std::string expected =
"4 unwind DW_CFA_val_expression register(5) 2\n"
@@ -737,7 +697,7 @@
this->memory_.SetMemory(0xa00, ops);
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xaad));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xaad));
ASSERT_EQ(expected + op_string, GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
@@ -746,7 +706,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_args_size) {
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2e, 0x04});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2002));
std::string expected =
"4 unwind DW_CFA_GNU_args_size 4\n"
@@ -757,7 +717,7 @@
ResetLogs();
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x2e, 0xa4, 0x80, 0x04});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5004));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x5000, 0x5004));
expected =
"4 unwind DW_CFA_GNU_args_size 65572\n"
@@ -769,7 +729,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_negative_offset_extended) {
this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x2f, 0x08, 0x10});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
std::string expected =
"4 unwind DW_CFA_GNU_negative_offset_extended register(8) 16\n"
@@ -780,7 +740,7 @@
ResetLogs();
this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x2f, 0x81, 0x02, 0xff, 0x01});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
expected =
"4 unwind DW_CFA_GNU_negative_offset_extended register(257) 255\n"
@@ -792,7 +752,7 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_register_override) {
this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01, 0x09, 0x02, 0x04});
- ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x306));
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x306));
std::string expected =
"4 unwind DW_CFA_register register(2) register(1)\n"
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index c28a41e..3a52044 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -103,7 +103,7 @@
this->memory_.SetData32(0x5508, 0x4500);
this->memory_.SetData32(0x550c, 0x500);
- ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -142,7 +142,7 @@
this->memory_.SetData32(0x5108, 0x1500);
this->memory_.SetData32(0x510c, 0x200);
- ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->LastErrorCode());
}
@@ -181,7 +181,7 @@
this->memory_.SetData32(0x5508, 0x4500);
this->memory_.SetData32(0x550c, 0x500);
- ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(2U, this->debug_frame_->TestGetFdeCount());
}
@@ -226,7 +226,7 @@
this->memory_.SetData64(0x5514, 0x4500);
this->memory_.SetData64(0x551c, 0x500);
- ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -267,7 +267,7 @@
this->memory_.SetData64(0x5114, 0x1500);
this->memory_.SetData64(0x511c, 0x200);
- ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->LastErrorCode());
}
@@ -312,10 +312,48 @@
this->memory_.SetData64(0x5514, 0x4500);
this->memory_.SetData64(0x551c, 0x500);
- ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(2U, this->debug_frame_->TestGetFdeCount());
}
+TYPED_TEST_P(DwarfDebugFrameTest, Init_non_zero_load_bias) {
+ // CIE 32 information.
+ this->memory_.SetData32(0x5000, 0xfc);
+ this->memory_.SetData32(0x5004, 0xffffffff);
+ this->memory_.SetData8(0x5008, 1);
+ this->memory_.SetData8(0x5009, 'z');
+ this->memory_.SetData8(0x500a, 'R');
+ this->memory_.SetData8(0x500b, '\0');
+ this->memory_.SetData8(0x500c, 0);
+ this->memory_.SetData8(0x500d, 0);
+ this->memory_.SetData8(0x500e, 0);
+ this->memory_.SetData8(0x500f, 0);
+ this->memory_.SetData8(0x5010, 0x1b);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5100, 0xfc);
+ this->memory_.SetData32(0x5104, 0);
+ this->memory_.SetData32(0x5108, 0x1500);
+ this->memory_.SetData32(0x510c, 0x200);
+ this->memory_.SetData8(0x5110, 0);
+ this->memory_.SetData8(0x5111, 0);
+
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200, 0x1000));
+ ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
+
+ typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+ this->debug_frame_->TestGetFdeInfo(0, &info);
+ EXPECT_EQ(0x5100U, info.offset);
+ EXPECT_EQ(0x2500U, info.start);
+ EXPECT_EQ(0x2700U, info.end);
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x2504);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x2500U, fde->pc_start);
+ EXPECT_EQ(0x2700U, fde->pc_end);
+}
+
TYPED_TEST_P(DwarfDebugFrameTest, Init_version1) {
// CIE 32 information.
this->memory_.SetData32(0x5000, 0xfc);
@@ -340,7 +378,7 @@
this->memory_.SetData16(0x5108, 0x1500);
this->memory_.SetData16(0x510a, 0x200);
- ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200, 0));
ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -383,7 +421,7 @@
this->memory_.SetData16(0x5108, 0x1500);
this->memory_.SetData16(0x510a, 0x200);
- ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200, 0));
ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -538,8 +576,8 @@
REGISTER_TYPED_TEST_CASE_P(DwarfDebugFrameTest, Init32, Init32_fde_not_following_cie,
Init32_do_not_fail_on_bad_next_entry, Init64,
Init64_do_not_fail_on_bad_next_entry, Init64_fde_not_following_cie,
- Init_version1, Init_version4, GetFdeOffsetFromPc, GetCieFde32,
- GetCieFde64);
+ Init_non_zero_load_bias, Init_version1, Init_version4,
+ GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index a73db65..e8d53e6 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -103,7 +103,7 @@
this->memory_.SetData32(0x5508, 0x4500);
this->memory_.SetData32(0x550c, 0x500);
- ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600));
+ ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(4U, this->eh_frame_->TestGetFdeCount());
typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -142,7 +142,7 @@
this->memory_.SetData32(0x5108, 0x1500);
this->memory_.SetData32(0x510c, 0x200);
- ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600));
+ ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
}
@@ -187,7 +187,7 @@
this->memory_.SetData64(0x5514, 0x4500);
this->memory_.SetData64(0x551c, 0x500);
- ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600));
+ ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(4U, this->eh_frame_->TestGetFdeCount());
typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -228,10 +228,48 @@
this->memory_.SetData64(0x5114, 0x1500);
this->memory_.SetData64(0x511c, 0x200);
- ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600));
+ ASSERT_FALSE(this->eh_frame_->Init(0x5000, 0x600, 0));
ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
}
+TYPED_TEST_P(DwarfEhFrameTest, Init_non_zero_load_bias) {
+ // CIE 32 information.
+ this->memory_.SetData32(0x5000, 0xfc);
+ this->memory_.SetData32(0x5004, 0);
+ this->memory_.SetData8(0x5008, 1);
+ this->memory_.SetData8(0x5009, 'z');
+ this->memory_.SetData8(0x500a, 'R');
+ this->memory_.SetData8(0x500b, '\0');
+ this->memory_.SetData8(0x500c, 0);
+ this->memory_.SetData8(0x500d, 0);
+ this->memory_.SetData8(0x500e, 0);
+ this->memory_.SetData8(0x500f, 0);
+ this->memory_.SetData8(0x5010, 0x1b);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5100, 0xfc);
+ this->memory_.SetData32(0x5104, 0x104);
+ this->memory_.SetData32(0x5108, 0x1500);
+ this->memory_.SetData32(0x510c, 0x200);
+ this->memory_.SetData8(0x5110, 0);
+ this->memory_.SetData8(0x5111, 0);
+
+ ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200, 0x2000));
+ ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
+
+ typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
+
+ this->eh_frame_->TestGetFdeInfo(0, &info);
+ EXPECT_EQ(0x5100U, info.offset);
+ EXPECT_EQ(0x8608U, info.start);
+ EXPECT_EQ(0x8808U, info.end);
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromPc(0x8700);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x8608U, fde->pc_start);
+ EXPECT_EQ(0x8808U, fde->pc_end);
+}
+
TYPED_TEST_P(DwarfEhFrameTest, Init_version1) {
// CIE 32 information.
this->memory_.SetData32(0x5000, 0xfc);
@@ -256,7 +294,7 @@
this->memory_.SetData16(0x5108, 0x1500);
this->memory_.SetData16(0x510a, 0x200);
- ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200));
+ ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200, 0));
ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -299,7 +337,7 @@
this->memory_.SetData16(0x5108, 0x1500);
this->memory_.SetData16(0x510a, 0x200);
- ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200));
+ ASSERT_TRUE(this->eh_frame_->Init(0x5000, 0x200, 0));
ASSERT_EQ(1U, this->eh_frame_->TestGetFdeCount());
typename DwarfEhFrame<TypeParam>::FdeInfo info(0, 0, 0);
@@ -450,8 +488,8 @@
}
REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, Init32, Init32_fde_not_following_cie, Init64,
- Init64_fde_not_following_cie, Init_version1, Init_version4,
- GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
+ Init64_fde_not_following_cie, Init_non_zero_load_bias, Init_version1,
+ Init_version4, GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index 4240419..19c7b98 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -83,7 +83,7 @@
this->memory_.SetData16(0x1004, 0x500);
this->memory_.SetData32(0x1006, 126);
- ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100));
+ ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding());
@@ -97,19 +97,66 @@
// Verify a zero fde count fails to init.
this->memory_.SetData32(0x1006, 0);
- ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
ASSERT_EQ(DWARF_ERROR_NO_FDES, this->eh_frame_->LastErrorCode());
// Verify an unexpected version will cause a fail.
this->memory_.SetData32(0x1006, 126);
this->memory_.SetData8(0x1000, 0);
- ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->LastErrorCode());
this->memory_.SetData8(0x1000, 2);
- ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->LastErrorCode());
}
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, Init_non_zero_load_bias) {
+ this->memory_.SetMemory(0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4,
+ DW_EH_PE_pcrel | DW_EH_PE_sdata4});
+ this->memory_.SetData16(0x1004, 0x500);
+ this->memory_.SetData32(0x1006, 1);
+ this->memory_.SetData32(0x100a, 0x2500);
+ this->memory_.SetData32(0x100e, 0x1400);
+
+ // CIE 32 information.
+ this->memory_.SetData32(0x1300, 0xfc);
+ this->memory_.SetData32(0x1304, 0);
+ this->memory_.SetData8(0x1308, 1);
+ this->memory_.SetData8(0x1309, 'z');
+ this->memory_.SetData8(0x130a, 'R');
+ this->memory_.SetData8(0x130b, '\0');
+ this->memory_.SetData8(0x130c, 0);
+ this->memory_.SetData8(0x130d, 0);
+ this->memory_.SetData8(0x130e, 0);
+ this->memory_.SetData8(0x130f, 0);
+ this->memory_.SetData8(0x1310, 0x1b);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x1400, 0xfc);
+ this->memory_.SetData32(0x1404, 0x104);
+ this->memory_.SetData32(0x1408, 0x10f8);
+ this->memory_.SetData32(0x140c, 0x200);
+ this->memory_.SetData8(0x1410, 0);
+ this->memory_.SetData8(0x1411, 0);
+
+ ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0x2000));
+ EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
+ EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
+ EXPECT_EQ(0x1b, this->eh_frame_->TestGetTableEncoding());
+ EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
+ EXPECT_EQ(1U, this->eh_frame_->TestGetFdeCount());
+ EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
+ EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
+ EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
+ EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
+ EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromPc(0x4600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x4500U, fde->pc_start);
+ EXPECT_EQ(0x4700U, fde->pc_end);
+}
+
TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_expect_cache_fail) {
this->eh_frame_->TestSetTableEntrySize(0x10);
this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
@@ -123,6 +170,7 @@
EXPECT_EQ(0x1000U, this->eh_frame_->LastErrorAddress());
}
+// We are assuming that pc rel, is really relative to the load_bias.
TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_pcrel) {
this->eh_frame_->TestSetTableEncoding(DW_EH_PE_pcrel | DW_EH_PE_udata4);
this->eh_frame_->TestSetEntriesOffset(0x1000);
@@ -134,8 +182,8 @@
auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
ASSERT_TRUE(info != nullptr);
- EXPECT_EQ(0x1380U, info->pc);
- EXPECT_EQ(0x1540U, info->offset);
+ EXPECT_EQ(0x340U, info->pc);
+ EXPECT_EQ(0x500U, info->offset);
}
TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_datarel) {
@@ -430,14 +478,14 @@
ASSERT_EQ(nullptr, this->eh_frame_->GetFdeFromPc(0x800));
}
-REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, GetFdeInfoFromIndex_expect_cache_fail,
- GetFdeInfoFromIndex_read_pcrel, GetFdeInfoFromIndex_read_datarel,
- GetFdeInfoFromIndex_cached, GetFdeOffsetBinary_verify,
- GetFdeOffsetBinary_index_fail, GetFdeOffsetSequential,
- GetFdeOffsetSequential_last_element, GetFdeOffsetSequential_end_check,
- GetFdeOffsetFromPc_fail_fde_count, GetFdeOffsetFromPc_binary_search,
- GetFdeOffsetFromPc_sequential_search, GetCieFde32, GetCieFde64,
- GetFdeFromPc_fde_not_found);
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias,
+ GetFdeInfoFromIndex_expect_cache_fail, GetFdeInfoFromIndex_read_pcrel,
+ GetFdeInfoFromIndex_read_datarel, GetFdeInfoFromIndex_cached,
+ GetFdeOffsetBinary_verify, GetFdeOffsetBinary_index_fail,
+ GetFdeOffsetSequential, GetFdeOffsetSequential_last_element,
+ GetFdeOffsetSequential_end_check, GetFdeOffsetFromPc_fail_fde_count,
+ GetFdeOffsetFromPc_binary_search, GetFdeOffsetFromPc_sequential_search,
+ GetCieFde32, GetCieFde64, GetFdeFromPc_fde_not_found);
typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameWithHdrTestTypes;
INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index c340291..414f2f2 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -36,7 +36,7 @@
MockDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
virtual ~MockDwarfSectionImpl() = default;
- MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
+ MOCK_METHOD3(Init, bool(uint64_t, uint64_t, uint64_t));
MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
@@ -884,7 +884,7 @@
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x00});
this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0xc2});
- ASSERT_TRUE(this->section_->Log(2, 0x1000, 0x1000, &fde));
+ ASSERT_TRUE(this->section_->Log(2, 0x1000, &fde));
ASSERT_EQ(
"4 unwind DW_CFA_nop\n"
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index 071d2df..2c6c879 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -30,13 +30,13 @@
MockDwarfSection(Memory* memory) : DwarfSection(memory) {}
virtual ~MockDwarfSection() = default;
- MOCK_METHOD4(Log, bool(uint8_t, uint64_t, uint64_t, const DwarfFde*));
+ MOCK_METHOD3(Log, bool(uint8_t, uint64_t, const DwarfFde*));
MOCK_METHOD5(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*));
MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
- MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
+ MOCK_METHOD3(Init, bool(uint64_t, uint64_t, uint64_t));
MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
index 66207db..3d5ddd6 100644
--- a/libunwindstack/tests/ElfFake.cpp
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -32,7 +32,7 @@
std::deque<FunctionData> ElfInterfaceFake::functions_;
std::deque<StepData> ElfInterfaceFake::steps_;
-bool ElfInterfaceFake::GetFunctionName(uint64_t, uint64_t, std::string* name, uint64_t* offset) {
+bool ElfInterfaceFake::GetFunctionName(uint64_t, std::string* name, uint64_t* offset) {
if (functions_.empty()) {
return false;
}
@@ -52,7 +52,7 @@
return true;
}
-bool ElfInterfaceFake::Step(uint64_t, uint64_t, Regs* regs, Memory*, bool* finished) {
+bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished) {
if (steps_.empty()) {
return false;
}
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index e232986..a3bf5ce 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -67,13 +67,13 @@
virtual ~ElfInterfaceFake() = default;
bool Init(uint64_t*) override { return false; }
- void InitHeaders() override {}
+ void InitHeaders(uint64_t) override {}
bool GetSoname(std::string*) override { return false; }
- bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override;
+ bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
bool GetGlobalVariable(const std::string&, uint64_t*) override;
- bool Step(uint64_t, uint64_t, Regs*, Memory*, bool*) override;
+ bool Step(uint64_t, Regs*, Memory*, bool*) override;
void FakeSetGlobalVariable(const std::string& global, uint64_t offset) {
globals_[global] = offset;
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 70a52ad..a8bb4aa 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -245,56 +245,41 @@
TEST_F(ElfInterfaceArmTest, HandleType_not_arm_exidx) {
ElfInterfaceArmFake interface(&memory_);
- ASSERT_FALSE(interface.HandleType(0x1000, PT_NULL, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_LOAD, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_DYNAMIC, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_INTERP, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_NOTE, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_SHLIB, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_PHDR, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_TLS, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_LOOS, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_HIOS, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_LOPROC, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_HIPROC, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_EH_FRAME, 0));
- ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_STACK, 0));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_NULL));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_LOAD));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_DYNAMIC));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_INTERP));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_NOTE));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_SHLIB));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_PHDR));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_TLS));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_LOOS));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_HIOS));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_LOPROC));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_HIPROC));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_EH_FRAME));
+ ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_STACK));
}
TEST_F(ElfInterfaceArmTest, HandleType_arm_exidx) {
ElfInterfaceArmFake interface(&memory_);
- Elf32_Phdr phdr;
+ Elf32_Phdr phdr = {};
interface.FakeSetStartOffset(0x1000);
interface.FakeSetTotalEntries(100);
- phdr.p_vaddr = 0x2000;
- phdr.p_memsz = 0xa00;
+ phdr.p_offset = 0x2000;
+ phdr.p_filesz = 0xa00;
// Verify that if reads fail, we don't set the values but still get true.
- ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0));
- ASSERT_EQ(0x1000U, interface.start_offset());
- ASSERT_EQ(100U, interface.total_entries());
-
- // Verify that if the second read fails, we still don't set the values.
- memory_.SetData32(
- 0x1000 + reinterpret_cast<uint64_t>(&phdr.p_vaddr) - reinterpret_cast<uint64_t>(&phdr),
- phdr.p_vaddr);
- ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0));
+ ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
ASSERT_EQ(0x1000U, interface.start_offset());
ASSERT_EQ(100U, interface.total_entries());
// Everything is correct and present.
- memory_.SetData32(
- 0x1000 + reinterpret_cast<uint64_t>(&phdr.p_memsz) - reinterpret_cast<uint64_t>(&phdr),
- phdr.p_memsz);
- ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0));
+ memory_.SetMemory(0x1000, &phdr, sizeof(phdr));
+ ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
ASSERT_EQ(0x2000U, interface.start_offset());
ASSERT_EQ(320U, interface.total_entries());
-
- // Non-zero load bias.
- ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001, 0x1000));
- ASSERT_EQ(0x1000U, interface.start_offset());
- ASSERT_EQ(320U, interface.total_entries());
}
TEST_F(ElfInterfaceArmTest, StepExidx) {
@@ -302,7 +287,7 @@
// FindEntry fails.
bool finished;
- ASSERT_FALSE(interface.StepExidx(0x7000, 0, nullptr, nullptr, &finished));
+ ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr, &finished));
EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
// ExtractEntry should fail.
@@ -316,18 +301,18 @@
regs[ARM_REG_LR] = 0x20000;
regs.set_sp(regs[ARM_REG_SP]);
regs.set_pc(0x1234);
- ASSERT_FALSE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
+ ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_MEMORY_INVALID, interface.LastErrorCode());
EXPECT_EQ(0x1004U, interface.LastErrorAddress());
// Eval should fail.
memory_.SetData32(0x1004, 0x81000000);
- ASSERT_FALSE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
+ ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
// Everything should pass.
memory_.SetData32(0x1004, 0x80b0b0b0);
- ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
ASSERT_FALSE(finished);
ASSERT_EQ(0x1000U, regs.sp());
@@ -336,11 +321,13 @@
ASSERT_EQ(0x20000U, regs[ARM_REG_PC]);
// Load bias is non-zero.
- ASSERT_TRUE(interface.StepExidx(0x8000, 0x1000, ®s, &process_memory_, &finished));
+ interface.set_load_bias(0x1000);
+ ASSERT_TRUE(interface.StepExidx(0x8000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
// Pc too small.
- ASSERT_FALSE(interface.StepExidx(0x8000, 0x9000, ®s, &process_memory_, &finished));
+ interface.set_load_bias(0x9000);
+ ASSERT_FALSE(interface.StepExidx(0x8000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
}
@@ -362,7 +349,7 @@
// Everything should pass.
bool finished;
- ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
ASSERT_FALSE(finished);
ASSERT_EQ(0x10004U, regs.sp());
@@ -386,7 +373,7 @@
regs.set_pc(0x1234);
bool finished;
- ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
ASSERT_TRUE(finished);
ASSERT_EQ(0x10000U, regs.sp());
@@ -409,7 +396,7 @@
regs.set_pc(0x1234);
bool finished;
- ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
ASSERT_TRUE(finished);
ASSERT_EQ(0x10000U, regs.sp());
@@ -436,7 +423,7 @@
regs.set_pc(0x1234);
bool finished;
- ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
ASSERT_TRUE(finished);
ASSERT_EQ(0U, regs.pc());
@@ -449,7 +436,7 @@
regs.set_sp(regs[ARM_REG_SP]);
regs.set_pc(0x1234);
- ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
ASSERT_TRUE(finished);
ASSERT_EQ(0U, regs.pc());
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index bf97e30..487d39c 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -116,8 +116,7 @@
template <typename Sym>
void ElfInterfaceTest::InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
uint64_t sym_offset, const char* name) {
- Sym sym;
- memset(&sym, 0, sizeof(sym));
+ Sym sym = {};
sym.st_info = STT_FUNC;
sym.st_value = value;
sym.st_size = size;
@@ -132,15 +131,13 @@
void ElfInterfaceTest::SinglePtLoad() {
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 1;
ehdr.e_phentsize = sizeof(Phdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Phdr phdr;
- memset(&phdr, 0, sizeof(phdr));
+ Phdr phdr = {};
phdr.p_type = PT_LOAD;
phdr.p_vaddr = 0x2000;
phdr.p_memsz = 0x10000;
@@ -172,15 +169,13 @@
void ElfInterfaceTest::MultipleExecutablePtLoads() {
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 3;
ehdr.e_phentsize = sizeof(Phdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Phdr phdr;
- memset(&phdr, 0, sizeof(phdr));
+ Phdr phdr = {};
phdr.p_type = PT_LOAD;
phdr.p_vaddr = 0x2000;
phdr.p_memsz = 0x10000;
@@ -241,15 +236,13 @@
void ElfInterfaceTest::MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr() {
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 3;
ehdr.e_phentsize = sizeof(Phdr) + 100;
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Phdr phdr;
- memset(&phdr, 0, sizeof(phdr));
+ Phdr phdr = {};
phdr.p_type = PT_LOAD;
phdr.p_vaddr = 0x2000;
phdr.p_memsz = 0x10000;
@@ -312,15 +305,13 @@
void ElfInterfaceTest::NonExecutablePtLoads() {
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 3;
ehdr.e_phentsize = sizeof(Phdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Phdr phdr;
- memset(&phdr, 0, sizeof(phdr));
+ Phdr phdr = {};
phdr.p_type = PT_LOAD;
phdr.p_vaddr = 0x2000;
phdr.p_memsz = 0x10000;
@@ -371,17 +362,15 @@
void ElfInterfaceTest::ManyPhdrs() {
std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 7;
ehdr.e_phentsize = sizeof(Phdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Phdr phdr;
uint64_t phdr_offset = 0x100;
- memset(&phdr, 0, sizeof(phdr));
+ Phdr phdr = {};
phdr.p_type = PT_LOAD;
phdr.p_vaddr = 0x2000;
phdr.p_memsz = 0x10000;
@@ -444,18 +433,16 @@
TEST_F(ElfInterfaceTest, elf32_arm) {
ElfInterfaceArm elf_arm(&memory_);
- Elf32_Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Elf32_Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 1;
ehdr.e_phentsize = sizeof(Elf32_Phdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Elf32_Phdr phdr;
- memset(&phdr, 0, sizeof(phdr));
+ Elf32_Phdr phdr = {};
phdr.p_type = PT_ARM_EXIDX;
- phdr.p_vaddr = 0x2000;
- phdr.p_memsz = 16;
+ phdr.p_offset = 0x2000;
+ phdr.p_filesz = 16;
memory_.SetMemory(0x100, &phdr, sizeof(phdr));
// Add arm exidx entries.
@@ -480,8 +467,7 @@
template <typename Ehdr, typename Phdr, typename Shdr, typename Dyn>
void ElfInterfaceTest::SonameInit(SonameTestEnum test_type) {
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_shoff = 0x200;
ehdr.e_shnum = 2;
ehdr.e_shentsize = sizeof(Shdr);
@@ -490,8 +476,7 @@
ehdr.e_phentsize = sizeof(Phdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Shdr shdr;
- memset(&shdr, 0, sizeof(shdr));
+ Shdr shdr = {};
shdr.sh_type = SHT_STRTAB;
if (test_type == SONAME_MISSING_MAP) {
shdr.sh_addr = 0x20100;
@@ -501,8 +486,7 @@
shdr.sh_offset = 0x10000;
memory_.SetMemory(0x200 + sizeof(shdr), &shdr, sizeof(shdr));
- Phdr phdr;
- memset(&phdr, 0, sizeof(phdr));
+ Phdr phdr = {};
phdr.p_type = PT_DYNAMIC;
phdr.p_offset = 0x2000;
phdr.p_memsz = sizeof(Dyn) * 3;
@@ -647,7 +631,7 @@
memory_.SetData32(0x10004, 0x500);
memory_.SetData32(0x10008, 250);
- elf.InitHeaders();
+ elf.InitHeaders(0);
EXPECT_FALSE(elf.eh_frame() == nullptr);
EXPECT_TRUE(elf.debug_frame() == nullptr);
@@ -680,7 +664,7 @@
memory_.SetData32(0x5108, 0x1500);
memory_.SetData32(0x510c, 0x200);
- elf.InitHeaders();
+ elf.InitHeaders(0);
EXPECT_TRUE(elf.eh_frame() == nullptr);
EXPECT_FALSE(elf.debug_frame() == nullptr);
@@ -703,7 +687,7 @@
elf.FakeSetDebugFrameOffset(0);
elf.FakeSetDebugFrameSize(0);
- elf.InitHeaders();
+ elf.InitHeaders(0);
EXPECT_TRUE(elf.eh_frame() == nullptr);
EXPECT_EQ(0U, elf.eh_frame_offset());
@@ -728,7 +712,7 @@
elf.FakeSetDebugFrameOffset(0x1000);
elf.FakeSetDebugFrameSize(0x100);
- elf.InitHeaders();
+ elf.InitHeaders(0);
EXPECT_TRUE(elf.eh_frame() == nullptr);
EXPECT_TRUE(elf.debug_frame() == nullptr);
@@ -748,8 +732,7 @@
void ElfInterfaceTest::InitSectionHeadersMalformed() {
std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_shoff = 0x1000;
ehdr.e_shnum = 10;
ehdr.e_shentsize = sizeof(Shdr);
@@ -774,8 +757,7 @@
uint64_t offset = 0x1000;
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_shoff = offset;
ehdr.e_shnum = 10;
ehdr.e_shentsize = entry_size;
@@ -783,8 +765,7 @@
offset += ehdr.e_shentsize;
- Shdr shdr;
- memset(&shdr, 0, sizeof(shdr));
+ Shdr shdr = {};
shdr.sh_type = SHT_SYMTAB;
shdr.sh_link = 4;
shdr.sh_addr = 0x5000;
@@ -833,10 +814,10 @@
// Look in the first symbol table.
std::string name;
uint64_t name_offset;
- ASSERT_TRUE(elf->GetFunctionName(0x90010, 0, &name, &name_offset));
+ ASSERT_TRUE(elf->GetFunctionName(0x90010, &name, &name_offset));
EXPECT_EQ("function_one", name);
EXPECT_EQ(16U, name_offset);
- ASSERT_TRUE(elf->GetFunctionName(0xd0020, 0, &name, &name_offset));
+ ASSERT_TRUE(elf->GetFunctionName(0xd0020, &name, &name_offset));
EXPECT_EQ("function_two", name);
EXPECT_EQ(32U, name_offset);
}
@@ -863,8 +844,7 @@
uint64_t offset = 0x2000;
- Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Ehdr ehdr = {};
ehdr.e_shoff = offset;
ehdr.e_shnum = 10;
ehdr.e_shentsize = sizeof(Shdr);
@@ -873,8 +853,7 @@
offset += ehdr.e_shentsize;
- Shdr shdr;
- memset(&shdr, 0, sizeof(shdr));
+ Shdr shdr = {};
shdr.sh_type = SHT_PROGBITS;
shdr.sh_link = 2;
shdr.sh_name = 0x200;
@@ -956,15 +935,13 @@
TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load) {
std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
- Elf32_Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Elf32_Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 1;
ehdr.e_phentsize = sizeof(Elf32_Phdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Elf32_Phdr phdr;
- memset(&phdr, 0, sizeof(phdr));
+ Elf32_Phdr phdr = {};
phdr.p_type = PT_LOAD;
phdr.p_vaddr = 0;
phdr.p_memsz = 0x10000;
@@ -984,15 +961,13 @@
TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load_non_zero_load_bias) {
std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
- Elf32_Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Elf32_Ehdr ehdr = {};
ehdr.e_phoff = 0x100;
ehdr.e_phnum = 1;
ehdr.e_phentsize = sizeof(Elf32_Phdr);
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Elf32_Phdr phdr;
- memset(&phdr, 0, sizeof(phdr));
+ Elf32_Phdr phdr = {};
phdr.p_type = PT_LOAD;
phdr.p_vaddr = 0x2000;
phdr.p_memsz = 0x10000;
@@ -1017,16 +992,14 @@
uint64_t sh_offset = 0x100;
- Elf32_Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Elf32_Ehdr ehdr = {};
ehdr.e_shstrndx = 1;
ehdr.e_shoff = sh_offset;
ehdr.e_shentsize = sizeof(Elf32_Shdr);
ehdr.e_shnum = 3;
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Elf32_Shdr shdr;
- memset(&shdr, 0, sizeof(shdr));
+ Elf32_Shdr shdr = {};
shdr.sh_type = SHT_NULL;
memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
@@ -1065,7 +1038,7 @@
uint64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- elf->InitHeaders();
+ elf->InitHeaders(0);
EXPECT_EQ(0U, load_bias);
EXPECT_FALSE(elf->IsValidPc(0));
EXPECT_FALSE(elf->IsValidPc(0x20ff));
@@ -1080,16 +1053,14 @@
uint64_t sh_offset = 0x100;
- Elf32_Ehdr ehdr;
- memset(&ehdr, 0, sizeof(ehdr));
+ Elf32_Ehdr ehdr = {};
ehdr.e_shstrndx = 1;
ehdr.e_shoff = sh_offset;
ehdr.e_shentsize = sizeof(Elf32_Shdr);
ehdr.e_shnum = 3;
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
- Elf32_Shdr shdr;
- memset(&shdr, 0, sizeof(shdr));
+ Elf32_Shdr shdr = {};
shdr.sh_type = SHT_NULL;
memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
@@ -1128,7 +1099,7 @@
uint64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- elf->InitHeaders();
+ elf->InitHeaders(0);
EXPECT_EQ(0U, load_bias);
EXPECT_FALSE(elf->IsValidPc(0));
EXPECT_FALSE(elf->IsValidPc(0x27ff));
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index aecbf6d..55fe16f 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -297,16 +297,11 @@
elf.FakeSetInterface(interface);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
MapInfo map_info(0x1000, 0x2000);
ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
- elf.FakeSetLoadBias(0x3000);
- ASSERT_EQ(0x3101U, elf.GetRelPc(0x1101, &map_info));
-
elf.FakeSetValid(false);
- elf.FakeSetLoadBias(0);
ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
}
@@ -328,7 +323,6 @@
}
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
bool finished;
ASSERT_TRUE(elf.Step(0x3000, 0x1000, ®s, &process_memory, &finished));
EXPECT_FALSE(finished);
@@ -342,11 +336,11 @@
virtual ~ElfInterfaceMock() = default;
bool Init(uint64_t*) override { return false; }
- void InitHeaders() override {}
+ void InitHeaders(uint64_t) override {}
bool GetSoname(std::string*) override { return false; }
- bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override { return false; }
+ bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
- MOCK_METHOD5(Step, bool(uint64_t, uint64_t, Regs*, Memory*, bool*));
+ MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*));
MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*));
MOCK_METHOD1(IsValidPc, bool(uint64_t));
@@ -358,7 +352,6 @@
TEST_F(ElfTest, step_in_interface) {
ElfFake elf(memory_);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
RegsArm regs;
@@ -367,30 +360,12 @@
MemoryFake process_memory;
bool finished;
- EXPECT_CALL(*interface, Step(0x1000, 0, ®s, &process_memory, &finished))
+ EXPECT_CALL(*interface, Step(0x1000, ®s, &process_memory, &finished))
.WillOnce(::testing::Return(true));
ASSERT_TRUE(elf.Step(0x1004, 0x1000, ®s, &process_memory, &finished));
}
-TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
- ElfFake elf(memory_);
- elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0x4000);
-
- RegsArm regs;
-
- ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
- elf.FakeSetInterface(interface);
- MemoryFake process_memory;
-
- bool finished;
- EXPECT_CALL(*interface, Step(0x7300, 0x4000, ®s, &process_memory, &finished))
- .WillOnce(::testing::Return(true));
-
- ASSERT_TRUE(elf.Step(0x7304, 0x7300, ®s, &process_memory, &finished));
-}
-
TEST_F(ElfTest, get_global_invalid_elf) {
ElfFake elf(memory_);
elf.FakeSetValid(false);
@@ -403,7 +378,6 @@
TEST_F(ElfTest, get_global_valid_not_in_interface) {
ElfFake elf(memory_);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
elf.FakeSetInterface(interface);
@@ -431,10 +405,26 @@
ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
}
+TEST_F(ElfTest, get_global_valid_dynamic_zero_non_zero_load_bias) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0x100);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ uint64_t offset;
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
+
+ ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+ EXPECT_EQ(0x200U, offset);
+}
+
TEST_F(ElfTest, get_global_valid_dynamic_zero) {
ElfFake elf(memory_);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
elf.FakeSetInterface(interface);
@@ -456,7 +446,6 @@
TEST_F(ElfTest, get_global_valid_in_gnu_debugdata_dynamic_zero) {
ElfFake elf(memory_);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
elf.FakeSetInterface(interface);
@@ -470,27 +459,9 @@
EXPECT_EQ(0x300U, offset);
}
-TEST_F(ElfTest, get_global_valid_dynamic_zero_non_zero_load_bias) {
- ElfFake elf(memory_);
- elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0x100);
-
- ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
- elf.FakeSetInterface(interface);
-
- uint64_t offset;
- std::string global("something");
- EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
- .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
-
- ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
- EXPECT_EQ(0x200U, offset);
-}
-
TEST_F(ElfTest, get_global_valid_dynamic_adjust_negative) {
ElfFake elf(memory_);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
interface->MockSetDynamicOffset(0x400);
@@ -510,7 +481,6 @@
TEST_F(ElfTest, get_global_valid_dynamic_adjust_positive) {
ElfFake elf(memory_);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
interface->MockSetDynamicOffset(0x1000);
@@ -530,7 +500,6 @@
TEST_F(ElfTest, is_valid_pc_elf_invalid) {
ElfFake elf(memory_);
elf.FakeSetValid(false);
- elf.FakeSetLoadBias(0);
EXPECT_FALSE(elf.IsValidPc(0x100));
EXPECT_FALSE(elf.IsValidPc(0x200));
@@ -539,7 +508,6 @@
TEST_F(ElfTest, is_valid_pc_interface) {
ElfFake elf(memory_);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
elf.FakeSetInterface(interface);
@@ -549,25 +517,9 @@
EXPECT_TRUE(elf.IsValidPc(0x1500));
}
-TEST_F(ElfTest, is_valid_pc_non_zero_load_bias) {
- ElfFake elf(memory_);
- elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0x1000);
-
- ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
- elf.FakeSetInterface(interface);
-
- EXPECT_CALL(*interface, IsValidPc(0x500)).WillOnce(::testing::Return(true));
-
- EXPECT_FALSE(elf.IsValidPc(0x100));
- EXPECT_FALSE(elf.IsValidPc(0x200));
- EXPECT_TRUE(elf.IsValidPc(0x1500));
-}
-
TEST_F(ElfTest, is_valid_pc_from_gnu_debugdata) {
ElfFake elf(memory_);
elf.FakeSetValid(true);
- elf.FakeSetLoadBias(0);
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
elf.FakeSetInterface(interface);
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index 45a7b58..b40a253 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -70,18 +70,18 @@
std::string name;
uint64_t func_offset;
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
ASSERT_EQ("fake_function", name);
ASSERT_EQ(0U, func_offset);
name.clear();
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x500f, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x500f, &this->memory_, &name, &func_offset));
ASSERT_EQ("fake_function", name);
ASSERT_EQ(0xfU, func_offset);
// Check one before and one after the function.
- ASSERT_FALSE(symbols.GetName<TypeParam>(0x4fff, 0, &this->memory_, &name, &func_offset));
- ASSERT_FALSE(symbols.GetName<TypeParam>(0x5010, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x4fff, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5010, &this->memory_, &name, &func_offset));
}
TYPED_TEST_P(SymbolsTest, no_symbol) {
@@ -98,7 +98,7 @@
// First verify that we can get the name.
std::string name;
uint64_t func_offset;
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
ASSERT_EQ("fake_function", name);
ASSERT_EQ(0U, func_offset);
@@ -107,7 +107,7 @@
this->memory_.SetMemory(offset, &sym, sizeof(sym));
// Clear the cache to force the symbol data to be re-read.
symbols.ClearCache();
- ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
// Set the function back, and set the shndx to UNDEF.
sym.st_info = STT_FUNC;
@@ -115,7 +115,7 @@
this->memory_.SetMemory(offset, &sym, sizeof(sym));
// Clear the cache to force the symbol data to be re-read.
symbols.ClearCache();
- ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
}
TYPED_TEST_P(SymbolsTest, multiple_entries) {
@@ -144,34 +144,34 @@
std::string name;
uint64_t func_offset;
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_two", name);
ASSERT_EQ(1U, func_offset);
name.clear();
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_one", name);
ASSERT_EQ(4U, func_offset);
name.clear();
- ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_three", name);
ASSERT_EQ(1U, func_offset);
// Reget some of the others to verify getting one function name doesn't
// affect any of the next calls.
name.clear();
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x5008, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5008, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_one", name);
ASSERT_EQ(8U, func_offset);
name.clear();
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x3008, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3008, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_two", name);
ASSERT_EQ(4U, func_offset);
name.clear();
- ASSERT_TRUE(symbols.GetName<TypeParam>(0xa01a, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa01a, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_three", name);
ASSERT_EQ(0xaU, func_offset);
}
@@ -203,47 +203,21 @@
std::string name;
uint64_t func_offset;
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_two", name);
ASSERT_EQ(1U, func_offset);
name.clear();
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_one", name);
ASSERT_EQ(4U, func_offset);
name.clear();
- ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, &this->memory_, &name, &func_offset));
ASSERT_EQ("function_three", name);
ASSERT_EQ(1U, func_offset);
}
-TYPED_TEST_P(SymbolsTest, load_bias) {
- Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
-
- TypeParam sym;
- this->InitSym(&sym, 0x5000, 0x10, 0x40);
- uint64_t offset = 0x1000;
- this->memory_.SetMemory(offset, &sym, sizeof(sym));
-
- std::string fake_name("fake_function");
- this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
-
- // Set a non-zero load_bias that should be a valid function offset.
- std::string name;
- uint64_t func_offset;
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
- ASSERT_EQ("fake_function", name);
- ASSERT_EQ(4U, func_offset);
-
- // Set a flag that should cause the load_bias to be ignored.
- sym.st_shndx = SHN_ABS;
- this->memory_.SetMemory(offset, &sym, sizeof(sym));
- // Clear the cache to force the symbol data to be re-read.
- symbols.ClearCache();
- ASSERT_FALSE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
-}
-
TYPED_TEST_P(SymbolsTest, symtab_value_out_of_bounds) {
Symbols symbols_end_at_100(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x100);
Symbols symbols_end_at_200(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x200);
@@ -265,18 +239,16 @@
std::string name;
uint64_t func_offset;
// Verify that we can get the function name properly for both entries.
- ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
ASSERT_EQ("fake_function", name);
ASSERT_EQ(0U, func_offset);
- ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x3000, &this->memory_, &name, &func_offset));
ASSERT_EQ("function", name);
ASSERT_EQ(0U, func_offset);
// Now use the symbol table that ends at 0x100.
- ASSERT_FALSE(
- symbols_end_at_100.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
- ASSERT_FALSE(
- symbols_end_at_100.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols_end_at_100.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols_end_at_100.GetName<TypeParam>(0x3000, &this->memory_, &name, &func_offset));
}
// Verify the entire func table is cached.
@@ -302,9 +274,9 @@
// Do call that should cache all of the entries (except the string data).
std::string name;
uint64_t func_offset;
- ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
this->memory_.Clear();
- ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
// Clear the memory and only put the symbol data string data in memory.
this->memory_.Clear();
@@ -317,15 +289,15 @@
fake_name = "third_entry";
this->memory_.SetMemory(0xa300, fake_name.c_str(), fake_name.size() + 1);
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x5001, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5001, &this->memory_, &name, &func_offset));
ASSERT_EQ("first_entry", name);
ASSERT_EQ(1U, func_offset);
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x2002, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x2002, &this->memory_, &name, &func_offset));
ASSERT_EQ("second_entry", name);
ASSERT_EQ(2U, func_offset);
- ASSERT_TRUE(symbols.GetName<TypeParam>(0x1003, 0, &this->memory_, &name, &func_offset));
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x1003, &this->memory_, &name, &func_offset));
ASSERT_EQ("third_entry", name);
ASSERT_EQ(3U, func_offset);
}
@@ -381,17 +353,17 @@
EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_1", &offset));
std::string name;
- EXPECT_TRUE(symbols.GetName<TypeParam>(0x10002, 0, &this->memory_, &name, &offset));
+ EXPECT_TRUE(symbols.GetName<TypeParam>(0x10002, &this->memory_, &name, &offset));
EXPECT_EQ("function_0", name);
EXPECT_EQ(2U, offset);
- EXPECT_TRUE(symbols.GetName<TypeParam>(0x12004, 0, &this->memory_, &name, &offset));
+ EXPECT_TRUE(symbols.GetName<TypeParam>(0x12004, &this->memory_, &name, &offset));
EXPECT_EQ("function_1", name);
EXPECT_EQ(4U, offset);
}
REGISTER_TYPED_TEST_CASE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
- multiple_entries_nonstandard_size, load_bias, symtab_value_out_of_bounds,
+ multiple_entries_nonstandard_size, symtab_value_out_of_bounds,
symtab_read_cached, get_global);
typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 285fc9e..a65c077 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -1200,4 +1200,43 @@
EXPECT_EQ(0xffcc1558U, unwinder.frames()[18].sp);
}
+// Test using a non-zero load bias library that has the fde entries
+// encoded as 0xb, which is not set as pc relative.
+TEST_F(UnwindOfflineTest, debug_frame_load_bias_arm) {
+ ASSERT_NO_FATAL_FAILURE(Init("debug_frame_load_bias_arm/", ARCH_ARM));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(8U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0005138c libc.so (__ioctl+8)\n"
+ " #01 pc 0002140f libc.so (ioctl+30)\n"
+ " #02 pc 00039535 libbinder.so (_ZN7android14IPCThreadState14talkWithDriverEb+204)\n"
+ " #03 pc 00039633 libbinder.so (_ZN7android14IPCThreadState20getAndExecuteCommandEv+10)\n"
+ " #04 pc 00039b57 libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb+38)\n"
+ " #05 pc 00000c21 mediaserver (main+104)\n"
+ " #06 pc 00084b89 libc.so (__libc_init+48)\n"
+ " #07 pc 00000b77 mediaserver (_start_main+38)\n",
+ frame_info);
+
+ EXPECT_EQ(0xf0be238cU, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xffd4a638U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xf0bb240fU, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xffd4a638U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0xf1a75535U, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xffd4a650U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0xf1a75633U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xffd4a6b0U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xf1a75b57U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xffd4a6d0U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x8d1cc21U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xffd4a6e8U, unwinder.frames()[5].sp);
+ EXPECT_EQ(0xf0c15b89U, unwinder.frames()[6].pc);
+ EXPECT_EQ(0xffd4a700U, unwinder.frames()[6].sp);
+ EXPECT_EQ(0x8d1cb77U, unwinder.frames()[7].pc);
+ EXPECT_EQ(0xffd4a718U, unwinder.frames()[7].sp);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so
new file mode 100644
index 0000000..4b7bf44
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so
new file mode 100644
index 0000000..013858e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt
new file mode 100644
index 0000000..10f1325
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt
@@ -0,0 +1,3 @@
+8d1c000-8d1f000 r-xp 0 00:00 0 mediaserver
+f0b91000-f0c2c000 r-xp 0 00:00 0 libc.so
+f1a41000-f1a97000 r-xp 0 00:00 0 libbinder.so
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver
new file mode 100644
index 0000000..9e4a83f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt
new file mode 100644
index 0000000..f147247
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: 3
+r1: c0306201
+r2: ffd4a658
+r3: 0
+r4: f0c36d8c
+r5: ffd4a658
+r6: f0168000
+r7: 36
+r8: ffd4a678
+r9: f016802c
+r10: ffd4a660
+r11: 0
+ip: 0
+sp: ffd4a638
+lr: f0bb2413
+pc: f0be238c
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data
new file mode 100644
index 0000000..847c819
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 5a8edfd..266a6db 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -37,7 +37,7 @@
namespace unwindstack {
-void DumpArm(ElfInterfaceArm* interface) {
+void DumpArm(Elf* elf, ElfInterfaceArm* interface) {
if (interface == nullptr) {
printf("No ARM Unwind Information.\n\n");
return;
@@ -48,12 +48,11 @@
uint64_t load_bias = entry.second.table_offset;
printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
entry.second.table_size + load_bias);
- for (auto addr : *interface) {
+ for (auto pc : *interface) {
std::string name;
- printf(" PC 0x%" PRIx64, addr + load_bias);
+ printf(" PC 0x%" PRIx64, pc + load_bias);
uint64_t func_offset;
- uint64_t pc = addr + load_bias;
- if (interface->GetFunctionName(pc, load_bias, &name, &func_offset) && !name.empty()) {
+ if (elf->GetFunctionName(pc + load_bias, &name, &func_offset) && !name.empty()) {
printf(" <%s>", name.c_str());
}
printf("\n");
@@ -63,7 +62,7 @@
continue;
}
ArmExidx arm(nullptr, interface->memory(), nullptr);
- arm.set_log(true);
+ arm.set_log(ARM_LOG_FULL);
arm.set_log_skip_execution(true);
arm.set_log_indent(2);
if (!arm.ExtractEntryData(entry)) {
@@ -82,21 +81,21 @@
printf("\n");
}
-void DumpDwarfSection(ElfInterface* interface, DwarfSection* section, uint64_t load_bias) {
+void DumpDwarfSection(Elf* elf, DwarfSection* section, uint64_t) {
for (const DwarfFde* fde : *section) {
// Sometimes there are entries that have empty length, skip those since
// they don't contain any interesting information.
if (fde == nullptr || fde->pc_start == fde->pc_end) {
continue;
}
- printf("\n PC 0x%" PRIx64, fde->pc_start + load_bias);
+ printf("\n PC 0x%" PRIx64 "-0x%" PRIx64, fde->pc_start, fde->pc_end);
std::string name;
uint64_t func_offset;
- if (interface->GetFunctionName(fde->pc_start, load_bias, &name, &func_offset) && !name.empty()) {
+ if (elf->GetFunctionName(fde->pc_start, &name, &func_offset) && !name.empty()) {
printf(" <%s>", name.c_str());
}
printf("\n");
- if (!section->Log(2, UINT64_MAX, load_bias, fde)) {
+ if (!section->Log(2, UINT64_MAX, fde)) {
printf("Failed to process cfa information for entry at 0x%" PRIx64 "\n", fde->pc_start);
}
}
@@ -126,13 +125,13 @@
ElfInterface* interface = elf.interface();
if (elf.machine_type() == EM_ARM) {
- DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface));
+ DumpArm(&elf, reinterpret_cast<ElfInterfaceArm*>(interface));
printf("\n");
}
if (interface->eh_frame() != nullptr) {
printf("eh_frame information:\n");
- DumpDwarfSection(interface, interface->eh_frame(), elf.GetLoadBias());
+ DumpDwarfSection(&elf, interface->eh_frame(), elf.GetLoadBias());
printf("\n");
} else {
printf("\nno eh_frame information\n");
@@ -140,7 +139,7 @@
if (interface->debug_frame() != nullptr) {
printf("\ndebug_frame information:\n");
- DumpDwarfSection(interface, interface->debug_frame(), elf.GetLoadBias());
+ DumpDwarfSection(&elf, interface->debug_frame(), elf.GetLoadBias());
printf("\n");
} else {
printf("\nno debug_frame information\n");
@@ -151,12 +150,12 @@
if (gnu_debugdata_interface != nullptr) {
if (gnu_debugdata_interface->eh_frame() != nullptr) {
printf("\ngnu_debugdata (eh_frame):\n");
- DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->eh_frame(), 0);
+ DumpDwarfSection(&elf, gnu_debugdata_interface->eh_frame(), 0);
printf("\n");
}
if (gnu_debugdata_interface->debug_frame() != nullptr) {
printf("\ngnu_debugdata (debug_frame):\n");
- DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->debug_frame(), 0);
+ DumpDwarfSection(&elf, gnu_debugdata_interface->debug_frame(), 0);
printf("\n");
}
} else {
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index 47a4f91..0f01566 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -34,7 +34,9 @@
#include <unwindstack/ElfInterface.h>
#include <unwindstack/Log.h>
+#include "ArmExidx.h"
#include "DwarfOp.h"
+#include "ElfInterfaceArm.h"
namespace unwindstack {
@@ -136,6 +138,32 @@
}
}
+void PrintArmRegInformation(ElfInterfaceArm* interface, uint64_t pc) {
+ printf("\nArm exidx:\n");
+ uint64_t entry_offset;
+ if (!interface->FindEntry(pc, &entry_offset)) {
+ return;
+ }
+
+ ArmExidx arm(nullptr, interface->memory(), nullptr);
+
+ log_to_stdout(true);
+ arm.set_log(ARM_LOG_BY_REG);
+ arm.set_log_skip_execution(true);
+ arm.set_log_indent(1);
+ if (!arm.ExtractEntryData(entry_offset)) {
+ if (arm.status() != ARM_STATUS_NO_UNWIND) {
+ printf(" Error trying to extract data.\n");
+ }
+ return;
+ }
+ if (arm.data()->size() != 0 && arm.Eval()) {
+ arm.LogByReg();
+ } else {
+ printf(" Error tring to evaluate exidx data.\n");
+ }
+}
+
int GetInfo(const char* file, uint64_t pc) {
MemoryFileAtOffset* memory = new MemoryFileAtOffset;
if (!memory->Init(file, 0)) {
@@ -162,12 +190,22 @@
printf("Soname: %s\n\n", soname.c_str());
}
- printf("PC 0x%" PRIx64 ":\n", pc);
+ printf("PC 0x%" PRIx64, pc);
+ std::string function_name;
+ uint64_t function_offset;
+ if (elf.GetFunctionName(pc, &function_name, &function_offset)) {
+ printf(" (%s)", function_name.c_str());
+ }
+ printf(":\n");
+
+ if (elf.machine_type() == EM_ARM) {
+ PrintArmRegInformation(reinterpret_cast<ElfInterfaceArm*>(interface), pc - load_bias);
+ }
DwarfSection* section = interface->eh_frame();
if (section != nullptr) {
printf("\neh_frame:\n");
- PrintRegInformation(section, memory, pc - load_bias, elf.class_type());
+ PrintRegInformation(section, memory, pc, elf.class_type());
} else {
printf("\nno eh_frame information\n");
}
@@ -175,7 +213,7 @@
section = interface->debug_frame();
if (section != nullptr) {
printf("\ndebug_frame:\n");
- PrintRegInformation(section, memory, pc - load_bias, elf.class_type());
+ PrintRegInformation(section, memory, pc, elf.class_type());
printf("\n");
} else {
printf("\nno debug_frame information\n");
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index 086dffe..f8e3e92 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -95,7 +95,6 @@
}
std::string name;
- uint64_t load_bias = elf.GetLoadBias();
if (argc == 3) {
std::string cur_name;
uint64_t func_offset;
@@ -113,8 +112,8 @@
// This is a crude way to get the symbols in order.
for (const auto& entry : elf.interface()->pt_loads()) {
- uint64_t start = entry.second.offset + load_bias;
- uint64_t end = entry.second.table_size + load_bias;
+ uint64_t start = entry.second.offset;
+ uint64_t end = entry.second.table_size;
for (uint64_t addr = start; addr < end; addr += 4) {
std::string cur_name;
uint64_t func_offset;
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 9395ef8..bbfa9d8 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -47,6 +47,7 @@
cc_defaults {
name: "libutils_defaults",
vendor_available: true,
+ recovery_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -90,6 +91,10 @@
},
},
+ recovery: {
+ exclude_shared_libs: ["libvndksupport"],
+ },
+
host: {
cflags: ["-DLIBUTILS_NATIVE=1"],
diff --git a/libutils/misc.cpp b/libutils/misc.cpp
index da28dfa..f074341 100644
--- a/libutils/misc.cpp
+++ b/libutils/misc.cpp
@@ -23,7 +23,7 @@
#include <utils/Log.h>
#include <utils/Vector.h>
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
#include <dlfcn.h>
#include <vndksupport/linker.h>
#endif
@@ -70,7 +70,7 @@
void add_sysprop_change_callback(sysprop_change_callback, int) {}
#endif
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
void (*get_report_sysprop_change_func())() {
void (*func)() = nullptr;
void* handle = android_load_sphal_library("libutils.so", RTLD_NOW);
@@ -85,7 +85,7 @@
void report_sysprop_change() {
do_report_sysprop_change();
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
// libutils.so is double loaded; from the default namespace and from the
// 'sphal' namespace. Redirect the sysprop change event to the other instance
// of libutils.so loaded in the 'sphal' namespace so that listeners attached
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 6c06618..2606aa9 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -58,6 +58,7 @@
name: "libziparchive",
host_supported: true,
vendor_available: true,
+ recovery_available: true,
vndk: {
enabled: true,
},
diff --git a/logcat/.clang-format b/logcat/.clang-format
deleted file mode 100644
index 393c309..0000000
--- a/logcat/.clang-format
+++ /dev/null
@@ -1,11 +0,0 @@
-BasedOnStyle: Google
-AllowShortFunctionsOnASingleLine: false
-
-CommentPragmas: NOLINT:.*
-DerivePointerAlignment: false
-IndentWidth: 4
-PointerAlignment: Left
-TabWidth: 4
-PenaltyExcessCharacter: 32
-
-Cpp11BracedListStyle: false
diff --git a/logcat/Android.bp b/logcat/Android.bp
index 01beb53..b0563a6 100644
--- a/logcat/Android.bp
+++ b/logcat/Android.bp
@@ -1,5 +1,5 @@
//
-// Copyright (C) 2006-2017 The Android Open Source Project
+// Copyright (C) 2006 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -31,25 +31,13 @@
logtags: ["event.logtags"],
}
-cc_library {
- name: "liblogcat",
-
- defaults: ["logcat_defaults"],
- srcs: [
- "logcat.cpp",
- "getopt_long.cpp",
- "logcat_system.cpp",
- ],
- export_include_dirs: ["include"],
-}
-
cc_binary {
name: "logcat",
defaults: ["logcat_defaults"],
- shared_libs: ["liblogcat"],
srcs: [
"logcat_main.cpp",
+ "logcat.cpp",
],
}
@@ -57,9 +45,9 @@
name: "logcatd",
defaults: ["logcat_defaults"],
- shared_libs: ["liblogcat"],
srcs: [
"logcatd_main.cpp",
+ "logcat.cpp",
],
}
diff --git a/logcat/getopt_long.cpp b/logcat/getopt_long.cpp
deleted file mode 100644
index da99906..0000000
--- a/logcat/getopt_long.cpp
+++ /dev/null
@@ -1,401 +0,0 @@
-/* $OpenBSD: getopt_long.c,v 1.26 2013/06/08 22:47:56 millert Exp $ */
-/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
-
-/*
- * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Sponsored in part by the Defense Advanced Research Projects
- * Agency (DARPA) and Air Force Research Laboratory, Air Force
- * Materiel Command, USAF, under agreement number F39502-99-1-0512.
- */
-/*-
- * Copyright (c) 2000 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Dieter Baron and Thomas Klausner.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/cdefs.h>
-
-#include <log/getopt.h>
-
-#define PRINT_ERROR ((context->opterr) && (*options != ':'))
-
-#define FLAG_PERMUTE 0x01 // permute non-options to the end of argv
-#define FLAG_ALLARGS 0x02 // treat non-options as args to option "-1"
-
-// return values
-#define BADCH (int)'?'
-#define BADARG ((*options == ':') ? (int)':' : (int)'?')
-#define INORDER (int)1
-
-#define D_PREFIX 0
-#define DD_PREFIX 1
-#define W_PREFIX 2
-
-// Compute the greatest common divisor of a and b.
-static int gcd(int a, int b) {
- int c = a % b;
- while (c) {
- a = b;
- b = c;
- c = a % b;
- }
- return b;
-}
-
-// Exchange the block from nonopt_start to nonopt_end with the block from
-// nonopt_end to opt_end (keeping the same order of arguments in each block).
-// Returns optind - (nonopt_end - nonopt_start) for convenience.
-static int permute_args(getopt_context* context, char* const* nargv) {
- // compute lengths of blocks and number and size of cycles
- int nnonopts = context->nonopt_end - context->nonopt_start;
- int nopts = context->optind - context->nonopt_end;
- int ncycle = gcd(nnonopts, nopts);
- int cyclelen = (context->optind - context->nonopt_start) / ncycle;
-
- for (int i = 0; i < ncycle; i++) {
- int cstart = context->nonopt_end + i;
- int pos = cstart;
- for (int j = 0; j < cyclelen; j++) {
- if (pos >= context->nonopt_end) {
- pos -= nnonopts;
- } else {
- pos += nopts;
- }
- char* swap = nargv[pos];
- const_cast<char**>(nargv)[pos] = nargv[cstart];
- const_cast<char**>(nargv)[cstart] = swap;
- }
- }
- return context->optind - (context->nonopt_end - context->nonopt_start);
-}
-
-// parse_long_options_r --
-// Parse long options in argc/argv argument vector.
-// Returns -1 if short_too is set and the option does not match long_options.
-static int parse_long_options_r(char* const* nargv, const char* options,
- const struct option* long_options, int* idx,
- bool short_too, struct getopt_context* context) {
- const char* current_argv = context->place;
- const char* current_dash;
- switch (context->dash_prefix) {
- case D_PREFIX:
- current_dash = "-";
- break;
- case DD_PREFIX:
- current_dash = "--";
- break;
- case W_PREFIX:
- current_dash = "-W ";
- break;
- default:
- current_dash = "";
- break;
- }
- context->optind++;
-
- const char* has_equal;
- size_t current_argv_len;
- if (!!(has_equal = strchr(current_argv, '='))) {
- // argument found (--option=arg)
- current_argv_len = has_equal - current_argv;
- has_equal++;
- } else {
- current_argv_len = strlen(current_argv);
- }
-
- int match = -1;
- bool exact_match = false;
- bool second_partial_match = false;
- for (int i = 0; long_options[i].name; i++) {
- // find matching long option
- if (strncmp(current_argv, long_options[i].name, current_argv_len)) {
- continue;
- }
-
- if (strlen(long_options[i].name) == current_argv_len) {
- // exact match
- match = i;
- exact_match = true;
- break;
- }
- // If this is a known short option, don't allow
- // a partial match of a single character.
- if (short_too && current_argv_len == 1) continue;
-
- if (match == -1) { // first partial match
- match = i;
- } else if (long_options[i].has_arg != long_options[match].has_arg ||
- long_options[i].flag != long_options[match].flag ||
- long_options[i].val != long_options[match].val) {
- second_partial_match = true;
- }
- }
- if (!exact_match && second_partial_match) {
- // ambiguous abbreviation
- if (PRINT_ERROR) {
- fprintf(context->optstderr ?: stderr,
- "option `%s%.*s' is ambiguous", current_dash,
- (int)current_argv_len, current_argv);
- }
- context->optopt = 0;
- return BADCH;
- }
- if (match != -1) { // option found
- if (long_options[match].has_arg == no_argument && has_equal) {
- if (PRINT_ERROR) {
- fprintf(context->optstderr ?: stderr,
- "option `%s%.*s' doesn't allow an argument",
- current_dash, (int)current_argv_len, current_argv);
- }
- // XXX: GNU sets optopt to val regardless of flag
- context->optopt =
- long_options[match].flag ? 0 : long_options[match].val;
- return BADCH;
- }
- if (long_options[match].has_arg == required_argument ||
- long_options[match].has_arg == optional_argument) {
- if (has_equal) {
- context->optarg = has_equal;
- } else if (long_options[match].has_arg == required_argument) {
- // optional argument doesn't use next nargv
- context->optarg = nargv[context->optind++];
- }
- }
- if ((long_options[match].has_arg == required_argument) &&
- !context->optarg) {
- // Missing argument; leading ':' indicates no error
- // should be generated.
- if (PRINT_ERROR) {
- fprintf(context->optstderr ?: stderr,
- "option `%s%s' requires an argument", current_dash,
- current_argv);
- }
- // XXX: GNU sets optopt to val regardless of flag
- context->optopt =
- long_options[match].flag ? 0 : long_options[match].val;
- context->optind--;
- return BADARG;
- }
- } else { // unknown option
- if (short_too) {
- context->optind--;
- return -1;
- }
- if (PRINT_ERROR) {
- fprintf(context->optstderr ?: stderr, "unrecognized option `%s%s'",
- current_dash, current_argv);
- }
- context->optopt = 0;
- return BADCH;
- }
- if (idx) *idx = match;
- if (long_options[match].flag) {
- *long_options[match].flag = long_options[match].val;
- return 0;
- }
- return long_options[match].val;
-}
-
-// getopt_long_r --
-// Parse argc/argv argument vector.
-int getopt_long_r(int nargc, char* const* nargv, const char* options,
- const struct option* long_options, int* idx,
- struct getopt_context* context) {
- if (!options) return -1;
-
- // XXX Some GNU programs (like cvs) set optind to 0 instead of
- // XXX using optreset. Work around this braindamage.
- if (!context->optind) context->optind = context->optreset = 1;
-
- // Disable GNU extensions if options string begins with a '+'.
- int flags = FLAG_PERMUTE;
- if (*options == '-') {
- flags |= FLAG_ALLARGS;
- } else if (*options == '+') {
- flags &= ~FLAG_PERMUTE;
- }
- if (*options == '+' || *options == '-') options++;
-
- context->optarg = nullptr;
- if (context->optreset) context->nonopt_start = context->nonopt_end = -1;
-start:
- if (context->optreset || !*context->place) { // update scanning pointer
- context->optreset = 0;
- if (context->optind >= nargc) { // end of argument vector
- context->place = EMSG;
- if (context->nonopt_end != -1) {
- // do permutation, if we have to
- context->optind = permute_args(context, nargv);
- } else if (context->nonopt_start != -1) {
- // If we skipped non-options, set optind to the first of them.
- context->optind = context->nonopt_start;
- }
- context->nonopt_start = context->nonopt_end = -1;
- return -1;
- }
- if (*(context->place = nargv[context->optind]) != '-' ||
- context->place[1] == '\0') {
- context->place = EMSG; // found non-option
- if (flags & FLAG_ALLARGS) {
- // GNU extension: return non-option as argument to option 1
- context->optarg = nargv[context->optind++];
- return INORDER;
- }
- if (!(flags & FLAG_PERMUTE)) {
- // If no permutation wanted, stop parsing at first non-option.
- return -1;
- }
- // do permutation
- if (context->nonopt_start == -1) {
- context->nonopt_start = context->optind;
- } else if (context->nonopt_end != -1) {
- context->nonopt_start = permute_args(context, nargv);
- context->nonopt_end = -1;
- }
- context->optind++;
- // process next argument
- goto start;
- }
- if (context->nonopt_start != -1 && context->nonopt_end == -1) {
- context->nonopt_end = context->optind;
- }
-
- // If we have "-" do nothing, if "--" we are done.
- if (context->place[1] != '\0' && *++(context->place) == '-' &&
- context->place[1] == '\0') {
- context->optind++;
- context->place = EMSG;
- // We found an option (--), so if we skipped
- // non-options, we have to permute.
- if (context->nonopt_end != -1) {
- context->optind = permute_args(context, nargv);
- }
- context->nonopt_start = context->nonopt_end = -1;
- return -1;
- }
- }
-
- int optchar;
- // Check long options if:
- // 1) we were passed some
- // 2) the arg is not just "-"
- // 3) either the arg starts with -- we are getopt_long_only()
- if (long_options && context->place != nargv[context->optind] &&
- (*context->place == '-')) {
- bool short_too = false;
- context->dash_prefix = D_PREFIX;
- if (*context->place == '-') {
- context->place++; // --foo long option
- context->dash_prefix = DD_PREFIX;
- } else if (*context->place != ':' && strchr(options, *context->place)) {
- short_too = true; // could be short option too
- }
-
- optchar = parse_long_options_r(nargv, options, long_options, idx,
- short_too, context);
- if (optchar != -1) {
- context->place = EMSG;
- return optchar;
- }
- }
-
- const char* oli; // option letter list index
- if ((optchar = (int)*(context->place)++) == (int)':' ||
- (optchar == (int)'-' && *context->place != '\0') ||
- !(oli = strchr(options, optchar))) {
- // If the user specified "-" and '-' isn't listed in
- // options, return -1 (non-option) as per POSIX.
- // Otherwise, it is an unknown option character (or ':').
- if (optchar == (int)'-' && *context->place == '\0') return -1;
- if (!*context->place) context->optind++;
- if (PRINT_ERROR) {
- fprintf(context->optstderr ?: stderr, "invalid option -- %c",
- optchar);
- }
- context->optopt = optchar;
- return BADCH;
- }
-
- static const char recargchar[] = "option requires an argument -- %c";
- if (long_options && optchar == 'W' && oli[1] == ';') {
- // -W long-option
- if (*context->place) { // no space
- ; // NOTHING
- } else if (++(context->optind) >= nargc) { // no arg
- context->place = EMSG;
- if (PRINT_ERROR) {
- fprintf(context->optstderr ?: stderr, recargchar, optchar);
- }
- context->optopt = optchar;
- return BADARG;
- } else { // white space
- context->place = nargv[context->optind];
- }
- context->dash_prefix = W_PREFIX;
- optchar = parse_long_options_r(nargv, options, long_options, idx, false,
- context);
- context->place = EMSG;
- return optchar;
- }
- if (*++oli != ':') { // doesn't take argument
- if (!*context->place) context->optind++;
- } else { // takes (optional) argument
- context->optarg = nullptr;
- if (*context->place) { // no white space
- context->optarg = context->place;
- } else if (oli[1] != ':') { // arg not optional
- if (++(context->optind) >= nargc) { // no arg
- context->place = EMSG;
- if (PRINT_ERROR) {
- fprintf(context->optstderr ?: stderr, recargchar, optchar);
- }
- context->optopt = optchar;
- return BADARG;
- }
- context->optarg = nargv[context->optind];
- }
- context->place = EMSG;
- context->optind++;
- }
- // dump back option letter
- return optchar;
-}
diff --git a/logcat/include/log/getopt.h b/logcat/include/log/getopt.h
deleted file mode 100644
index 0da2b10..0000000
--- a/logcat/include/log/getopt.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LOG_GETOPT_H_
-#define _LOG_GETOPT_H_
-
-#ifndef __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#elif __ANDROID_API__ > 24 /* > Nougat */
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-
-#include <getopt.h>
-#include <sys/cdefs.h>
-
-struct getopt_context {
- int opterr;
- int optind;
- int optopt;
- int optreset;
- const char* optarg;
- FILE* optstderr; /* NULL defaults to stderr */
- /* private */
- const char* place;
- int nonopt_start;
- int nonopt_end;
- int dash_prefix;
- /* expansion space */
- int __extra__;
- void* __stuff__;
-};
-
-#define EMSG ""
-#define NO_PREFIX (-1)
-
-#define INIT_GETOPT_CONTEXT(context) \
- context = { 1, 1, '?', 0, NULL, NULL, EMSG, -1, -1, NO_PREFIX, 0, NULL }
-
-__BEGIN_DECLS
-int getopt_long_r(int nargc, char* const* nargv, const char* options,
- const struct option* long_options, int* idx,
- struct getopt_context* context);
-
-__END_DECLS
-
-#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */
-
-#endif /* !_LOG_GETOPT_H_ */
diff --git a/logcat/include/log/logcat.h b/logcat/include/log/logcat.h
deleted file mode 100644
index 009672c..0000000
--- a/logcat/include/log/logcat.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2005-2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBS_LOGCAT_H /* header boilerplate */
-#define _LIBS_LOGCAT_H
-
-#include <stdio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#elif __ANDROID_API__ > 24 /* > Nougat */
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-
-/* For managing an in-process logcat function, rather than forking/execing
- *
- * It also serves as the basis for the logcat command.
- *
- * The following C API allows a logcat instance to be created, run
- * to completion, and then release all the associated resources.
- */
-
-/*
- * The opaque context
- */
-#ifndef __android_logcat_context_defined /* typedef boilerplate */
-#define __android_logcat_context_defined
-typedef struct android_logcat_context_internal* android_logcat_context;
-#endif
-
-/* Creates a context associated with this logcat instance
- *
- * Returns a pointer to the context, or a NULL on error.
- */
-android_logcat_context create_android_logcat();
-
-/* Collects and outputs the logcat data to output and error file descriptors
- *
- * Will block, performed in-thread and in-process
- *
- * The output file descriptor variable, if greater than or equal to 0, is
- * where the output (ie: stdout) will be sent. The file descriptor is closed
- * on android_logcat_destroy which terminates the instance, or when an -f flag
- * (output redirect to a file) is present in the command. The error file
- * descriptor variable, if greater than or equal to 0, is where the error
- * stream (ie: stderr) will be sent, also closed on android_logcat_destroy.
- * The error file descriptor can be set to equal to the output file descriptor,
- * which will mix output and error stream content, and will defer closure of
- * the file descriptor on -f flag redirection. Negative values for the file
- * descriptors will use stdout and stderr FILE references respectively
- * internally, and will not close the references as noted above.
- *
- * Return value is 0 for success, non-zero for errors.
- */
-int android_logcat_run_command(android_logcat_context ctx, int output, int error,
- int argc, char* const* argv, char* const* envp);
-
-/* Will not block, performed in-process
- *
- * Starts a thread, opens a pipe, returns reading end fd, saves off argv.
- * The command supports 2>&1 (mix content) and 2>/dev/null (drop content) for
- * scripted error (stderr) redirection.
- */
-int android_logcat_run_command_thread(android_logcat_context ctx, int argc,
- char* const* argv, char* const* envp);
-int android_logcat_run_command_thread_running(android_logcat_context ctx);
-
-/* Finished with context
- *
- * Kill the command thread ASAP (if any), and free up all associated resources.
- *
- * Return value is the result of the android_logcat_run_command, or
- * non-zero for any errors.
- */
-int android_logcat_destroy(android_logcat_context* ctx);
-
-/* derived helpers */
-
-/*
- * In-process thread that acts like somewhat like libc-like system and popen
- * respectively. Can not handle shell scripting, only pure calls to the
- * logcat operations. The android_logcat_system is a wrapper for the
- * create_android_logcat, android_logcat_run_command and android_logcat_destroy
- * API above. The android_logcat_popen is a wrapper for the
- * android_logcat_run_command_thread API above. The android_logcat_pclose is
- * a wrapper for a reasonable wait until output has subsided for command
- * completion, fclose on the FILE pointer and the android_logcat_destroy API.
- */
-int android_logcat_system(const char* command);
-FILE* android_logcat_popen(android_logcat_context* ctx, const char* command);
-int android_logcat_pclose(android_logcat_context* ctx, FILE* output);
-
-#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBS_LOGCAT_H */
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index ff85f54..0f56337 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -14,12 +14,15 @@
* limitations under the License.
*/
+#include "logcat.h"
+
#include <arpa/inet.h>
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <getopt.h>
#include <math.h>
#include <pthread.h>
#include <sched.h>
@@ -47,8 +50,6 @@
#include <cutils/sched_policy.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
-#include <log/getopt.h>
-#include <log/logcat.h>
#include <log/logprint.h>
#include <private/android_logger.h>
#include <system/thread_defs.h>
@@ -854,14 +855,8 @@
// net for stability dealing with possible mistaken inputs.
static const char delimiters[] = ",:; \t\n\r\f";
- struct getopt_context optctx;
- INIT_GETOPT_CONTEXT(optctx);
- optctx.opterr = !!context->error;
- optctx.optstderr = context->error;
-
- for (;;) {
- int ret;
-
+ optind = 0;
+ while (true) {
int option_index = 0;
// list of long-argument only strings for later comparison
static const char pid_str[] = "pid";
@@ -902,19 +897,18 @@
};
// clang-format on
- ret = getopt_long_r(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
- long_options, &option_index, &optctx);
- if (ret < 0) break;
+ int c = getopt_long(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
+ &option_index);
+ if (c == -1) break;
- switch (ret) {
+ switch (c) {
case 0:
// only long options
if (long_options[option_index].name == pid_str) {
// ToDo: determine runtime PID_MAX?
- if (!getSizeTArg(optctx.optarg, &pid, 1)) {
+ if (!getSizeTArg(optarg, &pid, 1)) {
logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
- long_options[option_index].name,
- optctx.optarg);
+ long_options[option_index].name, optarg);
goto exit;
}
break;
@@ -924,11 +918,9 @@
ANDROID_LOG_NONBLOCK;
// ToDo: implement API that supports setting a wrap timeout
size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
- if (optctx.optarg &&
- !getSizeTArg(optctx.optarg, &dummy, 1)) {
+ if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
- long_options[option_index].name,
- optctx.optarg);
+ long_options[option_index].name, optarg);
goto exit;
}
if ((dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) &&
@@ -949,8 +941,7 @@
break;
}
if (long_options[option_index].name == id_str) {
- setId = (optctx.optarg && optctx.optarg[0]) ? optctx.optarg
- : nullptr;
+ setId = (optarg && optarg[0]) ? optarg : nullptr;
}
break;
@@ -978,32 +969,27 @@
mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
// FALLTHRU
case 'T':
- if (strspn(optctx.optarg, "0123456789") !=
- strlen(optctx.optarg)) {
- char* cp = parseTime(tail_time, optctx.optarg);
+ if (strspn(optarg, "0123456789") != strlen(optarg)) {
+ char* cp = parseTime(tail_time, optarg);
if (!cp) {
- logcat_panic(context, HELP_FALSE,
- "-%c \"%s\" not in time format\n", ret,
- optctx.optarg);
+ logcat_panic(context, HELP_FALSE, "-%c \"%s\" not in time format\n", c,
+ optarg);
goto exit;
}
if (*cp) {
- char c = *cp;
+ char ch = *cp;
*cp = '\0';
if (context->error) {
- fprintf(
- context->error,
- "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
- ret, optctx.optarg, c, cp + 1);
+ fprintf(context->error, "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
+ c, optarg, ch, cp + 1);
}
- *cp = c;
+ *cp = ch;
}
} else {
- if (!getSizeTArg(optctx.optarg, &tail_lines, 1)) {
+ if (!getSizeTArg(optarg, &tail_lines, 1)) {
if (context->error) {
- fprintf(context->error,
- "WARNING: -%c %s invalid, setting to 1\n",
- ret, optctx.optarg);
+ fprintf(context->error, "WARNING: -%c %s invalid, setting to 1\n", c,
+ optarg);
}
tail_lines = 1;
}
@@ -1015,21 +1001,19 @@
break;
case 'e':
- context->regex = new pcrecpp::RE(optctx.optarg);
+ context->regex = new pcrecpp::RE(optarg);
break;
case 'm': {
- if (!getSizeTArg(optctx.optarg, &context->maxCount)) {
+ if (!getSizeTArg(optarg, &context->maxCount)) {
logcat_panic(context, HELP_FALSE,
- "-%c \"%s\" isn't an "
- "integer greater than zero\n",
- ret, optctx.optarg);
+ "-%c \"%s\" isn't an integer greater than zero\n", c, optarg);
goto exit;
}
} break;
case 'g':
- if (!optctx.optarg) {
+ if (!optarg) {
getLogSize = true;
break;
}
@@ -1037,8 +1021,8 @@
case 'G': {
char* cp;
- if (strtoll(optctx.optarg, &cp, 0) > 0) {
- setLogSize = strtoll(optctx.optarg, &cp, 0);
+ if (strtoll(optarg, &cp, 0) > 0) {
+ setLogSize = strtoll(optarg, &cp, 0);
} else {
setLogSize = 0;
}
@@ -1071,19 +1055,18 @@
} break;
case 'p':
- if (!optctx.optarg) {
+ if (!optarg) {
getPruneList = true;
break;
}
// FALLTHRU
case 'P':
- setPruneList = optctx.optarg;
+ setPruneList = optarg;
break;
case 'b': {
- std::unique_ptr<char, void (*)(void*)> buffers(
- strdup(optctx.optarg), free);
+ std::unique_ptr<char, void (*)(void*)> buffers(strdup(optarg), free);
char* arg = buffers.get();
unsigned idMask = 0;
char* sv = nullptr; // protect against -ENOMEM above
@@ -1147,40 +1130,33 @@
case 'f':
if ((tail_time == log_time::EPOCH) && !tail_lines) {
- tail_time = lastLogTime(optctx.optarg);
+ tail_time = lastLogTime(optarg);
}
// redirect output to a file
- context->outputFileName = optctx.optarg;
+ context->outputFileName = optarg;
break;
case 'r':
- if (!getSizeTArg(optctx.optarg, &context->logRotateSizeKBytes,
- 1)) {
- logcat_panic(context, HELP_TRUE,
- "Invalid parameter \"%s\" to -r\n",
- optctx.optarg);
+ if (!getSizeTArg(optarg, &context->logRotateSizeKBytes, 1)) {
+ logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -r\n", optarg);
goto exit;
}
break;
case 'n':
- if (!getSizeTArg(optctx.optarg, &context->maxRotatedLogs, 1)) {
- logcat_panic(context, HELP_TRUE,
- "Invalid parameter \"%s\" to -n\n",
- optctx.optarg);
+ if (!getSizeTArg(optarg, &context->maxRotatedLogs, 1)) {
+ logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -n\n", optarg);
goto exit;
}
break;
case 'v': {
- if (!strcmp(optctx.optarg, "help") ||
- !strcmp(optctx.optarg, "--help")) {
+ if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
show_format_help(context);
context->retval = EXIT_SUCCESS;
goto exit;
}
- std::unique_ptr<char, void (*)(void*)> formats(
- strdup(optctx.optarg), free);
+ std::unique_ptr<char, void (*)(void*)> formats(strdup(optarg), free);
char* arg = formats.get();
char* sv = nullptr; // protect against -ENOMEM above
while (!!(arg = strtok_r(arg, delimiters, &sv))) {
@@ -1300,8 +1276,7 @@
break;
case ':':
- logcat_panic(context, HELP_TRUE,
- "Option -%c needs an argument\n", optctx.optopt);
+ logcat_panic(context, HELP_TRUE, "Option -%c needs an argument\n", optopt);
goto exit;
case 'h':
@@ -1310,8 +1285,7 @@
goto exit;
default:
- logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n",
- optctx.optopt);
+ logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n", optopt);
goto exit;
}
}
@@ -1400,7 +1374,7 @@
"Invalid filter expression in logcat args\n");
goto exit;
}
- } else if (argc == optctx.optind) {
+ } else if (argc == optind) {
// Add from environment variable
const char* env_tags_orig = android::getenv(context, "ANDROID_LOG_TAGS");
@@ -1416,7 +1390,7 @@
}
} else {
// Add from commandline
- for (int i = optctx.optind ; i < argc ; i++) {
+ for (int i = optind ; i < argc ; i++) {
// skip stderr redirections of _all_ kinds
if ((argv[i][0] == '2') && (argv[i][1] == '>')) continue;
// skip stdout redirections of _all_ kinds
@@ -1707,105 +1681,6 @@
return __logcat(context);
}
-// starts a thread, opens a pipe, returns reading end.
-int android_logcat_run_command_thread(android_logcat_context ctx,
- int argc, char* const* argv,
- char* const* envp) {
- android_logcat_context_internal* context = ctx;
-
- int save_errno = EBUSY;
- if ((context->fds[0] >= 0) || (context->fds[1] >= 0)) goto exit;
-
- if (pipe(context->fds) < 0) {
- save_errno = errno;
- goto exit;
- }
-
- pthread_attr_t attr;
- if (pthread_attr_init(&attr)) {
- save_errno = errno;
- goto close_exit;
- }
-
- struct sched_param param;
- memset(¶m, 0, sizeof(param));
- pthread_attr_setschedparam(&attr, ¶m);
- pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
- if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
- save_errno = errno;
- goto pthread_attr_exit;
- }
-
- context->stop = false;
- context->thread_stopped = false;
- context->output_fd = context->fds[1];
- // save off arguments so they remain while thread is active.
- for (int i = 0; i < argc; ++i) {
- context->args.push_back(std::string(argv[i]));
- }
- // save off environment so they remain while thread is active.
- if (envp) for (size_t i = 0; envp[i]; ++i) {
- context->envs.push_back(std::string(envp[i]));
- }
-
- for (auto& str : context->args) {
- context->argv_hold.push_back(str.c_str());
- }
- context->argv_hold.push_back(nullptr);
- for (auto& str : context->envs) {
- context->envp_hold.push_back(str.c_str());
- }
- context->envp_hold.push_back(nullptr);
-
- context->argc = context->argv_hold.size() - 1;
- context->argv = (char* const*)&context->argv_hold[0];
- context->envp = (char* const*)&context->envp_hold[0];
-
-#ifdef DEBUG
- fprintf(stderr, "argv[%d] = {", context->argc);
- for (auto str : context->argv_hold) {
- fprintf(stderr, " \"%s\"", str ?: "nullptr");
- }
- fprintf(stderr, " }\n");
- fflush(stderr);
-#endif
- context->retval = EXIT_SUCCESS;
- if (pthread_create(&context->thr, &attr,
- (void*(*)(void*))__logcat, context)) {
- save_errno = errno;
- goto argv_exit;
- }
- pthread_attr_destroy(&attr);
-
- return context->fds[0];
-
-argv_exit:
- context->argv_hold.clear();
- context->args.clear();
- context->envp_hold.clear();
- context->envs.clear();
-pthread_attr_exit:
- pthread_attr_destroy(&attr);
-close_exit:
- close(context->fds[0]);
- context->fds[0] = -1;
- close(context->fds[1]);
- context->fds[1] = -1;
-exit:
- errno = save_errno;
- context->stop = true;
- context->thread_stopped = true;
- context->retval = EXIT_FAILURE;
- return -1;
-}
-
-// test if the thread is still doing 'stuff'
-int android_logcat_run_command_thread_running(android_logcat_context ctx) {
- android_logcat_context_internal* context = ctx;
-
- return context->thread_stopped == false;
-}
-
// Finished with context
int android_logcat_destroy(android_logcat_context* ctx) {
android_logcat_context_internal* context = *ctx;
diff --git a/logcat/logcat.h b/logcat/logcat.h
new file mode 100644
index 0000000..85ed7da
--- /dev/null
+++ b/logcat/logcat.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdio.h>
+
+/*
+ * The opaque context
+ */
+typedef struct android_logcat_context_internal* android_logcat_context;
+
+/* Creates a context associated with this logcat instance
+ *
+ * Returns a pointer to the context, or a NULL on error.
+ */
+android_logcat_context create_android_logcat();
+
+/* Collects and outputs the logcat data to output and error file descriptors
+ *
+ * Will block, performed in-thread and in-process
+ *
+ * The output file descriptor variable, if greater than or equal to 0, is
+ * where the output (ie: stdout) will be sent. The file descriptor is closed
+ * on android_logcat_destroy which terminates the instance, or when an -f flag
+ * (output redirect to a file) is present in the command. The error file
+ * descriptor variable, if greater than or equal to 0, is where the error
+ * stream (ie: stderr) will be sent, also closed on android_logcat_destroy.
+ * The error file descriptor can be set to equal to the output file descriptor,
+ * which will mix output and error stream content, and will defer closure of
+ * the file descriptor on -f flag redirection. Negative values for the file
+ * descriptors will use stdout and stderr FILE references respectively
+ * internally, and will not close the references as noted above.
+ *
+ * Return value is 0 for success, non-zero for errors.
+ */
+int android_logcat_run_command(android_logcat_context ctx, int output, int error, int argc,
+ char* const* argv, char* const* envp);
+
+/* Finished with context
+ *
+ * Kill the command thread ASAP (if any), and free up all associated resources.
+ *
+ * Return value is the result of the android_logcat_run_command, or
+ * non-zero for any errors.
+ */
+int android_logcat_destroy(android_logcat_context* ctx);
diff --git a/logcat/logcat_main.cpp b/logcat/logcat_main.cpp
index 9477e79..ecfa2ba 100644
--- a/logcat/logcat_main.cpp
+++ b/logcat/logcat_main.cpp
@@ -17,7 +17,7 @@
#include <signal.h>
#include <stdlib.h>
-#include <log/logcat.h>
+#include "logcat.h"
int main(int argc, char** argv, char** envp) {
android_logcat_context ctx = create_android_logcat();
diff --git a/logcat/logcat_system.cpp b/logcat/logcat_system.cpp
deleted file mode 100644
index 6dfd110..0000000
--- a/logcat/logcat_system.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <ctype.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <string>
-#include <vector>
-
-#include <log/logcat.h>
-
-static std::string unquote(const char*& cp, const char*& delim) {
- if ((*cp == '\'') || (*cp == '"')) {
- // KISS: Simple quotes. Do not handle the case
- // of concatenation like "blah"foo'bar'
- char quote = *cp++;
- delim = strchr(cp, quote);
- if (!delim) delim = cp + strlen(cp);
- std::string str(cp, delim);
- if (*delim) ++delim;
- return str;
- }
- delim = strpbrk(cp, " \t\f\r\n");
- if (!delim) delim = cp + strlen(cp);
- return std::string(cp, delim);
-}
-
-static bool __android_logcat_parse(const char* command,
- std::vector<std::string>& args,
- std::vector<std::string>& envs) {
- for (const char *delim, *cp = command; cp && *cp; cp = delim) {
- while (isspace(*cp)) ++cp;
- if ((args.size() == 0) && (*cp != '=') && !isdigit(*cp)) {
- const char* env = cp;
- while (isalnum(*cp) || (*cp == '_')) ++cp;
- if (cp && (*cp == '=')) {
- std::string str(env, ++cp);
- str += unquote(cp, delim);
- envs.push_back(str);
- continue;
- }
- cp = env;
- }
- args.push_back(unquote(cp, delim));
- if ((args.size() == 1) && (args[0] != "logcat") &&
- (args[0] != "/system/bin/logcat")) {
- return false;
- }
- }
- return args.size() != 0;
-}
-
-FILE* android_logcat_popen(android_logcat_context* ctx, const char* command) {
- *ctx = NULL;
-
- std::vector<std::string> args;
- std::vector<std::string> envs;
- if (!__android_logcat_parse(command, args, envs)) return NULL;
-
- std::vector<const char*> argv;
- for (auto& str : args) {
- argv.push_back(str.c_str());
- }
- argv.push_back(NULL);
-
- std::vector<const char*> envp;
- for (auto& str : envs) {
- envp.push_back(str.c_str());
- }
- envp.push_back(NULL);
-
- *ctx = create_android_logcat();
- if (!*ctx) return NULL;
-
- int fd = android_logcat_run_command_thread(
- *ctx, argv.size() - 1, (char* const*)&argv[0], (char* const*)&envp[0]);
- argv.clear();
- args.clear();
- envp.clear();
- envs.clear();
- if (fd < 0) {
- android_logcat_destroy(ctx);
- return NULL;
- }
-
- int duped = dup(fd);
- FILE* retval = fdopen(duped, "reb");
- if (!retval) {
- close(duped);
- android_logcat_destroy(ctx);
- }
- return retval;
-}
-
-int android_logcat_pclose(android_logcat_context* ctx, FILE* output) {
- if (*ctx) {
- static const useconds_t wait_sample = 20000;
- // Wait two seconds maximum
- for (size_t retry = ((2 * 1000000) + wait_sample - 1) / wait_sample;
- android_logcat_run_command_thread_running(*ctx) && retry; --retry) {
- usleep(wait_sample);
- }
- }
-
- if (output) fclose(output);
- return android_logcat_destroy(ctx);
-}
-
-int android_logcat_system(const char* command) {
- std::vector<std::string> args;
- std::vector<std::string> envs;
- if (!__android_logcat_parse(command, args, envs)) return -1;
-
- std::vector<const char*> argv;
- for (auto& str : args) {
- argv.push_back(str.c_str());
- }
- argv.push_back(NULL);
-
- std::vector<const char*> envp;
- for (auto& str : envs) {
- envp.push_back(str.c_str());
- }
- envp.push_back(NULL);
-
- android_logcat_context ctx = create_android_logcat();
- if (!ctx) return -1;
- /* Command return value */
- int retval = android_logcat_run_command(ctx, -1, -1, argv.size() - 1,
- (char* const*)&argv[0],
- (char* const*)&envp[0]);
- /* destroy return value */
- int ret = android_logcat_destroy(&ctx);
- /* Paranoia merging any discrepancies between the two return values */
- if (!ret) ret = retval;
- return ret;
-}
diff --git a/logcat/logcatd_main.cpp b/logcat/logcatd_main.cpp
index 9109eb1..c131846 100644
--- a/logcat/logcatd_main.cpp
+++ b/logcat/logcatd_main.cpp
@@ -21,7 +21,7 @@
#include <string>
#include <vector>
-#include <log/logcat.h>
+#include "logcat.h"
int main(int argc, char** argv, char** envp) {
android_logcat_context ctx = create_android_logcat();
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index defd3c4..66f6724 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -32,7 +32,6 @@
benchmark_src_files := \
logcat_benchmark.cpp \
- exec_benchmark.cpp \
# Build benchmarks for the device. Run with:
# adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
@@ -41,7 +40,7 @@
LOCAL_MODULE_TAGS := $(test_tags)
LOCAL_CFLAGS += $(test_c_flags)
LOCAL_SRC_FILES := $(benchmark_src_files)
-LOCAL_SHARED_LIBRARIES := libbase liblogcat
+LOCAL_SHARED_LIBRARIES := libbase
include $(BUILD_NATIVE_BENCHMARK)
# -----------------------------------------------------------------------------
@@ -51,7 +50,6 @@
test_src_files := \
logcat_test.cpp \
logcatd_test.cpp \
- liblogcat_test.cpp \
# Build tests for the device (with .so). Run with:
# adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
@@ -59,6 +57,6 @@
LOCAL_MODULE := $(test_module_prefix)unit-tests
LOCAL_MODULE_TAGS := $(test_tags)
LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libbase liblogcat
+LOCAL_SHARED_LIBRARIES := liblog libbase
LOCAL_SRC_FILES := $(test_src_files)
include $(BUILD_NATIVE_TEST)
diff --git a/logcat/tests/exec_benchmark.cpp b/logcat/tests/exec_benchmark.cpp
deleted file mode 100644
index c30a5f5..0000000
--- a/logcat/tests/exec_benchmark.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-
-#include <android-base/file.h>
-#include <benchmark/benchmark.h>
-#include <log/logcat.h>
-
-// Dump the statistics and report results
-
-static void logcat_popen_libc(benchmark::State& state, const char* cmd) {
- while (state.KeepRunning()) {
- FILE* fp = popen(cmd, "r");
- std::string ret;
- android::base::ReadFdToString(fileno(fp), &ret);
- pclose(fp);
- }
-}
-
-static void BM_logcat_stat_popen_libc(benchmark::State& state) {
- logcat_popen_libc(state, "logcat -b all -S");
-}
-BENCHMARK(BM_logcat_stat_popen_libc);
-
-static void logcat_popen_liblogcat(benchmark::State& state, const char* cmd) {
- while (state.KeepRunning()) {
- android_logcat_context ctx;
- FILE* fp = android_logcat_popen(&ctx, cmd);
- std::string ret;
- android::base::ReadFdToString(fileno(fp), &ret);
- android_logcat_pclose(&ctx, fp);
- }
-}
-
-static void BM_logcat_stat_popen_liblogcat(benchmark::State& state) {
- logcat_popen_liblogcat(state, "logcat -b all -S");
-}
-BENCHMARK(BM_logcat_stat_popen_liblogcat);
-
-static void logcat_system_libc(benchmark::State& state, const char* cmd) {
- while (state.KeepRunning()) {
- system(cmd);
- }
-}
-
-static void BM_logcat_stat_system_libc(benchmark::State& state) {
- logcat_system_libc(state, "logcat -b all -S >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_stat_system_libc);
-
-static void logcat_system_liblogcat(benchmark::State& state, const char* cmd) {
- while (state.KeepRunning()) {
- android_logcat_system(cmd);
- }
-}
-
-static void BM_logcat_stat_system_liblogcat(benchmark::State& state) {
- logcat_system_liblogcat(state, "logcat -b all -S >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_stat_system_liblogcat);
-
-// Dump the logs and report results
-
-static void BM_logcat_dump_popen_libc(benchmark::State& state) {
- logcat_popen_libc(state, "logcat -b all -d");
-}
-BENCHMARK(BM_logcat_dump_popen_libc);
-
-static void BM_logcat_dump_popen_liblogcat(benchmark::State& state) {
- logcat_popen_liblogcat(state, "logcat -b all -d");
-}
-BENCHMARK(BM_logcat_dump_popen_liblogcat);
-
-static void BM_logcat_dump_system_libc(benchmark::State& state) {
- logcat_system_libc(state, "logcat -b all -d >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_dump_system_libc);
-
-static void BM_logcat_dump_system_liblogcat(benchmark::State& state) {
- logcat_system_liblogcat(state, "logcat -b all -d >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_dump_system_liblogcat);
diff --git a/logcat/tests/liblogcat_test.cpp b/logcat/tests/liblogcat_test.cpp
deleted file mode 100644
index c8a00da..0000000
--- a/logcat/tests/liblogcat_test.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <log/logcat.h>
-
-#define logcat_define(context) android_logcat_context context
-#define logcat_popen(context, command) android_logcat_popen(&(context), command)
-#define logcat_pclose(context, fp) android_logcat_pclose(&(context), fp)
-#define logcat_system(command) android_logcat_system(command)
-#define logcat liblogcat
-
-#include "logcat_test.cpp"
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 786fb14..cc1632a 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -37,12 +37,6 @@
#include <log/log.h>
#include <log/log_event_list.h>
-#ifndef logcat_popen
-#define logcat_define(context)
-#define logcat_popen(context, command) popen((command), "r")
-#define logcat_pclose(context, fp) pclose(fp)
-#define logcat_system(command) system(command)
-#endif
#ifndef logcat_executable
#define USING_LOGCAT_EXECUTABLE_DEFAULT
#define logcat_executable "logcat"
@@ -78,7 +72,6 @@
TEST(logcat, buckets) {
FILE* fp;
- logcat_define(ctx);
#undef LOG_TAG
#define LOG_TAG "inject.buckets"
@@ -90,10 +83,9 @@
__android_log_bswrite(0, logcat_executable ".inject.buckets");
rest();
- ASSERT_TRUE(NULL !=
- (fp = logcat_popen(
- ctx, logcat_executable
- " -b radio -b events -b system -b main -d 2>/dev/null")));
+ ASSERT_TRUE(NULL != (fp = popen(logcat_executable
+ " -b radio -b events -b system -b main -d 2>/dev/null",
+ "r")));
char buffer[BIG_BUFFER];
@@ -111,7 +103,7 @@
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
EXPECT_EQ(ids, 15);
@@ -120,7 +112,6 @@
TEST(logcat, event_tag_filter) {
FILE* fp;
- logcat_define(ctx);
#undef LOG_TAG
#define LOG_TAG "inject.filter"
@@ -135,7 +126,7 @@
logcat_executable
" -b radio -b system -b main --pid=%d -d -s inject.filter 2>/dev/null",
getpid());
- ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, command.c_str())));
+ ASSERT_TRUE(NULL != (fp = popen(command.c_str(), "r")));
char buffer[BIG_BUFFER];
@@ -145,7 +136,7 @@
if (strncmp(begin, buffer, sizeof(begin) - 1)) ++count;
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
// logcat, liblogcat and logcatd test instances result in the progression
// of 3, 6 and 9 for our counts as each round is performed.
@@ -191,7 +182,6 @@
do {
FILE* fp;
- logcat_define(ctx);
char needle[32];
time_t now;
@@ -205,9 +195,8 @@
#endif
strftime(needle, sizeof(needle), "[ %Y-", ptm);
- ASSERT_TRUE(NULL != (fp = logcat_popen(
- ctx, logcat_executable
- " -v long -v year -b all -t 3 2>/dev/null")));
+ ASSERT_TRUE(NULL !=
+ (fp = popen(logcat_executable " -v long -v year -b all -t 3 2>/dev/null", "r")));
char buffer[BIG_BUFFER];
@@ -218,7 +207,7 @@
++count;
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
} while ((count < 3) && --tries && inject(3 - count));
@@ -268,12 +257,10 @@
do {
FILE* fp;
- logcat_define(ctx);
- ASSERT_TRUE(NULL !=
- (fp = logcat_popen(ctx, logcat_executable
- " -v long -v America/Los_Angeles "
- "-b all -t 3 2>/dev/null")));
+ ASSERT_TRUE(NULL != (fp = popen(logcat_executable
+ " -v long -v America/Los_Angeles -b all -t 3 2>/dev/null",
+ "r")));
char buffer[BIG_BUFFER];
@@ -287,7 +274,7 @@
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
} while ((count < 3) && --tries && inject(3 - count));
@@ -296,11 +283,11 @@
TEST(logcat, ntz) {
FILE* fp;
- logcat_define(ctx);
- ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, logcat_executable
- " -v long -v America/Los_Angeles -v "
- "zone -b all -t 3 2>/dev/null")));
+ ASSERT_TRUE(NULL !=
+ (fp = popen(logcat_executable
+ " -v long -v America/Los_Angeles -v zone -b all -t 3 2>/dev/null",
+ "r")));
char buffer[BIG_BUFFER];
@@ -312,7 +299,7 @@
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
ASSERT_EQ(0, count);
}
@@ -330,8 +317,7 @@
"ANDROID_PRINTF_LOG=long logcat -b all -t %d 2>/dev/null", num);
FILE* fp;
- logcat_define(ctx);
- ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
count = 0;
@@ -339,7 +325,7 @@
++count;
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
} while ((count < num) && --tries && inject(num - count));
@@ -377,8 +363,7 @@
do {
snprintf(buffer, sizeof(buffer), "%s -t 10 2>&1", cmd);
- logcat_define(ctx);
- ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
count = 0;
while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
@@ -391,7 +376,7 @@
free(last_timestamp);
last_timestamp = strdup(input);
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
} while ((count < 10) && --tries && inject(10 - count));
@@ -401,8 +386,7 @@
EXPECT_TRUE(second_timestamp != NULL);
snprintf(buffer, sizeof(buffer), "%s -t '%s' 2>&1", cmd, first_timestamp);
- logcat_define(ctx);
- ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
int second_count = 0;
int last_timestamp_count = -1;
@@ -442,7 +426,7 @@
last_timestamp_count = second_count;
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
EXPECT_TRUE(found);
if (!found) {
@@ -483,10 +467,8 @@
ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
FILE* fp;
- logcat_define(ctx);
ASSERT_TRUE(NULL !=
- (fp = logcat_popen(ctx, logcat_executable
- " -v brief -b events -t 100 2>/dev/null")));
+ (fp = popen(logcat_executable " -v brief -b events -t 100 2>/dev/null", "r")));
char buffer[BIG_BUFFER];
@@ -507,7 +489,7 @@
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
ASSERT_EQ(1, count);
}
@@ -521,12 +503,9 @@
FILE* fp[256]; // does this count as a multitude!
memset(fp, 0, sizeof(fp));
- logcat_define(ctx[sizeof(fp) / sizeof(fp[0])]);
size_t num = 0;
do {
- EXPECT_TRUE(NULL !=
- (fp[num] = logcat_popen(ctx[num], logcat_executable
- " -v brief -b events -t 100")));
+ EXPECT_TRUE(NULL != (fp[num] = popen(logcat_executable " -v brief -b events -t 100", "r")));
if (!fp[num]) {
fprintf(stderr,
"WARNING: limiting to %zu simultaneous logcat operations\n",
@@ -556,7 +535,7 @@
}
}
- logcat_pclose(ctx[idx], fp[idx]);
+ pclose(fp[idx]);
}
ASSERT_EQ(num, count);
@@ -564,10 +543,9 @@
static int get_groups(const char* cmd) {
FILE* fp;
- logcat_define(ctx);
// NB: crash log only available in user space
- EXPECT_TRUE(NULL != (fp = logcat_popen(ctx, cmd)));
+ EXPECT_TRUE(NULL != (fp = popen(cmd, "r")));
if (fp == NULL) {
return 0;
@@ -631,7 +609,7 @@
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
return count;
}
@@ -815,7 +793,7 @@
snprintf(command, sizeof(command), comm, buf);
int ret;
- EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
if (!ret) {
snprintf(command, sizeof(command), "ls -s %s 2>/dev/null", buf);
@@ -861,7 +839,7 @@
snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
int ret;
- EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
if (!ret) {
snprintf(command, sizeof(command), "ls %s 2>/dev/null", tmp_out_dir);
@@ -920,7 +898,7 @@
snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
int ret;
- EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
if (ret) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(IsFalse(system(command), command));
@@ -969,7 +947,7 @@
// re-run the command, it should only add a few lines more content if it
// continues where it left off.
snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
- EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
if (ret) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(IsFalse(system(command), command));
@@ -1052,7 +1030,7 @@
tmp_out_dir, log_filename, num_val);
int ret;
- EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
if (ret) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(IsFalse(system(command), command));
@@ -1082,7 +1060,7 @@
strcat(command, clear_cmd);
int ret;
- EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
if (ret) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(system(command));
@@ -1120,7 +1098,7 @@
snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
- int ret = logcat_system(command);
+ int ret = system(command);
if (ret) {
fprintf(stderr, "system(\"%s\")=%d", command, ret);
return -1;
@@ -1194,7 +1172,7 @@
" -b all -d"
" -f /das/nein/gerfingerpoken/logcat/log.txt"
" -n 256 -r 1024";
- EXPECT_FALSE(IsFalse(0 == logcat_system(command), command));
+ EXPECT_FALSE(IsFalse(0 == system(command), command));
}
#ifndef logcat
@@ -1329,10 +1307,7 @@
#endif
static bool get_white_black(char** list) {
- FILE* fp;
- logcat_define(ctx);
-
- fp = logcat_popen(ctx, logcat_executable " -p 2>/dev/null");
+ FILE* fp = popen(logcat_executable " -p 2>/dev/null", "r");
if (fp == NULL) {
fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
return false;
@@ -1360,19 +1335,15 @@
asprintf(list, "%s", buf);
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
return *list != NULL;
}
static bool set_white_black(const char* list) {
- FILE* fp;
- logcat_define(ctx);
-
char buffer[BIG_BUFFER];
-
snprintf(buffer, sizeof(buffer), logcat_executable " -P '%s' 2>&1",
list ? list : "");
- fp = logcat_popen(ctx, buffer);
+ FILE* fp = popen(buffer, "r");
if (fp == NULL) {
fprintf(stderr, "ERROR: %s\n", buffer);
return false;
@@ -1391,10 +1362,10 @@
continue;
}
fprintf(stderr, "%s\n", buf);
- logcat_pclose(ctx, fp);
+ pclose(fp);
return false;
}
- return logcat_pclose(ctx, fp) == 0;
+ return pclose(fp) == 0;
}
TEST(logcat, white_black_adjust) {
@@ -1429,7 +1400,6 @@
TEST(logcat, regex) {
FILE* fp;
- logcat_define(ctx);
int count = 0;
char buffer[BIG_BUFFER];
@@ -1450,7 +1420,7 @@
// Let the logs settle
rest();
- ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
while (fgets(buffer, sizeof(buffer), fp)) {
if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
@@ -1462,14 +1432,13 @@
count++;
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
ASSERT_EQ(2, count);
}
TEST(logcat, maxcount) {
FILE* fp;
- logcat_define(ctx);
int count = 0;
char buffer[BIG_BUFFER];
@@ -1488,7 +1457,7 @@
rest();
- ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
while (fgets(buffer, sizeof(buffer), fp)) {
if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
@@ -1498,7 +1467,7 @@
count++;
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
ASSERT_EQ(3, count);
}
@@ -1510,13 +1479,7 @@
;
static bool End_to_End(const char* tag, const char* fmt, ...) {
- logcat_define(ctx);
- FILE* fp = logcat_popen(ctx, logcat_executable
- " -v brief"
- " -b events"
- " -v descriptive"
- " -t 100"
- " 2>/dev/null");
+ FILE* fp = popen(logcat_executable " -v brief -b events -v descriptive -t 100 2>/dev/null", "r");
if (!fp) {
fprintf(stderr, "End_to_End: popen failed");
return false;
@@ -1551,7 +1514,7 @@
}
}
- logcat_pclose(ctx, fp);
+ pclose(fp);
if ((count == 0) && (lastMatch.length() > 0)) {
// Help us pinpoint where things went wrong ...
@@ -1741,13 +1704,12 @@
}
static bool reportedSecurity(const char* command) {
- logcat_define(ctx);
- FILE* fp = logcat_popen(ctx, command);
+ FILE* fp = popen(command, "r");
if (!fp) return true;
std::string ret;
bool val = android::base::ReadFdToString(fileno(fp), &ret);
- logcat_pclose(ctx, fp);
+ pclose(fp);
if (!val) return true;
return std::string::npos != ret.find("'security'");
@@ -1762,13 +1724,12 @@
}
static size_t commandOutputSize(const char* command) {
- logcat_define(ctx);
- FILE* fp = logcat_popen(ctx, command);
+ FILE* fp = popen(command, "r");
if (!fp) return 0;
std::string ret;
if (!android::base::ReadFdToString(fileno(fp), &ret)) return 0;
- if (logcat_pclose(ctx, fp) != 0) return 0;
+ if (pclose(fp) != 0) return 0;
return ret.size();
}
diff --git a/logd/logd.rc b/logd/logd.rc
index bd303b7..c740ecf 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -6,6 +6,7 @@
file /dev/kmsg w
user logd
group logd system package_info readproc
+ capabilities SYSLOG AUDIT_CONTROL SETGID
writepid /dev/cpuset/system-background/tasks
service logd-reinit /system/bin/logd --reinit
diff --git a/logd/main.cpp b/logd/main.cpp
index 606aa63..b697d44 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -87,37 +87,27 @@
//
static int drop_privs(bool klogd, bool auditd) {
- // Tricky, if ro.build.type is "eng" then this is true because of the
- // side effect that ro.debuggable == 1 as well, else it is false.
- bool eng =
- __android_logger_property_get_bool("ro.build.type", BOOL_DEFAULT_FALSE);
-
- struct sched_param param;
- memset(¶m, 0, sizeof(param));
+ sched_param param = {};
if (set_sched_policy(0, SP_BACKGROUND) < 0) {
android::prdebug("failed to set background scheduling policy");
- if (!eng) return -1;
+ return -1;
}
if (sched_setscheduler((pid_t)0, SCHED_BATCH, ¶m) < 0) {
android::prdebug("failed to set batch scheduler");
- if (!eng) return -1;
+ return -1;
}
if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
android::prdebug("failed to set background cgroup");
- if (!eng) return -1;
- }
-
- if (!eng && (prctl(PR_SET_DUMPABLE, 0) < 0)) {
- android::prdebug("failed to clear PR_SET_DUMPABLE");
return -1;
}
- if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
- android::prdebug("failed to set PR_SET_KEEPCAPS");
- if (!eng) return -1;
+ if (__android_logger_property_get_bool("ro.debuggable", BOOL_DEFAULT_FALSE) &&
+ prctl(PR_SET_DUMPABLE, 0) == -1) {
+ android::prdebug("failed to clear PR_SET_DUMPABLE");
+ return -1;
}
std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(),
@@ -138,24 +128,24 @@
android::prdebug(
"failed to set CAP_SETGID, CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)",
errno);
- if (!eng) return -1;
+ return -1;
}
gid_t groups[] = { AID_READPROC };
if (setgroups(arraysize(groups), groups) == -1) {
android::prdebug("failed to set AID_READPROC groups");
- if (!eng) return -1;
+ return -1;
}
if (setgid(AID_LOGD) != 0) {
android::prdebug("failed to set AID_LOGD gid");
- if (!eng) return -1;
+ return -1;
}
if (setuid(AID_LOGD) != 0) {
android::prdebug("failed to set AID_LOGD uid");
- if (!eng) return -1;
+ return -1;
}
if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, cap_value, CAP_CLEAR) < 0) {
@@ -166,7 +156,7 @@
}
if (cap_set_proc(caps.get()) < 0) {
android::prdebug("failed to clear CAP_SETGID (%d)", errno);
- if (!eng) return -1;
+ return -1;
}
return 0;
@@ -473,7 +463,7 @@
bool auditd =
__android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
if (drop_privs(klogd, auditd) != 0) {
- return -1;
+ return EXIT_FAILURE;
}
// Serves the purpose of managing the last logs times read on a
@@ -501,7 +491,7 @@
LogReader* reader = new LogReader(logBuf);
if (reader->startListener()) {
- exit(1);
+ return EXIT_FAILURE;
}
// LogListener listens on /dev/socket/logdw for client
@@ -511,7 +501,7 @@
LogListener* swl = new LogListener(logBuf, reader);
// Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
if (swl->startListener(600)) {
- exit(1);
+ return EXIT_FAILURE;
}
// Command listener listens on /dev/socket/logd for incoming logd
@@ -519,7 +509,7 @@
CommandListener* cl = new CommandListener(logBuf, reader, swl);
if (cl->startListener()) {
- exit(1);
+ return EXIT_FAILURE;
}
// LogAudit listens on NETLINK_AUDIT socket for selinux
@@ -554,5 +544,5 @@
TEMP_FAILURE_RETRY(pause());
- exit(0);
+ return EXIT_SUCCESS;
}
diff --git a/mkbootimg/OWNERS b/mkbootimg/OWNERS
index 39448cf..3a00860 100644
--- a/mkbootimg/OWNERS
+++ b/mkbootimg/OWNERS
@@ -1 +1,2 @@
+hridya@google.com
tbao@google.com
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
index ac20d05..fda9af0 100755
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg
@@ -45,6 +45,22 @@
f.write(pack(str(pad) + 'x'))
+def get_number_of_pages(image_size, page_size):
+ """calculates the number of pages required for the image"""
+ return (image_size + page_size - 1) / page_size
+
+
+def get_recovery_dtbo_offset(args):
+ """calculates the offset of recovery_dtbo image in the boot image"""
+ num_header_pages = 1 # header occupies a page
+ num_kernel_pages = get_number_of_pages(filesize(args.kernel), args.pagesize)
+ num_ramdisk_pages = get_number_of_pages(filesize(args.ramdisk), args.pagesize)
+ num_second_pages = get_number_of_pages(filesize(args.second), args.pagesize)
+ dtbo_offset = args.pagesize * (num_header_pages + num_kernel_pages +
+ num_ramdisk_pages + num_second_pages)
+ return dtbo_offset
+
+
def write_header(args):
BOOT_MAGIC = 'ANDROID!'.encode()
args.output.write(pack('8s', BOOT_MAGIC))
@@ -76,9 +92,12 @@
args.output.write(pack('1024s', args.cmdline[512:].encode()))
if args.header_version > 0:
- args.output.write(pack('I', filesize(args.recovery_dtbo))) # size in bytes
- args.output.write(pack('Q', args.base + args.recovery_dtbo_offset)) # physical load addr
- args.output.write(pack('I', args.output.tell() + 4)) # size of boot header
+ args.output.write(pack('I', filesize(args.recovery_dtbo))) # size in bytes
+ if args.recovery_dtbo:
+ args.output.write(pack('Q', get_recovery_dtbo_offset(args))) # recovery dtbo offset
+ else:
+ args.output.write(pack('Q', 0)) # Will be set to 0 for devices without a recovery dtbo
+ args.output.write(pack('I', args.output.tell() + 4)) # size of boot header
pad_file(args.output, args.pagesize)
return img_id
@@ -150,8 +169,6 @@
parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
default=0x00f00000)
- parser.add_argument('--recovery_dtbo_offset', help='recovery dtbo offset', type=parse_int,
- default=0x0f000000)
parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
default=0)
parser.add_argument('--os_patch_level', help='operating system patch level',
diff --git a/mkbootimg/unpack_bootimg b/mkbootimg/unpack_bootimg
index 8e42ec0..c37acd5 100755
--- a/mkbootimg/unpack_bootimg
+++ b/mkbootimg/unpack_bootimg
@@ -76,8 +76,8 @@
if version > 0:
recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0]
print('recovery dtbo size: %s' % recovery_dtbo_size)
- recovery_dtbo_address = unpack('Q', args.boot_img.read(8))[0]
- print('recovery dtbo load address: %s' % recovery_dtbo_address)
+ recovery_dtbo_offset = unpack('Q', args.boot_img.read(8))[0]
+ print('recovery dtbo offset: %s' % recovery_dtbo_offset)
boot_header_size = unpack('I', args.boot_img.read(4))[0]
print('boot header size: %s' % boot_header_size)
else:
@@ -95,16 +95,13 @@
) # header + kernel
image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk'))
- num_second_pages = get_number_of_pages(second_size, page_size)
second_offset = page_size * (
num_header_pages + num_kernel_pages + num_ramdisk_pages
) # header + kernel + ramdisk
image_info_list.append((second_offset, second_size, 'second'))
if recovery_dtbo_size > 0:
- dtbo_offset = page_size * (num_header_pages + num_kernel_pages +
- num_ramdisk_pages + num_second_pages)
- image_info_list.append((dtbo_offset, recovery_dtbo_size,
+ image_info_list.append((recovery_dtbo_offset, recovery_dtbo_size,
'recovery_dtbo'))
for image_info in image_info_list:
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 3c9e5f3..478b8d0 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -201,6 +201,7 @@
$$(hide) sed -i -e 's?%VNDK_CORE_LIBRARIES%?$$(PRIVATE_VNDK_CORE_LIBRARIES)?g' $$@
$$(hide) sed -i -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $$@
$$(hide) sed -i -e 's?%VNDK_VER%?$$(PRIVATE_VNDK_VERSION)?g' $$@
+ $$(hide) sed -i -e 's?%PRODUCT%?$$(TARGET_COPY_OUT_PRODUCT)?g' $$@
llndk_libraries_list :=
vndksp_libraries_list :=
@@ -306,6 +307,16 @@
_enforce_vndk_lite_at_runtime :=
#######################################
+# ld.config.txt for recovery
+include $(CLEAR_VARS)
+LOCAL_MODULE := ld.config.recovery.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := etc/ld.config.recovery.txt
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/etc
+LOCAL_MODULE_STEM := ld.config.txt
+include $(BUILD_PREBUILT)
+
+#######################################
# llndk.libraries.txt
include $(CLEAR_VARS)
LOCAL_MODULE := llndk.libraries.txt
diff --git a/rootdir/etc/ld.config.recovery.txt b/rootdir/etc/ld.config.recovery.txt
new file mode 100644
index 0000000..5d6c01a
--- /dev/null
+++ b/rootdir/etc/ld.config.recovery.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Bionic loader config file for recovery mode
+#
+
+dir.recovery = /system/bin
+
+[recovery]
+namespace.default.search.paths = /system/${LIB}
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index a0b1996..42dc7ab 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -7,7 +7,7 @@
# absolute path of an executable is selected.
dir.system = /system/bin/
dir.system = /system/xbin/
-dir.system = /product/bin/
+dir.system = /%PRODUCT%/bin/
dir.vendor = /odm/bin/
dir.vendor = /vendor/bin/
@@ -39,7 +39,7 @@
namespace.default.isolated = true
namespace.default.search.paths = /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
# We can't have entire /system/${LIB} as permitted paths because doing so
# makes it possible to load libs in /system/${LIB}/vndk* directories by
@@ -51,7 +51,7 @@
namespace.default.permitted.paths = /system/${LIB}/drm
namespace.default.permitted.paths += /system/${LIB}/extractors
namespace.default.permitted.paths += /system/${LIB}/hw
-namespace.default.permitted.paths += /product/${LIB}
+namespace.default.permitted.paths += /%PRODUCT%/${LIB}
# These are where odex files are located. libart has to be able to dlopen the files
namespace.default.permitted.paths += /system/framework
namespace.default.permitted.paths += /system/app
@@ -63,9 +63,9 @@
namespace.default.permitted.paths += /odm/app
namespace.default.permitted.paths += /odm/priv-app
namespace.default.permitted.paths += /oem/app
-namespace.default.permitted.paths += /product/framework
-namespace.default.permitted.paths += /product/app
-namespace.default.permitted.paths += /product/priv-app
+namespace.default.permitted.paths += /%PRODUCT%/framework
+namespace.default.permitted.paths += /%PRODUCT%/app
+namespace.default.permitted.paths += /%PRODUCT%/priv-app
namespace.default.permitted.paths += /data
namespace.default.permitted.paths += /mnt/expand
@@ -88,10 +88,10 @@
namespace.default.asan.permitted.paths += /odm/app
namespace.default.asan.permitted.paths += /odm/priv-app
namespace.default.asan.permitted.paths += /oem/app
-namespace.default.asan.permitted.paths += /product/${LIB}
-namespace.default.asan.permitted.paths += /product/framework
-namespace.default.asan.permitted.paths += /product/app
-namespace.default.asan.permitted.paths += /product/priv-app
+namespace.default.asan.permitted.paths += /%PRODUCT%/${LIB}
+namespace.default.asan.permitted.paths += /%PRODUCT%/framework
+namespace.default.asan.permitted.paths += /%PRODUCT%/app
+namespace.default.asan.permitted.paths += /%PRODUCT%/priv-app
namespace.default.asan.permitted.paths += /mnt/expand
###############################################################################
@@ -327,7 +327,7 @@
namespace.system.isolated = false
namespace.system.search.paths = /system/${LIB}
-namespace.system.search.paths += /product/${LIB}
+namespace.system.search.paths += /%PRODUCT%/${LIB}
namespace.system.asan.search.paths = /data/asan/system/${LIB}
namespace.system.asan.search.paths += /system/${LIB}
@@ -345,4 +345,4 @@
[postinstall]
namespace.default.isolated = false
namespace.default.search.paths = /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 2e42b70..7834dd5 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -1,21 +1,46 @@
phony {
name: "shell_and_utilities",
required: [
+ "shell_and_utilities_system",
+ "shell_and_utilities_recovery",
+ "shell_and_utilities_vendor",
+ ],
+}
+
+phony {
+ name: "shell_and_utilities_system",
+ required: [
"awk",
- "awk_vendor",
"bzip2",
"grep",
- "grep_vendor",
"logwrapper",
- "logwrapper_vendor",
"mkshrc",
- "mkshrc_vendor",
+ "newfs_msdos",
"reboot",
"sh",
- "sh_vendor",
"toolbox",
- "toolbox_vendor",
"toybox",
+ ],
+}
+
+phony {
+ name: "shell_and_utilities_recovery",
+ required: [
+ "sh.recovery",
+ "toolbox.recovery",
+ "toybox.recovery",
+ ],
+}
+
+phony {
+ name: "shell_and_utilities_vendor",
+ required: [
+ "awk_vendor",
+ "grep_vendor",
+ "logwrapper_vendor",
+ "mkshrc_vendor",
+ "sh_vendor",
+ "toolbox_vendor",
"toybox_vendor",
],
}
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index 678b853..e310e6b 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -18,11 +18,9 @@
(a) didn't stand out given all the other systems-level changes and (b)
in Marshmallow we changed direction and started the move to toybox.
-Not everything is provided by toybox, though. We currently still use
-the BSD dd and grep (because the toybox versions are still unfinished),
-and for the bzip2 command-line tools we use the ones that are part of
-the bzip2 distribution. The awk added in Android P is Brian Kernighan's
-"one true" awk.
+Not everything is provided by toybox, though. For the bzip2 command-line tools
+we use the ones that are part of the bzip2 distribution. The awk added in
+Android P is Brian Kernighan's "one true" awk.
The lists below show what tools were provided and where they came from in
each release starting with Gingerbread. This doesn't tell the full story,
@@ -195,13 +193,15 @@
Android Q
---------
+BSD: fsck\_msdos newfs\_msdos
+
bzip2: bzcat bzip2 bunzip2
one-true-awk: awk
PCRE: egrep fgrep grep
-toolbox: getevent getprop newfs\_msdos
+toolbox: getevent getprop
toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
chroot chrt cksum clear cmp comm cp cpio cut date dd df diff dirname
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 077f542..fc51705 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -26,7 +26,6 @@
"toolbox.c",
"getevent.c",
"getprop.cpp",
- "newfs_msdos.c",
],
generated_headers: [
"toolbox_input_labels",
@@ -39,13 +38,13 @@
symlinks: [
"getevent",
"getprop",
- "newfs_msdos",
],
}
cc_binary {
name: "toolbox",
defaults: ["toolbox_binary_defaults"],
+ recovery_available: true,
}
cc_binary {
@@ -61,10 +60,3 @@
defaults: ["toolbox_defaults"],
srcs: ["r.c"],
}
-
-cc_binary_host {
- name: "newfs_msdos",
- defaults: ["toolbox_defaults"],
- srcs: ["newfs_msdos.c"],
- cflags: ["-Dnewfs_msdos_main=main"]
-}
diff --git a/toolbox/MODULE_LICENSE_BSD b/toolbox/MODULE_LICENSE_APACHE2
similarity index 100%
rename from toolbox/MODULE_LICENSE_BSD
rename to toolbox/MODULE_LICENSE_APACHE2
diff --git a/toolbox/NOTICE b/toolbox/NOTICE
index e9ab58d..8e8a91c 100644
--- a/toolbox/NOTICE
+++ b/toolbox/NOTICE
@@ -1,4 +1,4 @@
-Copyright (C) 2010 The Android Open Source Project
+Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,974 +14,3 @@
-------------------------------------------------------------------
-Copyright (C) 2014, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1987, 1993
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1987, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Jeffrey Mogul.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-David Hitz of Auspex Systems Inc.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993, 1994, 2003
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Kevin Fall.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Chris Newcomb.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Ken Smith of The State University of New York at Buffalo.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1990, 1993, 1994, 2003
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1991, 1993
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1991, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1991, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Keith Muller of the University of California, San Diego and Lance
-Visser of Convex Computer Corporation.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1992, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
-All rights reserved.
-
-This code is derived from software contributed to The NetBSD Foundation
-by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
-NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1998 Robert Nordier
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
-OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
-GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
-IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
-Copyright (C) 2010 Dimitry Andric <dimitry@andric.com>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (c) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2001-2002,2004 The NetBSD Foundation, Inc.
-All rights reserved.
-
-This code is derived from software contributed to The NetBSD Foundation
-by Luke Mewburn.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2007 The NetBSD Foundation, Inc.
-All rights reserved.
-
-This code is derived from software contributed to The NetBSD Foundation
-by Luke Mewburn.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2008, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2009, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2010 The NetBSD Foundation, Inc.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2010, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2012, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2013, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2014, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
diff --git a/toolbox/getprop.cpp b/toolbox/getprop.cpp
index 9e324a0..ca345cb 100644
--- a/toolbox/getprop.cpp
+++ b/toolbox/getprop.cpp
@@ -1,18 +1,18 @@
-//
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
#include <getopt.h>
#include <sys/system_properties.h>
diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c
deleted file mode 100644
index 5fc8b02..0000000
--- a/toolbox/newfs_msdos.c
+++ /dev/null
@@ -1,1108 +0,0 @@
-/*
- * Copyright (c) 1998 Robert Nordier
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
- * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
- * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD: src/sbin/newfs_msdos/newfs_msdos.c,v 1.33 2009/04/11 14:56:29 ed Exp $";
-#endif /* not lint */
-
-#include <sys/param.h>
-
-#ifdef __APPLE__
-#elif defined(ANDROID)
-#include <linux/fs.h>
-#include <linux/hdreg.h>
-#include <stdarg.h>
-#include <sys/ioctl.h>
-#else
-#include <sys/fdcio.h>
-#include <sys/disk.h>
-#include <sys/disklabel.h>
-#include <sys/mount.h>
-#endif
-
-#include <sys/stat.h>
-#include <sys/time.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <paths.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
-#define MAXU16 0xffff /* maximum unsigned 16-bit quantity */
-#define BPN 4 /* bits per nibble */
-#define NPB 2 /* nibbles per byte */
-
-#define DOSMAGIC 0xaa55 /* DOS magic number */
-#define MINBPS 512 /* minimum bytes per sector */
-#define MAXSPC 128 /* maximum sectors per cluster */
-#define MAXNFT 16 /* maximum number of FATs */
-#define DEFBLK 4096 /* default block size */
-#define DEFBLK16 2048 /* default block size FAT16 */
-#define DEFRDE 512 /* default root directory entries */
-#define RESFTE 2 /* reserved FAT entries */
-#define MINCLS12 1 /* minimum FAT12 clusters */
-#define MINCLS16 0x1000 /* minimum FAT16 clusters */
-#define MINCLS32 2 /* minimum FAT32 clusters */
-#define MAXCLS12 0xfed /* maximum FAT12 clusters */
-#define MAXCLS16 0xfff5 /* maximum FAT16 clusters */
-#define MAXCLS32 0xffffff5 /* maximum FAT32 clusters */
-
-#define mincls(fat) ((fat) == 12 ? MINCLS12 : \
- (fat) == 16 ? MINCLS16 : \
- MINCLS32)
-
-#define maxcls(fat) ((fat) == 12 ? MAXCLS12 : \
- (fat) == 16 ? MAXCLS16 : \
- MAXCLS32)
-
-#define mk1(p, x) \
- (p) = (u_int8_t)(x)
-
-#define mk2(p, x) \
- (p)[0] = (u_int8_t)(x), \
- (p)[1] = (u_int8_t)((x) >> 010)
-
-#define mk4(p, x) \
- (p)[0] = (u_int8_t)(x), \
- (p)[1] = (u_int8_t)((x) >> 010), \
- (p)[2] = (u_int8_t)((x) >> 020), \
- (p)[3] = (u_int8_t)((x) >> 030)
-
-#define argto1(arg, lo, msg) argtou(arg, lo, 0xff, msg)
-#define argto2(arg, lo, msg) argtou(arg, lo, 0xffff, msg)
-#define argto4(arg, lo, msg) argtou(arg, lo, 0xffffffff, msg)
-#define argtox(arg, lo, msg) argtou(arg, lo, UINT_MAX, msg)
-
-struct bs {
- u_int8_t jmp[3]; /* bootstrap entry point */
- u_int8_t oem[8]; /* OEM name and version */
-};
-
-struct bsbpb {
- u_int8_t bps[2]; /* bytes per sector */
- u_int8_t spc; /* sectors per cluster */
- u_int8_t res[2]; /* reserved sectors */
- u_int8_t nft; /* number of FATs */
- u_int8_t rde[2]; /* root directory entries */
- u_int8_t sec[2]; /* total sectors */
- u_int8_t mid; /* media descriptor */
- u_int8_t spf[2]; /* sectors per FAT */
- u_int8_t spt[2]; /* sectors per track */
- u_int8_t hds[2]; /* drive heads */
- u_int8_t hid[4]; /* hidden sectors */
- u_int8_t bsec[4]; /* big total sectors */
-};
-
-struct bsxbpb {
- u_int8_t bspf[4]; /* big sectors per FAT */
- u_int8_t xflg[2]; /* FAT control flags */
- u_int8_t vers[2]; /* file system version */
- u_int8_t rdcl[4]; /* root directory start cluster */
- u_int8_t infs[2]; /* file system info sector */
- u_int8_t bkbs[2]; /* backup boot sector */
- u_int8_t rsvd[12]; /* reserved */
-};
-
-struct bsx {
- u_int8_t drv; /* drive number */
- u_int8_t rsvd; /* reserved */
- u_int8_t sig; /* extended boot signature */
- u_int8_t volid[4]; /* volume ID number */
- u_int8_t label[11]; /* volume label */
- u_int8_t type[8]; /* file system type */
-};
-
-struct de {
- u_int8_t namext[11]; /* name and extension */
- u_int8_t attr; /* attributes */
- u_int8_t rsvd[10]; /* reserved */
- u_int8_t time[2]; /* creation time */
- u_int8_t date[2]; /* creation date */
- u_int8_t clus[2]; /* starting cluster */
- u_int8_t size[4]; /* size */
-};
-
-struct bpb {
- u_int bps; /* bytes per sector */
- u_int spc; /* sectors per cluster */
- u_int res; /* reserved sectors */
- u_int nft; /* number of FATs */
- u_int rde; /* root directory entries */
- u_int sec; /* total sectors */
- u_int mid; /* media descriptor */
- u_int spf; /* sectors per FAT */
- u_int spt; /* sectors per track */
- u_int hds; /* drive heads */
- u_int hid; /* hidden sectors */
- u_int bsec; /* big total sectors */
- u_int bspf; /* big sectors per FAT */
- u_int rdcl; /* root directory start cluster */
- u_int infs; /* file system info sector */
- u_int bkbs; /* backup boot sector */
-};
-
-#define BPBGAP 0, 0, 0, 0, 0, 0
-
-static struct {
- const char *name;
- struct bpb bpb;
-} const stdfmt[] = {
- {"160", {512, 1, 1, 2, 64, 320, 0xfe, 1, 8, 1, BPBGAP}},
- {"180", {512, 1, 1, 2, 64, 360, 0xfc, 2, 9, 1, BPBGAP}},
- {"320", {512, 2, 1, 2, 112, 640, 0xff, 1, 8, 2, BPBGAP}},
- {"360", {512, 2, 1, 2, 112, 720, 0xfd, 2, 9, 2, BPBGAP}},
- {"640", {512, 2, 1, 2, 112, 1280, 0xfb, 2, 8, 2, BPBGAP}},
- {"720", {512, 2, 1, 2, 112, 1440, 0xf9, 3, 9, 2, BPBGAP}},
- {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}},
- {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2, 8, 2, BPBGAP}},
- {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}},
- {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}}
-};
-
-static const u_int8_t bootcode[] = {
- 0xfa, /* cli */
- 0x31, 0xc0, /* xor ax,ax */
- 0x8e, 0xd0, /* mov ss,ax */
- 0xbc, 0x00, 0x7c, /* mov sp,7c00h */
- 0xfb, /* sti */
- 0x8e, 0xd8, /* mov ds,ax */
- 0xe8, 0x00, 0x00, /* call $ + 3 */
- 0x5e, /* pop si */
- 0x83, 0xc6, 0x19, /* add si,+19h */
- 0xbb, 0x07, 0x00, /* mov bx,0007h */
- 0xfc, /* cld */
- 0xac, /* lodsb */
- 0x84, 0xc0, /* test al,al */
- 0x74, 0x06, /* jz $ + 8 */
- 0xb4, 0x0e, /* mov ah,0eh */
- 0xcd, 0x10, /* int 10h */
- 0xeb, 0xf5, /* jmp $ - 9 */
- 0x30, 0xe4, /* xor ah,ah */
- 0xcd, 0x16, /* int 16h */
- 0xcd, 0x19, /* int 19h */
- 0x0d, 0x0a,
- 'N', 'o', 'n', '-', 's', 'y', 's', 't',
- 'e', 'm', ' ', 'd', 'i', 's', 'k',
- 0x0d, 0x0a,
- 'P', 'r', 'e', 's', 's', ' ', 'a', 'n',
- 'y', ' ', 'k', 'e', 'y', ' ', 't', 'o',
- ' ', 'r', 'e', 'b', 'o', 'o', 't',
- 0x0d, 0x0a,
- 0
-};
-
-static void check_mounted(const char *, mode_t);
-static void getstdfmt(const char *, struct bpb *);
-static void getdiskinfo(int, const char *, const char *, int, struct bpb *);
-static void print_bpb(struct bpb *);
-static u_int ckgeom(const char *, u_int, const char *);
-static u_int argtou(const char *, u_int, u_int, const char *);
-static off_t argtooff(const char *, const char *);
-static int oklabel(const char *);
-static void mklabel(u_int8_t *, const char *);
-static void setstr(u_int8_t *, const char *, size_t);
-static void usage(void);
-
-/*
- * Construct a FAT12, FAT16, or FAT32 file system.
- */
-int newfs_msdos_main(int argc, char *argv[])
-{
- static const char opts[] = "@:NAB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:";
- const char *opt_B = NULL, *opt_L = NULL, *opt_O = NULL, *opt_f = NULL;
- u_int opt_F = 0, opt_I = 0, opt_S = 0, opt_a = 0, opt_b = 0, opt_c = 0;
- u_int opt_e = 0, opt_h = 0, opt_i = 0, opt_k = 0, opt_m = 0, opt_n = 0;
- u_int opt_o = 0, opt_r = 0, opt_s = 0, opt_u = 0;
- u_int opt_A = 0;
- int opt_N = 0;
- int Iflag = 0, mflag = 0, oflag = 0;
- char buf[MAXPATHLEN];
- struct stat sb;
- struct timeval tv;
- struct bpb bpb;
- struct tm *tm;
- struct bs *bs;
- struct bsbpb *bsbpb;
- struct bsxbpb *bsxbpb;
- struct bsx *bsx;
- struct de *de;
- u_int8_t *img;
- const char *fname, *dtype, *bname;
- ssize_t n;
- time_t now;
- u_int fat, bss, rds, cls, dir, lsn, x, x1, x2;
- u_int extra_res, alignment=0, set_res, set_spf, set_spc, tempx, attempts=0;
- int ch, fd, fd1;
- off_t opt_create = 0, opt_ofs = 0;
-
- while ((ch = getopt(argc, argv, opts)) != -1)
- switch (ch) {
- case '@':
- opt_ofs = argtooff(optarg, "offset");
- break;
- case 'N':
- opt_N = 1;
- break;
- case 'A':
- opt_A = 1;
- break;
- case 'B':
- opt_B = optarg;
- break;
- case 'C':
- opt_create = argtooff(optarg, "create size");
- break;
- case 'F':
- if (strcmp(optarg, "12") && strcmp(optarg, "16") && strcmp(optarg, "32"))
- errx(1, "%s: bad FAT type", optarg);
- opt_F = atoi(optarg);
- break;
- case 'I':
- opt_I = argto4(optarg, 0, "volume ID");
- Iflag = 1;
- break;
- case 'L':
- if (!oklabel(optarg))
- errx(1, "%s: bad volume label", optarg);
- opt_L = optarg;
- break;
- case 'O':
- if (strlen(optarg) > 8)
- errx(1, "%s: bad OEM string", optarg);
- opt_O = optarg;
- break;
- case 'S':
- opt_S = argto2(optarg, 1, "bytes/sector");
- break;
- case 'a':
- opt_a = argto4(optarg, 1, "sectors/FAT");
- break;
- case 'b':
- opt_b = argtox(optarg, 1, "block size");
- opt_c = 0;
- break;
- case 'c':
- opt_c = argto1(optarg, 1, "sectors/cluster");
- opt_b = 0;
- break;
- case 'e':
- opt_e = argto2(optarg, 1, "directory entries");
- break;
- case 'f':
- opt_f = optarg;
- break;
- case 'h':
- opt_h = argto2(optarg, 1, "drive heads");
- break;
- case 'i':
- opt_i = argto2(optarg, 1, "info sector");
- break;
- case 'k':
- opt_k = argto2(optarg, 1, "backup sector");
- break;
- case 'm':
- opt_m = argto1(optarg, 0, "media descriptor");
- mflag = 1;
- break;
- case 'n':
- opt_n = argto1(optarg, 1, "number of FATs");
- break;
- case 'o':
- opt_o = argto4(optarg, 0, "hidden sectors");
- oflag = 1;
- break;
- case 'r':
- opt_r = argto2(optarg, 1, "reserved sectors");
- break;
- case 's':
- opt_s = argto4(optarg, 1, "file system size");
- break;
- case 'u':
- opt_u = argto2(optarg, 1, "sectors/track");
- break;
- default:
- usage();
- }
- argc -= optind;
- argv += optind;
- if (argc < 1 || argc > 2)
- usage();
- fname = *argv++;
- if (!opt_create && !strchr(fname, '/')) {
- snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
- if (!(fname = strdup(buf)))
- err(1, "%s", buf);
- }
- dtype = *argv;
- if (opt_A) {
- if (opt_r)
- errx(1, "align (-A) is incompatible with -r");
- if (opt_N)
- errx(1, "align (-A) is incompatible with -N");
- }
- if (opt_create) {
- if (opt_N)
- errx(1, "create (-C) is incompatible with -N");
- fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
- if (fd == -1)
- errx(1, "failed to create %s", fname);
- if (ftruncate(fd, opt_create))
- errx(1, "failed to initialize %jd bytes", (intmax_t)opt_create);
- } else if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1)
- err(1, "%s", fname);
- if (fstat(fd, &sb))
- err(1, "%s", fname);
- if (opt_create) {
- if (!S_ISREG(sb.st_mode))
- warnx("warning, %s is not a regular file", fname);
- } else {
- if (!S_ISCHR(sb.st_mode))
- warnx("warning, %s is not a character device", fname);
- }
- if (!opt_N)
- check_mounted(fname, sb.st_mode);
- if (opt_ofs && opt_ofs != lseek(fd, opt_ofs, SEEK_SET))
- errx(1, "cannot seek to %jd", (intmax_t)opt_ofs);
- memset(&bpb, 0, sizeof(bpb));
- if (opt_f) {
- getstdfmt(opt_f, &bpb);
- bpb.bsec = bpb.sec;
- bpb.sec = 0;
- bpb.bspf = bpb.spf;
- bpb.spf = 0;
- }
- if (opt_h)
- bpb.hds = opt_h;
- if (opt_u)
- bpb.spt = opt_u;
- if (opt_S)
- bpb.bps = opt_S;
- if (opt_s)
- bpb.bsec = opt_s;
- if (oflag)
- bpb.hid = opt_o;
- if (!(opt_f || (opt_h && opt_u && opt_S && opt_s && oflag))) {
- off_t delta;
- getdiskinfo(fd, fname, dtype, oflag, &bpb);
- if (opt_s) {
- bpb.bsec = opt_s;
- }
- bpb.bsec -= (opt_ofs / bpb.bps);
- delta = bpb.bsec % bpb.spt;
- if (delta != 0) {
- warnx("trim %d sectors from %d to adjust to a multiple of %d",
- (int)delta, bpb.bsec, bpb.spt);
- bpb.bsec -= delta;
- }
- if (bpb.spc == 0) { /* set defaults */
- if (bpb.bsec <= 6000) /* about 3MB -> 512 bytes */
- bpb.spc = 1;
- else if (bpb.bsec <= (1<<17)) /* 64M -> 4k */
- bpb.spc = 8;
- else if (bpb.bsec <= (1<<19)) /* 256M -> 8k */
- bpb.spc = 16;
- else if (bpb.bsec <= (1<<22)) /* 2G -> 16k, some versions of windows
- require a minimum of 65527 clusters */
- bpb.spc = 32;
- else
- bpb.spc = 64; /* otherwise 32k */
- }
- }
- if (!powerof2(bpb.bps))
- errx(1, "bytes/sector (%u) is not a power of 2", bpb.bps);
- if (bpb.bps < MINBPS)
- errx(1, "bytes/sector (%u) is too small; minimum is %u",
- bpb.bps, MINBPS);
- if (!(fat = opt_F)) {
- if (opt_f)
- fat = 12;
- else if (!opt_e && (opt_i || opt_k))
- fat = 32;
- }
- if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k)))
- errx(1, "-%c is not a legal FAT%s option",
- fat == 32 ? 'e' : opt_i ? 'i' : 'k',
- fat == 32 ? "32" : "12/16");
- if (opt_f && fat == 32)
- bpb.rde = 0;
- if (opt_b) {
- if (!powerof2(opt_b))
- errx(1, "block size (%u) is not a power of 2", opt_b);
- if (opt_b < bpb.bps)
- errx(1, "block size (%u) is too small; minimum is %u",
- opt_b, bpb.bps);
- if (opt_b > bpb.bps * MAXSPC)
- errx(1, "block size (%u) is too large; maximum is %u", opt_b, bpb.bps * MAXSPC);
- bpb.spc = opt_b / bpb.bps;
- }
- if (opt_c) {
- if (!powerof2(opt_c))
- errx(1, "sectors/cluster (%u) is not a power of 2", opt_c);
- bpb.spc = opt_c;
- }
- if (opt_r)
- bpb.res = opt_r;
- if (opt_n) {
- if (opt_n > MAXNFT)
- errx(1, "number of FATs (%u) is too large; maximum is %u", opt_n, MAXNFT);
- bpb.nft = opt_n;
- }
- if (opt_e)
- bpb.rde = opt_e;
- if (mflag) {
- if (opt_m < 0xf0)
- errx(1, "illegal media descriptor (%#x)", opt_m);
- bpb.mid = opt_m;
- }
- if (opt_a)
- bpb.bspf = opt_a;
- if (opt_i)
- bpb.infs = opt_i;
- if (opt_k)
- bpb.bkbs = opt_k;
- bss = 1;
- bname = NULL;
- fd1 = -1;
- if (opt_B) {
- bname = opt_B;
- if (!strchr(bname, '/')) {
- snprintf(buf, sizeof(buf), "/boot/%s", bname);
- if (!(bname = strdup(buf)))
- err(1, "%s", buf);
- }
- if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb))
- err(1, "%s", bname);
- if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps ||
- sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16)
- errx(1, "%s: inappropriate file type or format", bname);
- bss = sb.st_size / bpb.bps;
- }
- if (!bpb.nft)
- bpb.nft = 2;
- if (!fat) {
- if (bpb.bsec < (bpb.res ? bpb.res : bss) +
- howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) *
- ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) *
- bpb.nft +
- howmany(bpb.rde ? bpb.rde : DEFRDE,
- bpb.bps / sizeof(struct de)) +
- (bpb.spc ? MINCLS16 : MAXCLS12 + 1) *
- (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps)))
- fat = 12;
- else if (bpb.rde || bpb.bsec <
- (bpb.res ? bpb.res : bss) +
- howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft +
- howmany(DEFRDE, bpb.bps / sizeof(struct de)) +
- (MAXCLS16 + 1) *
- (bpb.spc ? bpb.spc : howmany(8192, bpb.bps)))
- fat = 16;
- else
- fat = 32;
- }
- x = bss;
- if (fat == 32) {
- if (!bpb.infs) {
- if (x == MAXU16 || x == bpb.bkbs)
- errx(1, "no room for info sector");
- bpb.infs = x;
- }
- if (bpb.infs != MAXU16 && x <= bpb.infs)
- x = bpb.infs + 1;
- if (!bpb.bkbs) {
- if (x == MAXU16)
- errx(1, "no room for backup sector");
- bpb.bkbs = x;
- } else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs)
- errx(1, "backup sector would overwrite info sector");
- if (bpb.bkbs != MAXU16 && x <= bpb.bkbs)
- x = bpb.bkbs + 1;
- }
-
- extra_res = 0;
- set_res = !bpb.res;
- set_spf = !bpb.bspf;
- set_spc = !bpb.spc;
- tempx = x;
- /*
- * Attempt to align if opt_A is set. This is done by increasing the number
- * of reserved blocks. This can cause other factors to change, which can in
- * turn change the alignment. This should take at most 2 iterations, as
- * increasing the reserved amount may cause the FAT size to decrease by 1,
- * requiring another nft reserved blocks. If spc changes, it will
- * be half of its previous size, and thus will not throw off alignment.
- */
- do {
- x = tempx;
- if (set_res)
- bpb.res = (fat == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x) + extra_res;
- else if (bpb.res < x)
- errx(1, "too few reserved sectors");
- if (fat != 32 && !bpb.rde)
- bpb.rde = DEFRDE;
- rds = howmany(bpb.rde, bpb.bps / sizeof(struct de));
- if (set_spc)
- for (bpb.spc = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bps);
- bpb.spc < MAXSPC &&
- bpb.res +
- howmany((RESFTE + maxcls(fat)) * (fat / BPN),
- bpb.bps * NPB) * bpb.nft +
- rds +
- (u_int64_t)(maxcls(fat) + 1) * bpb.spc <= bpb.bsec;
- bpb.spc <<= 1);
- if (fat != 32 && bpb.bspf > MAXU16)
- errx(1, "too many sectors/FAT for FAT12/16");
- x1 = bpb.res + rds;
- x = bpb.bspf ? bpb.bspf : 1;
- if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec)
- errx(1, "meta data exceeds file system size");
- x1 += x * bpb.nft;
- x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB /
- (bpb.spc * bpb.bps * NPB + fat / BPN * bpb.nft);
- x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN), bpb.bps * NPB);
- if (set_spf) {
- if (!bpb.bspf) {
- bpb.bspf = x2;
- }
- x1 += (bpb.bspf - 1) * bpb.nft;
- }
- if(set_res) {
- /* attempt to align root directory */
- alignment = (bpb.res + bpb.bspf * bpb.nft) % bpb.spc;
- extra_res += bpb.spc - alignment;
- }
- attempts++;
- } while(opt_A && alignment != 0 && attempts < 2);
- if (alignment != 0)
- warnx("warning: Alignment failed.");
-
- cls = (bpb.bsec - x1) / bpb.spc;
- x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (fat / BPN) - RESFTE;
- if (cls > x)
- cls = x;
- if (bpb.bspf < x2)
- warnx("warning: sectors/FAT limits file system to %u clusters", cls);
- if (cls < mincls(fat))
- errx(1, "%u clusters too few clusters for FAT%u, need %u", cls, fat, mincls(fat));
- if (cls > maxcls(fat)) {
- cls = maxcls(fat);
- bpb.bsec = x1 + (cls + 1) * bpb.spc - 1;
- warnx("warning: FAT type limits file system to %u sectors", bpb.bsec);
- }
- printf("%s: %u sector%s in %u FAT%u cluster%s (%u bytes/cluster)\n",
- fname, cls * bpb.spc, cls * bpb.spc == 1 ? "" : "s", cls, fat,
- cls == 1 ? "" : "s", bpb.bps * bpb.spc);
- if (!bpb.mid)
- bpb.mid = !bpb.hid ? 0xf0 : 0xf8;
- if (fat == 32)
- bpb.rdcl = RESFTE;
- if (bpb.hid + bpb.bsec <= MAXU16) {
- bpb.sec = bpb.bsec;
- bpb.bsec = 0;
- }
- if (fat != 32) {
- bpb.spf = bpb.bspf;
- bpb.bspf = 0;
- }
- print_bpb(&bpb);
- if (!opt_N) {
- gettimeofday(&tv, NULL);
- now = tv.tv_sec;
- tm = localtime(&now);
- if (!(img = malloc(bpb.bps)))
- err(1, "%u", bpb.bps);
- dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft;
- for (lsn = 0; lsn < dir + (fat == 32 ? bpb.spc : rds); lsn++) {
- x = lsn;
- if (opt_B && fat == 32 && bpb.bkbs != MAXU16 && bss <= bpb.bkbs && x >= bpb.bkbs) {
- x -= bpb.bkbs;
- if (!x && lseek(fd1, opt_ofs, SEEK_SET))
- err(1, "%s", bname);
- }
- if (opt_B && x < bss) {
- if ((n = read(fd1, img, bpb.bps)) == -1)
- err(1, "%s", bname);
- if ((unsigned)n != bpb.bps)
- errx(1, "%s: can't read sector %u", bname, x);
- } else
- memset(img, 0, bpb.bps);
- if (!lsn || (fat == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) {
- x1 = sizeof(struct bs);
- bsbpb = (struct bsbpb *)(img + x1);
- mk2(bsbpb->bps, bpb.bps);
- mk1(bsbpb->spc, bpb.spc);
- mk2(bsbpb->res, bpb.res);
- mk1(bsbpb->nft, bpb.nft);
- mk2(bsbpb->rde, bpb.rde);
- mk2(bsbpb->sec, bpb.sec);
- mk1(bsbpb->mid, bpb.mid);
- mk2(bsbpb->spf, bpb.spf);
- mk2(bsbpb->spt, bpb.spt);
- mk2(bsbpb->hds, bpb.hds);
- mk4(bsbpb->hid, bpb.hid);
- mk4(bsbpb->bsec, bpb.bsec);
- x1 += sizeof(struct bsbpb);
- if (fat == 32) {
- bsxbpb = (struct bsxbpb *)(img + x1);
- mk4(bsxbpb->bspf, bpb.bspf);
- mk2(bsxbpb->xflg, 0);
- mk2(bsxbpb->vers, 0);
- mk4(bsxbpb->rdcl, bpb.rdcl);
- mk2(bsxbpb->infs, bpb.infs);
- mk2(bsxbpb->bkbs, bpb.bkbs);
- x1 += sizeof(struct bsxbpb);
- }
- bsx = (struct bsx *)(img + x1);
- mk1(bsx->sig, 0x29);
- if (Iflag)
- x = opt_I;
- else
- x = (((u_int)(1 + tm->tm_mon) << 8 |
- (u_int)tm->tm_mday) +
- ((u_int)tm->tm_sec << 8 |
- (u_int)(tv.tv_usec / 10))) << 16 |
- ((u_int)(1900 + tm->tm_year) +
- ((u_int)tm->tm_hour << 8 |
- (u_int)tm->tm_min));
- mk4(bsx->volid, x);
- mklabel(bsx->label, opt_L ? opt_L : "NO NAME");
- snprintf(buf, sizeof(buf), "FAT%u", fat);
- setstr(bsx->type, buf, sizeof(bsx->type));
- if (!opt_B) {
- x1 += sizeof(struct bsx);
- bs = (struct bs *)img;
- mk1(bs->jmp[0], 0xeb);
- mk1(bs->jmp[1], x1 - 2);
- mk1(bs->jmp[2], 0x90);
- setstr(bs->oem, opt_O ? opt_O : "BSD 4.4",
- sizeof(bs->oem));
- memcpy(img + x1, bootcode, sizeof(bootcode));
- mk2(img + MINBPS - 2, DOSMAGIC);
- }
- } else if (fat == 32 && bpb.infs != MAXU16 &&
- (lsn == bpb.infs || (bpb.bkbs != MAXU16 &&
- lsn == bpb.bkbs + bpb.infs))) {
- mk4(img, 0x41615252);
- mk4(img + MINBPS - 28, 0x61417272);
- mk4(img + MINBPS - 24, 0xffffffff);
- mk4(img + MINBPS - 20, bpb.rdcl);
- mk2(img + MINBPS - 2, DOSMAGIC);
- } else if (lsn >= bpb.res && lsn < dir &&
- !((lsn - bpb.res) % (bpb.spf ? bpb.spf : bpb.bspf))) {
- mk1(img[0], bpb.mid);
- for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++)
- mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff);
- } else if (lsn == dir && opt_L) {
- de = (struct de *)img;
- mklabel(de->namext, opt_L);
- mk1(de->attr, 050);
- x = (u_int)tm->tm_hour << 11 |
- (u_int)tm->tm_min << 5 |
- (u_int)tm->tm_sec >> 1;
- mk2(de->time, x);
- x = (u_int)(tm->tm_year - 80) << 9 |
- (u_int)(tm->tm_mon + 1) << 5 |
- (u_int)tm->tm_mday;
- mk2(de->date, x);
- }
- if ((n = write(fd, img, bpb.bps)) == -1)
- err(1, "%s", fname);
- if ((unsigned)n != bpb.bps) {
- errx(1, "%s: can't write sector %u", fname, lsn);
- exit(1);
- }
- }
- free(img);
- }
- return 0;
-}
-
-/*
- * Exit with error if file system is mounted.
- */
-static void check_mounted(const char *fname, mode_t mode)
-{
-#ifdef ANDROID
- warnx("Skipping mount checks");
-#else
- struct statfs *mp;
- const char *s1, *s2;
- size_t len;
- int n, r;
-
- if (!(n = getmntinfo(&mp, MNT_NOWAIT)))
- err(1, "getmntinfo");
- len = strlen(_PATH_DEV);
- s1 = fname;
- if (!strncmp(s1, _PATH_DEV, len))
- s1 += len;
- r = S_ISCHR(mode) && s1 != fname && *s1 == 'r';
- for (; n--; mp++) {
- s2 = mp->f_mntfromname;
- if (!strncmp(s2, _PATH_DEV, len))
- s2 += len;
- if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || !strcmp(s1, s2))
- errx(1, "%s is mounted on %s", fname, mp->f_mntonname);
- }
-#endif
-}
-
-/*
- * Get a standard format.
- */
-static void getstdfmt(const char *fmt, struct bpb *bpb)
-{
- u_int x, i;
-
- x = sizeof(stdfmt) / sizeof(stdfmt[0]);
- for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++);
- if (i == x)
- errx(1, "%s: unknown standard format", fmt);
- *bpb = stdfmt[i].bpb;
-}
-
-/*
- * Get disk slice, partition, and geometry information.
- */
-
-#ifdef __APPLE__
-static void getdiskinfo(__unused int fd, __unused const char* fname, __unused const char* dtype,
- __unused int oflag, __unused struct bpb* bpb) {}
-#elif ANDROID
-static void getdiskinfo(int fd, const char *fname, const char *dtype,
- __unused int oflag,struct bpb *bpb)
-{
- struct hd_geometry geom;
- u_long block_size;
-
- if (ioctl(fd, BLKSSZGET, &bpb->bps)) {
- fprintf(stderr, "Error getting bytes / sector (%s)\n", strerror(errno));
- exit(1);
- }
-
- ckgeom(fname, bpb->bps, "bytes/sector");
-
- if (ioctl(fd, BLKGETSIZE, &block_size)) {
- fprintf(stderr, "Error getting blocksize (%s)\n", strerror(errno));
- exit(1);
- }
-
- if (block_size > UINT32_MAX) {
- fprintf(stderr, "Error blocksize too large: %lu\n", block_size);
- exit(1);
- }
-
- bpb->bsec = (u_int)block_size;
-
- if (ioctl(fd, HDIO_GETGEO, &geom)) {
- fprintf(stderr, "Error getting gemoetry (%s) - trying sane values\n", strerror(errno));
- geom.heads = 64;
- geom.sectors = 63;
- }
-
- if (!geom.heads) {
- printf("Bogus heads from kernel - setting sane value\n");
- geom.heads = 64;
- }
-
- if (!geom.sectors) {
- printf("Bogus sectors from kernel - setting sane value\n");
- geom.sectors = 63;
- }
-
- bpb->spt = geom.sectors;
- ckgeom(fname, bpb->spt, "sectors/track");
-
- bpb->hds = geom.heads;
- ckgeom(fname, bpb->hds, "drive heads");
-}
-
-#else
-
-static void getdiskinfo(int fd, const char *fname, const char *dtype,
- __unused int oflag, struct bpb *bpb)
-{
- struct disklabel *lp, dlp;
- struct fd_type type;
- off_t ms, hs = 0;
-
- lp = NULL;
-
- /* If the user specified a disk type, try to use that */
- if (dtype != NULL) {
- lp = getdiskbyname(dtype);
- }
-
- /* Maybe it's a floppy drive */
- if (lp == NULL) {
- if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) {
- struct stat st;
-
- if (fstat(fd, &st))
- err(1, "Cannot get disk size");
- /* create a fake geometry for a file image */
- ms = st.st_size;
- dlp.d_secsize = 512;
- dlp.d_nsectors = 63;
- dlp.d_ntracks = 255;
- dlp.d_secperunit = ms / dlp.d_secsize;
- lp = &dlp;
- } else if (ioctl(fd, FD_GTYPE, &type) != -1) {
- dlp.d_secsize = 128 << type.secsize;
- dlp.d_nsectors = type.sectrac;
- dlp.d_ntracks = type.heads;
- dlp.d_secperunit = ms / dlp.d_secsize;
- lp = &dlp;
- }
- }
-
- /* Maybe it's a fixed drive */
- if (lp == NULL) {
- if (ioctl(fd, DIOCGDINFO, &dlp) == -1) {
- if (bpb->bps == 0 && ioctl(fd, DIOCGSECTORSIZE, &dlp.d_secsize) == -1)
- errx(1, "Cannot get sector size, %s", strerror(errno));
-
- /* XXX Should we use bpb->bps if it's set? */
- dlp.d_secperunit = ms / dlp.d_secsize;
-
- if (bpb->spt == 0 && ioctl(fd, DIOCGFWSECTORS, &dlp.d_nsectors) == -1) {
- warnx("Cannot get number of sectors per track, %s", strerror(errno));
- dlp.d_nsectors = 63;
- }
- if (bpb->hds == 0 && ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) {
- warnx("Cannot get number of heads, %s", strerror(errno));
- if (dlp.d_secperunit <= 63*1*1024)
- dlp.d_ntracks = 1;
- else if (dlp.d_secperunit <= 63*16*1024)
- dlp.d_ntracks = 16;
- else
- dlp.d_ntracks = 255;
- }
- }
-
- hs = (ms / dlp.d_secsize) - dlp.d_secperunit;
- lp = &dlp;
- }
-
- if (bpb->bps == 0)
- bpb->bps = ckgeom(fname, lp->d_secsize, "bytes/sector");
- if (bpb->spt == 0)
- bpb->spt = ckgeom(fname, lp->d_nsectors, "sectors/track");
- if (bpb->hds == 0)
- bpb->hds = ckgeom(fname, lp->d_ntracks, "drive heads");
- if (bpb->bsec == 0)
- bpb->bsec = lp->d_secperunit;
- if (bpb->hid == 0)
- bpb->hid = hs;
-}
-#endif
-
-/*
- * Print out BPB values.
- */
-static void print_bpb(struct bpb *bpb)
-{
- printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res,
- bpb->nft);
- if (bpb->rde)
- printf(" rde=%u", bpb->rde);
- if (bpb->sec)
- printf(" sec=%u", bpb->sec);
- printf(" mid=%#x", bpb->mid);
- if (bpb->spf)
- printf(" spf=%u", bpb->spf);
- printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid);
- if (bpb->bsec)
- printf(" bsec=%u", bpb->bsec);
- if (!bpb->spf) {
- printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl);
- printf(" infs=");
- printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs);
- printf(" bkbs=");
- printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs);
- }
- printf("\n");
-}
-
-/*
- * Check a disk geometry value.
- */
-static u_int ckgeom(const char *fname, u_int val, const char *msg)
-{
- if (!val)
- errx(1, "%s: no default %s", fname, msg);
- if (val > MAXU16)
- errx(1, "%s: illegal %s %d", fname, msg, val);
- return val;
-}
-
-/*
- * Convert and check a numeric option argument.
- */
-static u_int argtou(const char *arg, u_int lo, u_int hi, const char *msg)
-{
- char *s;
- u_long x;
-
- errno = 0;
- x = strtoul(arg, &s, 0);
- if (errno || !*arg || *s || x < lo || x > hi)
- errx(1, "%s: bad %s", arg, msg);
- return x;
-}
-
-/*
- * Same for off_t, with optional skmgpP suffix
- */
-static off_t argtooff(const char *arg, const char *msg)
-{
- char *s;
- off_t x;
-
- x = strtoll(arg, &s, 0);
- /* allow at most one extra char */
- if (errno || x < 0 || (s[0] && s[1]) )
- errx(1, "%s: bad %s", arg, msg);
- if (*s) { /* the extra char is the multiplier */
- switch (*s) {
- default:
- errx(1, "%s: bad %s", arg, msg);
- /* notreached */
-
- case 's': /* sector */
- case 'S':
- x <<= 9; /* times 512 */
- break;
-
- case 'k': /* kilobyte */
- case 'K':
- x <<= 10; /* times 1024 */
- break;
-
- case 'm': /* megabyte */
- case 'M':
- x <<= 20; /* times 1024*1024 */
- break;
-
- case 'g': /* gigabyte */
- case 'G':
- x <<= 30; /* times 1024*1024*1024 */
- break;
-
- case 'p': /* partition start */
- case 'P': /* partition start */
- case 'l': /* partition length */
- case 'L': /* partition length */
- errx(1, "%s: not supported yet %s", arg, msg);
- /* notreached */
- }
- }
- return x;
-}
-
-/*
- * Check a volume label.
- */
-static int oklabel(const char *src)
-{
- int c, i;
-
- for (i = 0; i <= 11; i++) {
- c = (u_char)*src++;
- if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
- break;
- }
- return i && !c;
-}
-
-/*
- * Make a volume label.
- */
-static void mklabel(u_int8_t *dest, const char *src)
-{
- int c, i;
-
- for (i = 0; i < 11; i++) {
- c = *src ? toupper(*src++) : ' ';
- *dest++ = !i && c == '\xe5' ? 5 : c;
- }
-}
-
-/*
- * Copy string, padding with spaces.
- */
-static void setstr(u_int8_t *dest, const char *src, size_t len)
-{
- while (len--)
- *dest++ = *src ? *src++ : ' ';
-}
-
-/*
- * Print usage message.
- */
-static void usage(void)
-{
- fprintf(stderr,
- "usage: newfs_msdos [ -options ] special [disktype]\n"
- "where the options are:\n"
- "\t-@ create file system at specified offset\n"
- "\t-A Attempt to cluster align root directory\n"
- "\t-B get bootstrap from file\n"
- "\t-C create image file with specified size\n"
- "\t-F FAT type (12, 16, or 32)\n"
- "\t-I volume ID\n"
- "\t-L volume label\n"
- "\t-N don't create file system: just print out parameters\n"
- "\t-O OEM string\n"
- "\t-S bytes/sector\n"
- "\t-a sectors/FAT\n"
- "\t-b block size\n"
- "\t-c sectors/cluster\n"
- "\t-e root directory entries\n"
- "\t-f standard format\n"
- "\t-h drive heads\n"
- "\t-i file system info sector\n"
- "\t-k backup boot sector\n"
- "\t-m media descriptor\n"
- "\t-n number of FATs\n"
- "\t-o hidden sectors\n"
- "\t-r reserved sectors\n"
- "\t-s file system size (sectors)\n"
- "\t-u sectors/track\n");
- exit(1);
-}
diff --git a/toolbox/tools.h b/toolbox/tools.h
index 3d4bc3e..abeb3ef 100644
--- a/toolbox/tools.h
+++ b/toolbox/tools.h
@@ -1,4 +1,3 @@
TOOL(getevent)
TOOL(getprop)
-TOOL(newfs_msdos)
TOOL(toolbox)