Merge "fix misuse of formatting specifiers"
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 0531cf9..bc5ba38 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -425,6 +425,7 @@
// Used to pass multiple values to the stdin read thread.
struct StdinReadArgs {
int stdin_fd, write_fd;
+ bool raw_stdin;
std::unique_ptr<ShellProtocol> protocol;
};
@@ -452,26 +453,42 @@
D("stdin_read_thread(): pre unix_read(fdi=%d,...)", args->stdin_fd);
int r = unix_read(args->stdin_fd, buffer_ptr, buffer_size);
D("stdin_read_thread(): post unix_read(fdi=%d,...)", args->stdin_fd);
- if (r <= 0) break;
- for (int n = 0; n < r; n++){
- switch(buffer_ptr[n]) {
- case '\n':
- state = 1;
- break;
- case '\r':
- state = 1;
- break;
- case '~':
- if(state == 1) state++;
- break;
- case '.':
- if(state == 2) {
- fprintf(stderr,"\n* disconnect *\n");
- stdin_raw_restore(args->stdin_fd);
- exit(0);
+ if (r <= 0) {
+ // Only devices using the shell protocol know to close subprocess
+ // stdin. For older devices we want to just leave the connection
+ // open, otherwise an unpredictable amount of return data could
+ // be lost due to the FD closing before all data has been received.
+ if (args->protocol) {
+ args->protocol->Write(ShellProtocol::kIdCloseStdin, 0);
+ }
+ break;
+ }
+ // If we made stdin raw, check input for the "~." escape sequence. In
+ // this situation signals like Ctrl+C are sent remotely rather than
+ // interpreted locally so this provides an emergency out if the remote
+ // process starts ignoring the signal. SSH also does this, see the
+ // "escape characters" section on the ssh man page for more info.
+ if (args->raw_stdin) {
+ for (int n = 0; n < r; n++){
+ switch(buffer_ptr[n]) {
+ case '\n':
+ state = 1;
+ break;
+ case '\r':
+ state = 1;
+ break;
+ case '~':
+ if(state == 1) state++;
+ break;
+ case '.':
+ if(state == 2) {
+ stdin_raw_restore(args->stdin_fd);
+ fprintf(stderr,"\n* disconnect *\n");
+ exit(0);
+ }
+ default:
+ state = 0;
}
- default:
- state = 0;
}
}
if (args->protocol) {
@@ -488,8 +505,44 @@
return nullptr;
}
-static int interactive_shell(const std::string& service_string,
- bool use_shell_protocol) {
+// Returns a shell service string with the indicated arguments and command.
+static std::string ShellServiceString(bool use_shell_protocol,
+ const std::string& type_arg,
+ const std::string& command) {
+ std::vector<std::string> args;
+ if (use_shell_protocol) {
+ args.push_back(kShellServiceArgShellProtocol);
+ }
+ if (!type_arg.empty()) {
+ args.push_back(type_arg);
+ }
+
+ // Shell service string can look like: shell[,arg1,arg2,...]:[command].
+ return android::base::StringPrintf("shell%s%s:%s",
+ args.empty() ? "" : ",",
+ android::base::Join(args, ',').c_str(),
+ command.c_str());
+}
+
+// Connects to a shell on the device and read/writes data.
+//
+// Note: currently this function doesn't properly clean up resources; the
+// FD connected to the adb server is never closed and the stdin read thread
+// may never exit.
+//
+// On success returns the remote exit code if |use_shell_protocol| is true,
+// 0 otherwise. On failure returns 1.
+static int RemoteShell(bool use_shell_protocol, const std::string& type_arg,
+ const std::string& command) {
+ std::string service_string = ShellServiceString(use_shell_protocol,
+ type_arg, command);
+
+ // Make local stdin raw if the device allocates a PTY, which happens if:
+ // 1. We are explicitly asking for a PTY shell, or
+ // 2. We don't specify shell type and are starting an interactive session.
+ bool raw_stdin = (type_arg == kShellServiceArgPty ||
+ (type_arg.empty() && command.empty()));
+
std::string error;
int fd = adb_connect(service_string, &error);
if (fd < 0) {
@@ -502,13 +555,16 @@
LOG(ERROR) << "couldn't allocate StdinReadArgs object";
return 1;
}
- args->stdin_fd = 0;
+ args->stdin_fd = STDIN_FILENO;
args->write_fd = fd;
+ args->raw_stdin = raw_stdin;
if (use_shell_protocol) {
args->protocol.reset(new ShellProtocol(args->write_fd));
}
- stdin_raw_init(args->stdin_fd);
+ if (raw_stdin) {
+ stdin_raw_init(STDIN_FILENO);
+ }
int exit_code = 0;
if (!adb_thread_create(stdin_read_thread, args)) {
@@ -519,7 +575,12 @@
exit_code = read_and_dump(fd, use_shell_protocol);
}
- stdin_raw_restore(args->stdin_fd);
+ if (raw_stdin) {
+ stdin_raw_restore(STDIN_FILENO);
+ }
+
+ // TODO(dpursell): properly exit stdin_read_thread and close |fd|.
+
return exit_code;
}
@@ -795,25 +856,6 @@
return adb_command(cmd);
}
-// Returns a shell service string with the indicated arguments and command.
-static std::string ShellServiceString(bool use_shell_protocol,
- const std::string& type_arg,
- const std::string& command) {
- std::vector<std::string> args;
- if (use_shell_protocol) {
- args.push_back(kShellServiceArgShellProtocol);
- }
- if (!type_arg.empty()) {
- args.push_back(type_arg);
- }
-
- // Shell service string can look like: shell[,arg1,arg2,...]:[command].
- return android::base::StringPrintf("shell%s%s:%s",
- args.empty() ? "" : ",",
- android::base::Join(args, ',').c_str(),
- command.c_str());
-}
-
// Connects to the device "shell" service with |command| and prints the
// resulting output.
static int send_shell_command(TransportType transport_type, const char* serial,
@@ -1320,51 +1362,26 @@
}
}
+ std::string command;
+ if (argc) {
+ // We don't escape here, just like ssh(1). http://b/20564385.
+ command = android::base::Join(
+ std::vector<const char*>(argv, argv + argc), ' ');
+ }
+
if (h) {
printf("\x1b[41;33m");
fflush(stdout);
}
- if (!argc) {
- D("starting interactive shell");
- std::string service_string =
- ShellServiceString(use_shell_protocol, shell_type_arg, "");
- r = interactive_shell(service_string, use_shell_protocol);
- if (h) {
- printf("\x1b[0m");
- fflush(stdout);
- }
- return r;
+ r = RemoteShell(use_shell_protocol, shell_type_arg, command);
+
+ if (h) {
+ printf("\x1b[0m");
+ fflush(stdout);
}
- // We don't escape here, just like ssh(1). http://b/20564385.
- std::string command = android::base::Join(
- std::vector<const char*>(argv, argv + argc), ' ');
- std::string service_string =
- ShellServiceString(use_shell_protocol, shell_type_arg, command);
-
- while (true) {
- D("non-interactive shell loop. cmd=%s", service_string.c_str());
- std::string error;
- int fd = adb_connect(service_string, &error);
- int r;
- if (fd >= 0) {
- D("about to read_and_dump(fd=%d)", fd);
- r = read_and_dump(fd, use_shell_protocol);
- D("read_and_dump() done.");
- adb_close(fd);
- } else {
- fprintf(stderr,"error: %s\n", error.c_str());
- r = -1;
- }
-
- if (h) {
- printf("\x1b[0m");
- fflush(stdout);
- }
- D("non-interactive shell loop. return r=%d", r);
- return r;
- }
+ return r;
}
else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
int exec_in = !strcmp(argv[0], "exec-in");
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index 8aeea81..544afce 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -382,7 +382,7 @@
subprocess->PassDataStreams();
subprocess->WaitForExit();
- D("deleting Subprocess");
+ D("deleting Subprocess for PID %d", subprocess->pid());
delete subprocess;
return nullptr;
@@ -501,11 +501,31 @@
return &protocol_sfd_;
}
- // We only care about stdin packets.
- if (stdinout_sfd_.valid() && input_->id() == ShellProtocol::kIdStdin) {
- input_bytes_left_ = input_->data_length();
- } else {
- input_bytes_left_ = 0;
+ if (stdinout_sfd_.valid()) {
+ switch (input_->id()) {
+ case ShellProtocol::kIdStdin:
+ input_bytes_left_ = input_->data_length();
+ break;
+ case ShellProtocol::kIdCloseStdin:
+ if (type_ == SubprocessType::kRaw) {
+ if (adb_shutdown(stdinout_sfd_.fd(), SHUT_WR) == 0) {
+ return nullptr;
+ }
+ PLOG(ERROR) << "failed to shutdown writes to FD "
+ << stdinout_sfd_.fd();
+ return &stdinout_sfd_;
+ } else {
+ // PTYs can't close just input, so rather than close the
+ // FD and risk losing subprocess output, leave it open.
+ // This only happens if the client starts a PTY shell
+ // non-interactively which is rare and unsupported.
+ // If necessary, the client can manually close the shell
+ // with `exit` or by killing the adb client process.
+ D("can't close input for PTY FD %d",
+ stdinout_sfd_.fd());
+ }
+ break;
+ }
}
}
@@ -532,7 +552,9 @@
ScopedFd* Subprocess::PassOutput(ScopedFd* sfd, ShellProtocol::Id id) {
int bytes = adb_read(sfd->fd(), output_->data(), output_->data_capacity());
if (bytes == 0 || (bytes < 0 && errno != EAGAIN)) {
- if (bytes < 0) {
+ // read() returns EIO if a PTY closes; don't report this as an error,
+ // it just means the subprocess completed.
+ if (bytes < 0 && !(type_ == SubprocessType::kPty && errno == EIO)) {
PLOG(ERROR) << "error reading output FD " << sfd->fd();
}
return sfd;
diff --git a/adb/shell_service.h b/adb/shell_service.h
index 8868f10..01410a9 100644
--- a/adb/shell_service.h
+++ b/adb/shell_service.h
@@ -50,11 +50,12 @@
public:
// This is an unscoped enum to make it easier to compare against raw bytes.
enum Id : uint8_t {
- kIdStdin = 0,
+ kIdStdin = 0,
kIdStdout = 1,
kIdStderr = 2,
- kIdExit = 3,
- kIdInvalid = 255, // Indicates an invalid or unknown packet.
+ kIdExit = 3,
+ kIdCloseStdin = 4, // Close subprocess stdin if possible.
+ kIdInvalid = 255, // Indicates an invalid or unknown packet.
};
// ShellPackets will probably be too large to allocate on the stack so they
diff --git a/adb/shell_service_test.cpp b/adb/shell_service_test.cpp
index 20efd84..e18f905 100644
--- a/adb/shell_service_test.cpp
+++ b/adb/shell_service_test.cpp
@@ -245,6 +245,25 @@
EXPECT_FALSE(stdout.find("--abc123--") == std::string::npos);
}
+// Tests closing raw subprocess stdin.
+TEST_F(ShellServiceTest, CloseClientStdin) {
+ ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+ "cat; echo TEST_DONE",
+ SubprocessType::kRaw, SubprocessProtocol::kShell));
+
+ std::string input = "foo\nbar";
+ ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
+ memcpy(protocol->data(), input.data(), input.length());
+ ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, input.length()));
+ ASSERT_TRUE(protocol->Write(ShellProtocol::kIdCloseStdin, 0));
+ delete protocol;
+
+ std::string stdout, stderr;
+ EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ ExpectLinesEqual(stdout, {"foo", "barTEST_DONE"});
+ ExpectLinesEqual(stderr, {});
+}
+
// Tests that nothing breaks when the stdin/stdout pipe closes.
TEST_F(ShellServiceTest, CloseStdinStdoutSubprocess) {
ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 5918a94..501a75a 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -488,6 +488,10 @@
{
return shutdown(fd, SHUT_RDWR);
}
+static __inline__ int adb_shutdown(int fd, int direction)
+{
+ return shutdown(fd, direction);
+}
#undef shutdown
#define shutdown ____xxx_shutdown
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 42f6d9b..994b851 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -3265,6 +3265,15 @@
// terminal.
return _console_read(_console_handle, buf, len);
} else {
+ // On older versions of Windows (definitely 7, definitely not 10),
+ // ReadConsole() with a size >= 31367 fails, so if |fd| is a console
+ // we need to limit the read size. This may also catch devices like NUL,
+ // but that is OK as we just want to avoid capping pipes and files which
+ // don't need size limiting. This isatty() test is very simple and quick
+ // and doesn't call the OS.
+ if (isatty(fd) && len > 4096) {
+ len = 4096;
+ }
// Just call into C Runtime which can read from pipes/files and which
// can do LF/CR translation (which is overridable with _setmode()).
// Undefine the macro that is set in sysdeps.h which bans calls to
diff --git a/adb/usb_linux_client.cpp b/adb/usb_linux_client.cpp
index 817917e..3495a71 100644
--- a/adb/usb_linux_client.cpp
+++ b/adb/usb_linux_client.cpp
@@ -88,8 +88,11 @@
__le32 fs_count;
__le32 hs_count;
__le32 ss_count;
+ __le32 os_count;
struct func_desc fs_descs, hs_descs;
struct ss_func_desc ss_descs;
+ struct usb_os_desc_header os_header;
+ struct usb_ext_compat_desc os_desc;
} __attribute__((packed));
static struct func_desc fs_descriptors = {
@@ -181,6 +184,24 @@
},
};
+struct usb_ext_compat_desc os_desc_compat = {
+ .bFirstInterfaceNumber = 0,
+ .Reserved1 = cpu_to_le32(1),
+ .CompatibleID = {0},
+ .SubCompatibleID = {0},
+ .Reserved2 = {0},
+};
+
+static struct usb_os_desc_header os_desc_header = {
+ .interface = cpu_to_le32(1),
+ .dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_desc_compat)),
+ .bcdVersion = cpu_to_le32(1),
+ .wIndex = cpu_to_le32(4),
+ .bCount = cpu_to_le32(1),
+ .Reserved = cpu_to_le32(0),
+};
+
+
#define STR_INTERFACE_ "ADB Interface"
static const struct {
@@ -332,13 +353,16 @@
v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
- FUNCTIONFS_HAS_SS_DESC;
+ FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC;
v2_descriptor.fs_count = 3;
v2_descriptor.hs_count = 3;
v2_descriptor.ss_count = 5;
+ v2_descriptor.os_count = 1;
v2_descriptor.fs_descs = fs_descriptors;
v2_descriptor.hs_descs = hs_descriptors;
v2_descriptor.ss_descs = ss_descriptors;
+ v2_descriptor.os_header = os_desc_header;
+ v2_descriptor.os_desc = os_desc_compat;
if (h->control < 0) { // might have already done this before
D("OPENING %s", USB_FFS_ADB_EP0);
diff --git a/crash_reporter/Android.mk b/crash_reporter/Android.mk
index 8756956..11dfcbc 100644
--- a/crash_reporter/Android.mk
+++ b/crash_reporter/Android.mk
@@ -60,6 +60,7 @@
LOCAL_REQUIRED_MODULES := core2md \
crash_reporter_logs.conf \
crash_sender \
+ crash_server \
dbus-send
LOCAL_INIT_RC := crash_reporter.rc
LOCAL_RTTI_FLAG := -frtti
@@ -92,6 +93,22 @@
LOCAL_SRC_FILES := $(warn_collector_src)
include $(BUILD_EXECUTABLE)
+# /etc/os-release.d/crash_server configuration file.
+# ========================================================
+ifdef OSRELEASED_DIRECTORY
+include $(CLEAR_VARS)
+LOCAL_MODULE := crash_server
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY)
+include $(BUILD_SYSTEM)/base_rules.mk
+
+# If the crash server isn't set, use a blank value. crash_sender
+# will log it as a configuration error.
+$(LOCAL_BUILT_MODULE): BRILLO_CRASH_SERVER ?= ""
+$(LOCAL_BUILT_MODULE):
+ echo $(BRILLO_CRASH_SERVER) > $@
+endif
+
# Crash reporter logs conf file.
# ========================================================
include $(CLEAR_VARS)
diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc
index ae56b4c..2a9d1d3 100644
--- a/crash_reporter/crash_collector.cc
+++ b/crash_reporter/crash_collector.cc
@@ -35,8 +35,8 @@
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/key_value_store.h>
-#include <chromeos/process.h>
+#include <brillo/key_value_store.h>
+#include <brillo/process.h>
namespace {
@@ -340,7 +340,7 @@
bool CrashCollector::GetLogContents(const FilePath &config_path,
const std::string &exec_name,
const FilePath &output_file) {
- chromeos::KeyValueStore store;
+ brillo::KeyValueStore store;
if (!store.Load(config_path)) {
LOG(INFO) << "Unable to read log configuration file "
<< config_path.value();
@@ -351,7 +351,7 @@
if (!store.GetString(exec_name, &command))
return false;
- chromeos::ProcessImpl diag_process;
+ brillo::ProcessImpl diag_process;
diag_process.AddArg(kShellPath);
diag_process.AddStringOption("-c", command);
diag_process.RedirectOutput(output_file.value());
diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc
index 32cbe9f..d00a5b5 100644
--- a/crash_reporter/crash_collector_test.cc
+++ b/crash_reporter/crash_collector_test.cc
@@ -22,14 +22,14 @@
#include <base/files/file_util.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/syslog_logging.h>
#include <gtest/gtest.h>
#include "crash_collector.h"
using base::FilePath;
using base::StringPrintf;
-using chromeos::FindLog;
+using brillo::FindLog;
using ::testing::Invoke;
using ::testing::Return;
@@ -54,7 +54,7 @@
collector_.Initialize(CountCrash, IsMetrics);
test_dir_ = FilePath("test");
base::CreateDirectory(test_dir_);
- chromeos::ClearLog();
+ brillo::ClearLog();
}
void TearDown() {
@@ -208,7 +208,7 @@
symlink(kMetaFileBasename,
meta_symlink_path.value().c_str()));
ASSERT_TRUE(base::PathExists(meta_symlink_path));
- chromeos::ClearLog();
+ brillo::ClearLog();
collector_.WriteCrashMetaData(meta_symlink_path,
"kernel",
payload_file.value());
@@ -221,7 +221,7 @@
// Test target of dangling symlink is not created.
base::DeleteFile(meta_file, false);
ASSERT_FALSE(base::PathExists(meta_file));
- chromeos::ClearLog();
+ brillo::ClearLog();
collector_.WriteCrashMetaData(meta_symlink_path, "kernel",
payload_file.value());
EXPECT_FALSE(base::PathExists(meta_file));
diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc
index 7872f7b..3955fe5 100644
--- a/crash_reporter/crash_reporter.cc
+++ b/crash_reporter/crash_reporter.cc
@@ -25,9 +25,9 @@
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/flag_helper.h>
-#include <chromeos/process.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/flag_helper.h>
+#include <brillo/process.h>
+#include <brillo/syslog_logging.h>
#include <metrics/metrics_library.h>
#include "kernel_collector.h"
@@ -102,7 +102,7 @@
// Note: This will mean that the dbus-send process will become a zombie and
// reparent to init for reaping, but that's OK -- see above.
- chromeos::ProcessImpl dbus_send;
+ brillo::ProcessImpl dbus_send;
dbus_send.AddArg("/system/bin/dbus-send");
dbus_send.AddArg("--type=signal");
dbus_send.AddArg("--system");
@@ -183,10 +183,10 @@
}
// Accumulate logs to help in diagnosing failures during user collection.
- chromeos::LogToString(true);
+ brillo::LogToString(true);
// Handle the crash, get the name of the process from procfs.
bool handled = user_collector->HandleCrash(user, nullptr);
- chromeos::LogToString(false);
+ brillo::LogToString(false);
if (!handled)
return 1;
return 0;
@@ -198,9 +198,9 @@
CHECK(!udev_event.empty()) << "--udev= must be set";
// Accumulate logs to help in diagnosing failures during user collection.
- chromeos::LogToString(true);
+ brillo::LogToString(true);
bool handled = udev_collector->HandleCrash(udev_event);
- chromeos::LogToString(false);
+ brillo::LogToString(false);
if (!handled)
return 1;
return 0;
@@ -209,9 +209,9 @@
static int HandleKernelWarning(KernelWarningCollector
*kernel_warning_collector) {
// Accumulate logs to help in diagnosing failures during collection.
- chromeos::LogToString(true);
+ brillo::LogToString(true);
bool handled = kernel_warning_collector->Collect();
- chromeos::LogToString(false);
+ brillo::LogToString(false);
if (!handled)
return 1;
return 0;
@@ -278,9 +278,9 @@
OpenStandardFileDescriptors();
FilePath my_path = base::MakeAbsoluteFilePath(FilePath(argv[0]));
s_metrics_lib.Init();
- chromeos::FlagHelper::Init(argc, argv, "Chromium OS Crash Reporter");
- chromeos::OpenLog(my_path.BaseName().value().c_str(), true);
- chromeos::InitLog(chromeos::kLogToSyslog);
+ brillo::FlagHelper::Init(argc, argv, "Chromium OS Crash Reporter");
+ brillo::OpenLog(my_path.BaseName().value().c_str(), true);
+ brillo::InitLog(brillo::kLogToSyslog);
KernelCollector kernel_collector;
kernel_collector.Initialize(CountKernelCrash, IsFeedbackAllowed);
diff --git a/crash_reporter/crash_reporter.rc b/crash_reporter/crash_reporter.rc
index 7bfe0c2..57c1d40 100644
--- a/crash_reporter/crash_reporter.rc
+++ b/crash_reporter/crash_reporter.rc
@@ -5,7 +5,7 @@
on property:crash_reporter.coredump.enabled=0
write /proc/sys/kernel/core_pattern "core"
-on boot
+on post-fs-data
# Allow catching multiple unrelated concurrent crashes, but use a finite
# number to prevent infinitely recursing on crash handling.
write /proc/sys/kernel/core_pipe_limit 4
diff --git a/crash_reporter/crash_reporter_logs_test.cc b/crash_reporter/crash_reporter_logs_test.cc
index c9ca02d..e778002 100644
--- a/crash_reporter/crash_reporter_logs_test.cc
+++ b/crash_reporter/crash_reporter_logs_test.cc
@@ -17,7 +17,7 @@
#include <string>
#include <base/files/file_path.h>
-#include <chromeos/key_value_store.h>
+#include <brillo/key_value_store.h>
#include <gtest/gtest.h>
namespace {
@@ -32,7 +32,7 @@
// Tests that the config file is parsable and that Chrome is listed.
TEST(CrashReporterLogsTest, ReadConfig) {
- chromeos::KeyValueStore store;
+ brillo::KeyValueStore store;
ASSERT_TRUE(store.Load(base::FilePath(kConfigFile)));
std::string command;
EXPECT_TRUE(store.GetString(kChromeExecName, &command));
diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender
index 8a422dd..5b859a8 100755
--- a/crash_reporter/crash_sender
+++ b/crash_reporter/crash_sender
@@ -78,6 +78,9 @@
# The weave configuration file.
WEAVE_CONF_FILE="/etc/weaved/weaved.conf"
+# The os-release.d folder.
+OSRELEASED_FOLDER="/etc/os-release.d"
+
# The syslog tag for all logging we emit.
TAG="$(basename $0)[$$]"
@@ -256,7 +259,12 @@
get_key_value() {
local file="$1" key="$2" value
- if [ -f "${file}" ]; then
+ if [ -f "${file}/${key}" ]; then
+ # Get the value from a folder where each key is its own file. The key
+ # file's entire contents is the value.
+ value=$(cat "${file}/${key}")
+ elif [ -f "${file}" ]; then
+ # Get the value from a file that has multiple key=value combinations.
# Return the first entry. There shouldn't be more than one anyways.
# Substr at length($1) + 2 skips past the key and following = sign (awk
# uses 1-based indexes), but preserves embedded = characters.
@@ -291,15 +299,15 @@
local report_payload="$(get_key_value "${meta_path}" "payload")"
local kind="$(get_kind "${meta_path}")"
local exec_name="$(get_key_value "${meta_path}" "exec_name")"
- local url="$(getprop crash_reporter.server)"
- local brillo_version="$(get_key_value "${meta_path}" "ver")"
+ local url="$(get_key_value "${OSRELEASED_FOLDER}" "crash_server")"
+ local bdk_version="$(get_key_value "${meta_path}" "bdk_version")"
local hwclass="$(get_hardware_class)"
local write_payload_size="$(get_key_value "${meta_path}" "payload_size")"
local log="$(get_key_value "${meta_path}" "log")"
local sig="$(get_key_value "${meta_path}" "sig")"
local send_payload_size="$(stat -c "%s" "${report_payload}" 2>/dev/null)"
- local product="$(get_key_value "${meta_path}" "upload_var_prod")"
- local version="$(get_key_value "${meta_path}" "upload_var_ver")"
+ local product="$(get_key_value "${meta_path}" "product_id")"
+ local version="$(get_key_value "${meta_path}" "product_version")"
local upload_prefix="$(get_key_value "${meta_path}" "upload_prefix")"
local guid
local model_manifest_id="$(get_key_value "${WEAVE_CONF_FILE}" "model_id")"
@@ -350,32 +358,13 @@
esac
done
- # When uploading Chrome reports we need to report the right product and
- # version. If the meta file does not specify it, use GOOGLE_CRASH_ID
- # as the product and GOOGLE_CRASH_VERSION_ID as the version.
- if [ "${product}" = "undefined" ]; then
- product="$(get_key_value /etc/os-release 'GOOGLE_CRASH_ID')"
- fi
- if [ "${version}" = "undefined" ]; then
- version="$(get_key_value /etc/os-release 'GOOGLE_CRASH_VERSION_ID')"
- fi
-
- # If GOOGLE_CRASH_* is undefined, we look for ID and VERSION_ID in
- # /etc/os-release.
- if [ "${product}" = "undefined" ]; then
- product="$(get_key_value /etc/os-release 'ID')"
- fi
- if [ "${version}" = "undefined" ]; then
- version="$(get_key_value /etc/os-release 'VERSION_ID')"
- fi
-
# If ID or VERSION_ID is undefined, we use the default product name
- # and CHROMEOS_RELEASE_VERSION from /etc/lsb-release.
+ # and bdk_version from /etc/os-release.d.
if [ "${product}" = "undefined" ]; then
product="${BRILLO_PRODUCT}"
fi
if [ "${version}" = "undefined" ]; then
- version="${brillo_version}"
+ version="${bdk_version}"
fi
local image_type
@@ -408,6 +397,7 @@
lecho " Metadata: ${meta_path} (${kind})"
lecho " Payload: ${report_payload}"
lecho " Version: ${version}"
+ lecho " Bdk Version: ${bdk_version}"
[ -n "${image_type}" ] && lecho " Image type: ${image_type}"
[ -n "${boot_mode}" ] && lecho " Boot mode: ${boot_mode}"
if is_mock; then
@@ -460,6 +450,7 @@
--capath "${RESTRICTED_CERTIFICATES_PATH}" --ciphers HIGH \
-F "prod=${product}" \
-F "ver=${version}" \
+ -F "bdk_version=${bdk_version}" \
-F "hwclass=${hwclass}" \
-F "exec_name=${exec_name}" \
-F "model_manifest_id=${model_manifest_id}" \
diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc
index e690b77..cdb0ae7 100644
--- a/crash_reporter/kernel_collector_test.cc
+++ b/crash_reporter/kernel_collector_test.cc
@@ -22,13 +22,13 @@
#include <base/files/scoped_temp_dir.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/syslog_logging.h>
#include <gtest/gtest.h>
using base::FilePath;
using base::StringPrintf;
-using chromeos::FindLog;
-using chromeos::GetLog;
+using brillo::FindLog;
+using brillo::GetLog;
namespace {
@@ -78,7 +78,7 @@
test_crash_directory_ = scoped_temp_dir_.path().Append("crash_directory");
ASSERT_TRUE(base::CreateDirectory(test_crash_directory_));
- chromeos::ClearLog();
+ brillo::ClearLog();
}
FilePath test_kcrash_;
@@ -282,7 +282,7 @@
ASSERT_EQ(1, s_crashes);
ASSERT_TRUE(FindLog("(handling)"));
static const char kNamePrefix[] = "Stored kcrash to ";
- std::string log = chromeos::GetLog();
+ std::string log = brillo::GetLog();
size_t pos = log.find(kNamePrefix);
ASSERT_NE(std::string::npos, pos)
<< "Did not find string \"" << kNamePrefix << "\" in log: {\n"
diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc
index a39441d..d445557 100644
--- a/crash_reporter/list_proxies.cc
+++ b/crash_reporter/list_proxies.cc
@@ -28,8 +28,8 @@
#include <base/strings/string_tokenizer.h>
#include <base/strings/string_util.h>
#include <base/values.h>
-#include <chromeos/daemons/dbus_daemon.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/daemons/dbus_daemon.h>
+#include <brillo/syslog_logging.h>
#include "libcrosservice/dbus-proxies.h"
@@ -111,7 +111,7 @@
// must be called, which blocks on the D-Bus call to Chrome. The call returns
// after either the timeout or the proxy has been resolved. The resolved
// proxies can then be accessed through the proxies() function.
-class ProxyResolver : public chromeos::DBusDaemon {
+class ProxyResolver : public brillo::DBusDaemon {
public:
ProxyResolver(const std::string& source_url,
const std::string& signal_interface,
@@ -138,7 +138,7 @@
timeout_callback_.callback(),
timeout_);
- return chromeos::DBusDaemon::Run();
+ return brillo::DBusDaemon::Run();
}
protected:
@@ -162,7 +162,7 @@
return;
}
- chromeos::ErrorPtr error;
+ brillo::ErrorPtr error;
call_proxy_->ResolveNetworkProxy(source_url_,
signal_interface_,
signal_name_,
@@ -189,7 +189,7 @@
}
int OnInit() override {
- int return_code = chromeos::DBusDaemon::OnInit();
+ int return_code = brillo::DBusDaemon::OnInit();
if (return_code != EX_OK)
return return_code;
@@ -276,13 +276,13 @@
}
// Default to logging to syslog.
- int init_flags = chromeos::kLogToSyslog;
+ int init_flags = brillo::kLogToSyslog;
// Log to stderr if a TTY (and "-quiet" wasn't passed), or if "-verbose"
// was passed.
if ((!quiet && isatty(STDERR_FILENO)) || verbose)
- init_flags |= chromeos::kLogToStderr;
- chromeos::InitLog(init_flags);
+ init_flags |= brillo::kLogToStderr;
+ brillo::InitLog(init_flags);
std::string url;
base::CommandLine::StringVector urls = cl->GetArgs();
diff --git a/crash_reporter/testrunner.cc b/crash_reporter/testrunner.cc
index a8c717e..744cf10 100644
--- a/crash_reporter/testrunner.cc
+++ b/crash_reporter/testrunner.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <chromeos/test_helpers.h>
+#include <brillo/test_helpers.h>
#include <gtest/gtest.h>
int main(int argc, char** argv) {
diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc
index 576fdbd..1e018db 100644
--- a/crash_reporter/udev_collector.cc
+++ b/crash_reporter/udev_collector.cc
@@ -27,7 +27,7 @@
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/process.h>
+#include <brillo/process.h>
using base::FilePath;
@@ -120,7 +120,7 @@
}
// Compress the output using gzip.
- chromeos::ProcessImpl gzip_process;
+ brillo::ProcessImpl gzip_process;
gzip_process.AddArg(kGzipPath);
gzip_process.AddArg(crash_path.value());
int process_result = gzip_process.Run();
diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc
index a6643fb..5474f48 100644
--- a/crash_reporter/udev_collector_test.cc
+++ b/crash_reporter/udev_collector_test.cc
@@ -18,7 +18,7 @@
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/syslog_logging.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -141,7 +141,7 @@
kLogConfigFileContents,
strlen(kLogConfigFileContents)));
- chromeos::ClearLog();
+ brillo::ClearLog();
}
UdevCollectorMock collector_;
diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc
index c5c0662..3bdeca1 100644
--- a/crash_reporter/unclean_shutdown_collector_test.cc
+++ b/crash_reporter/unclean_shutdown_collector_test.cc
@@ -20,12 +20,12 @@
#include <base/files/file_util.h>
#include <base/strings/string_util.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/syslog_logging.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using base::FilePath;
-using ::chromeos::FindLog;
+using ::brillo::FindLog;
namespace {
@@ -65,7 +65,7 @@
base::DeleteFile(test_unclean_, true);
// Set up an alternate power manager state file as well
collector_.powerd_suspended_file_ = FilePath(kTestSuspended);
- chromeos::ClearLog();
+ brillo::ClearLog();
}
protected:
diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc
index c4f02af..56e7bb9 100644
--- a/crash_reporter/user_collector.cc
+++ b/crash_reporter/user_collector.cc
@@ -37,8 +37,9 @@
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/process.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/osrelease_reader.h>
+#include <brillo/process.h>
+#include <brillo/syslog_logging.h>
#include <cutils/properties.h>
#include <private/android_filesystem_config.h>
@@ -58,11 +59,10 @@
const char *UserCollector::kUserId = "Uid:\t";
const char *UserCollector::kGroupId = "Gid:\t";
-// The property containing the OS version.
-const char kVersionProperty[] = "ro.build.id";
-
-// The property containing the product id.
-const char kProductIDProperty[] = "ro.product.product_id";
+// Product information keys in the /etc/os-release.d folder.
+static const char kBdkVersionKey[] = "bdk_version";
+static const char kProductIDKey[] = "product_id";
+static const char kProductVersionKey[] = "product_version";
using base::FilePath;
@@ -187,7 +187,7 @@
AddCrashMetaData("sig", kCollectionErrorSignature);
AddCrashMetaData("error_type", GetErrorTypeSignature(error_type));
std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid);
- std::string error_log = chromeos::GetLog();
+ std::string error_log = brillo::GetLog();
FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog");
if (GetLogContents(FilePath(log_config_path_), kCollectionErrorSignature,
diag_log_path)) {
@@ -367,7 +367,7 @@
const FilePath &minidump_path,
const FilePath &temp_directory) {
FilePath output_path = temp_directory.Append("output");
- chromeos::ProcessImpl core2md;
+ brillo::ProcessImpl core2md;
core2md.RedirectOutput(output_path.value());
core2md.AddArg(kCoreToMinidumpConverterPath);
core2md.AddArg(core_path.value());
@@ -505,11 +505,28 @@
if (GetLogContents(FilePath(log_config_path_), exec, log_path))
AddCrashMetaData("log", log_path.value());
- char value[PROPERTY_VALUE_MAX];
- property_get(kVersionProperty, value, "undefined");
- AddCrashMetaUploadData("ver", value);
- property_get(kProductIDProperty, value, "undefined");
- AddCrashMetaUploadData("prod", value);
+ brillo::OsReleaseReader reader;
+ reader.Load();
+ std::string value = "undefined";
+ if (!reader.GetString(kBdkVersionKey, &value)) {
+ LOG(ERROR) << "Could not read " << kBdkVersionKey
+ << " from /etc/os-release.d/";
+ }
+ AddCrashMetaData(kBdkVersionKey, value);
+
+ value = "undefined";
+ if (!reader.GetString(kProductIDKey, &value)) {
+ LOG(ERROR) << "Could not read " << kProductIDKey
+ << " from /etc/os-release.d/";
+ }
+ AddCrashMetaData(kProductIDKey, value);
+
+ value = "undefined";
+ if (!reader.GetString(kProductVersionKey, &value)) {
+ LOG(ERROR) << "Could not read " << kProductVersionKey
+ << " from /etc/os-release.d/";
+ }
+ AddCrashMetaData(kProductVersionKey, value);
ErrorType error_type =
ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path);
diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc
index 1dfac2a..72e61e6 100644
--- a/crash_reporter/user_collector_test.cc
+++ b/crash_reporter/user_collector_test.cc
@@ -23,12 +23,12 @@
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/string_split.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/syslog_logging.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using base::FilePath;
-using chromeos::FindLog;
+using brillo::FindLog;
namespace {
@@ -73,7 +73,7 @@
base::DeleteFile(FilePath("test"), true);
mkdir("test", 0777);
pid_ = getpid();
- chromeos::ClearLog();
+ brillo::ClearLog();
}
protected:
@@ -222,7 +222,7 @@
"GetSymlinkTarget failed - Path /proc/0 DirectoryExists: 0"));
EXPECT_TRUE(FindLog("stat /proc/0/exe failed: -1 2"));
- chromeos::ClearLog();
+ brillo::ClearLog();
pid_t my_pid = getpid();
EXPECT_TRUE(collector_.GetExecutableBaseNameFromPid(my_pid, &base_name));
EXPECT_FALSE(FindLog("Readlink failed"));
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index b47199f..28fff3f 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -1,49 +1,59 @@
# Copyright 2011 The Android Open Source Project
LOCAL_PATH:= $(call my-dir)
+
+common_static_libraries := \
+ liblogwrap \
+ libfec \
+ libfec_rs \
+ libbase \
+ libmincrypt \
+ libcrypto_static \
+ libext4_utils_static \
+ libsquashfs_utils
+
include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.cpp fs_mgr_fstab.c
-LOCAL_SRC_FILES += fs_mgr_format.c fs_mgr_slotselect.c
-
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
+LOCAL_SRC_FILES:= \
+ fs_mgr.c \
+ fs_mgr_format.c \
+ fs_mgr_fstab.c \
+ fs_mgr_slotselect.c \
+ fs_mgr_verity.cpp
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include \
system/vold \
system/extras/ext4_utils \
- external/openssl/include
-
+ external/openssl/include \
+ bootable/recovery
LOCAL_MODULE:= libfs_mgr
-LOCAL_STATIC_LIBRARIES := liblogwrap libmincrypt libext4_utils_static libsquashfs_utils libbase
-LOCAL_C_INCLUDES += system/extras/ext4_utils system/extras/squashfs_utils \
- bootable/recovery
+LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_CFLAGS := -Werror
-
ifneq (,$(filter userdebug,$(TARGET_BUILD_VARIANT)))
LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
endif
-
include $(BUILD_STATIC_LIBRARY)
-
-
include $(CLEAR_VARS)
-
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
LOCAL_SRC_FILES:= fs_mgr_main.c
-
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
LOCAL_MODULE:= fs_mgr
-
LOCAL_MODULE_TAGS := optional
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
-
-LOCAL_STATIC_LIBRARIES := libfs_mgr liblogwrap libcutils liblog libc libmincrypt libext4_utils_static libsquashfs_utils libbase
-LOCAL_STATIC_LIBRARIES += libsparse_static libz libselinux
+LOCAL_STATIC_LIBRARIES := libfs_mgr \
+ $(common_static_libraries) \
+ libcutils \
+ liblog \
+ libc \
+ libsparse_static \
+ libz \
+ libselinux
LOCAL_CXX_STL := libc++_static
-
LOCAL_CFLAGS := -Werror
-
include $(BUILD_EXECUTABLE)
-
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 8f7c6a2..c358982 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -38,8 +38,7 @@
#include "mincrypt/sha.h"
#include "mincrypt/sha256.h"
-#include "ext4_sb.h"
-#include "squashfs_utils.h"
+#include "fec/io.h"
#include "fs_mgr.h"
#include "fs_mgr_priv.h"
@@ -47,11 +46,19 @@
#define FSTAB_PREFIX "/fstab."
-#define VERITY_METADATA_SIZE 32768
#define VERITY_TABLE_RSA_KEY "/verity_key"
#define VERITY_TABLE_HASH_IDX 8
#define VERITY_TABLE_SALT_IDX 9
+#define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
+#define VERITY_TABLE_OPT_LOGGING "ignore_corruption"
+#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks"
+
+#define VERITY_TABLE_OPT_FEC_FORMAT \
+ "use_fec_from_device %s fec_start %" PRIu64 " fec_blocks %" PRIu64 \
+ " fec_roots %u " VERITY_TABLE_OPT_IGNZERO
+#define VERITY_TABLE_OPT_FEC_ARGS 9
+
#define METADATA_MAGIC 0x01564c54
#define METADATA_TAG_MAX_LENGTH 63
#define METADATA_EOD "eod"
@@ -92,7 +99,7 @@
}
if (!fread(key, sizeof(*key), 1, f)) {
- ERROR("Could not read key!");
+ ERROR("Could not read key!\n");
fclose(f);
free(key);
return NULL;
@@ -109,7 +116,8 @@
return key;
}
-static int verify_table(char *signature, char *table, int table_length)
+static int verify_table(const uint8_t *signature, const char *table,
+ uint32_t table_length)
{
RSAPublicKey *key;
uint8_t hash_buf[SHA256_DIGEST_SIZE];
@@ -121,17 +129,17 @@
// Now get the public key from the keyfile
key = load_key(VERITY_TABLE_RSA_KEY);
if (!key) {
- ERROR("Couldn't load verity keys");
+ ERROR("Couldn't load verity keys\n");
goto out;
}
// verify the result
if (!RSA_verify(key,
- (uint8_t*) signature,
+ signature,
RSANUMBYTES,
(uint8_t*) hash_buf,
SHA256_DIGEST_SIZE)) {
- ERROR("Couldn't verify table.");
+ ERROR("Couldn't verify table\n");
goto out;
}
@@ -142,11 +150,11 @@
return retval;
}
-static int invalidate_table(char *table, int table_length)
+static int invalidate_table(char *table, size_t table_length)
{
- int n = 0;
- int idx = 0;
- int cleared = 0;
+ size_t n = 0;
+ size_t idx = 0;
+ size_t cleared = 0;
while (n < table_length) {
if (table[n++] == ' ') {
@@ -169,177 +177,6 @@
return -1;
}
-static int squashfs_get_target_device_size(char *blk_device, uint64_t *device_size)
-{
- struct squashfs_info sq_info;
-
- if (squashfs_parse_sb(blk_device, &sq_info) >= 0) {
- *device_size = sq_info.bytes_used_4K_padded;
- return 0;
- } else {
- return -1;
- }
-}
-
-static int ext4_get_target_device_size(char *blk_device, uint64_t *device_size)
-{
- int data_device;
- struct ext4_super_block sb;
- struct fs_info info;
-
- info.len = 0; /* Only len is set to 0 to ask the device for real size. */
-
- data_device = TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC));
- if (data_device == -1) {
- ERROR("Error opening block device (%s)", strerror(errno));
- return -1;
- }
-
- if (TEMP_FAILURE_RETRY(lseek64(data_device, 1024, SEEK_SET)) < 0) {
- ERROR("Error seeking to superblock");
- close(data_device);
- return -1;
- }
-
- if (!android::base::ReadFully(data_device, &sb, sizeof(sb))) {
- ERROR("Error reading superblock");
- close(data_device);
- return -1;
- }
-
- ext4_parse_sb(&sb, &info);
- *device_size = info.len;
-
- close(data_device);
- return 0;
-}
-
-static int get_fs_size(char *fs_type, char *blk_device, uint64_t *device_size) {
- if (!strcmp(fs_type, "ext4")) {
- if (ext4_get_target_device_size(blk_device, device_size) < 0) {
- ERROR("Failed to get ext4 fs size on %s.", blk_device);
- return -1;
- }
- } else if (!strcmp(fs_type, "squashfs")) {
- if (squashfs_get_target_device_size(blk_device, device_size) < 0) {
- ERROR("Failed to get squashfs fs size on %s.", blk_device);
- return -1;
- }
- } else {
- ERROR("%s: Unsupported filesystem for verity.", fs_type);
- return -1;
- }
- return 0;
-}
-
-static int read_verity_metadata(uint64_t device_size, char *block_device, char **signature,
- char **table)
-{
- unsigned magic_number;
- unsigned table_length;
- int protocol_version;
- int device;
- int retval = FS_MGR_SETUP_VERITY_FAIL;
-
- *signature = NULL;
-
- if (table) {
- *table = NULL;
- }
-
- device = TEMP_FAILURE_RETRY(open(block_device, O_RDONLY | O_CLOEXEC));
- if (device == -1) {
- ERROR("Could not open block device %s (%s).\n", block_device, strerror(errno));
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(lseek64(device, device_size, SEEK_SET)) < 0) {
- ERROR("Could not seek to start of verity metadata block.\n");
- goto out;
- }
- // check the magic number
- if (!android::base::ReadFully(device, &magic_number, sizeof(magic_number))) {
- ERROR("Couldn't read magic number!\n");
- goto out;
- }
-
-#ifdef ALLOW_ADBD_DISABLE_VERITY
- if (magic_number == VERITY_METADATA_MAGIC_DISABLE) {
- retval = FS_MGR_SETUP_VERITY_DISABLED;
- INFO("Attempt to cleanly disable verity - only works in USERDEBUG");
- goto out;
- }
-#endif
-
- if (magic_number != VERITY_METADATA_MAGIC_NUMBER) {
- ERROR("Couldn't find verity metadata at offset %" PRIu64 "!\n", device_size);
- goto out;
- }
-
- // check the protocol version
- if (!android::base::ReadFully(device, &protocol_version,
- sizeof(protocol_version))) {
- ERROR("Couldn't read verity metadata protocol version!\n");
- goto out;
- }
- if (protocol_version != 0) {
- ERROR("Got unknown verity metadata protocol version %d!\n", protocol_version);
- goto out;
- }
-
- // get the signature
- *signature = (char*) malloc(RSANUMBYTES);
- if (!*signature) {
- ERROR("Couldn't allocate memory for signature!\n");
- goto out;
- }
- if (!android::base::ReadFully(device, *signature, RSANUMBYTES)) {
- ERROR("Couldn't read signature from verity metadata!\n");
- goto out;
- }
-
- if (!table) {
- retval = FS_MGR_SETUP_VERITY_SUCCESS;
- goto out;
- }
-
- // get the size of the table
- if (!android::base::ReadFully(device, &table_length, sizeof(table_length))) {
- ERROR("Couldn't get the size of the verity table from metadata!\n");
- goto out;
- }
-
- // get the table + null terminator
- *table = static_cast<char*>(malloc(table_length + 1));
- if (!*table) {
- ERROR("Couldn't allocate memory for verity table!\n");
- goto out;
- }
- if (!android::base::ReadFully(device, *table, table_length)) {
- ERROR("Couldn't read the verity table from metadata!\n");
- goto out;
- }
-
- (*table)[table_length] = 0;
- retval = FS_MGR_SETUP_VERITY_SUCCESS;
-
-out:
- if (device != -1)
- close(device);
-
- if (retval != FS_MGR_SETUP_VERITY_SUCCESS) {
- free(*signature);
- *signature = NULL;
-
- if (table) {
- free(*table);
- *table = NULL;
- }
- }
-
- return retval;
-}
-
static void verity_ioctl_init(struct dm_ioctl *io, char *name, unsigned flags)
{
memset(io, 0, DM_BUF_SIZE);
@@ -379,8 +216,76 @@
return 0;
}
-static int load_verity_table(struct dm_ioctl *io, char *name, uint64_t device_size, int fd, char *table,
- int mode)
+struct verity_table_params {
+ const char *table;
+ int mode;
+ struct fec_ecc_metadata ecc;
+ const char *ecc_dev;
+};
+
+typedef bool (*format_verity_table_func)(char *buf, const size_t bufsize,
+ const struct verity_table_params *params);
+
+static bool format_verity_table(char *buf, const size_t bufsize,
+ const struct verity_table_params *params)
+{
+ const char *mode_flag = NULL;
+ int res = -1;
+
+ if (params->mode == VERITY_MODE_RESTART) {
+ mode_flag = VERITY_TABLE_OPT_RESTART;
+ } else if (params->mode == VERITY_MODE_LOGGING) {
+ mode_flag = VERITY_TABLE_OPT_LOGGING;
+ }
+
+ if (params->ecc.valid) {
+ if (mode_flag) {
+ res = snprintf(buf, bufsize,
+ "%s %u %s " VERITY_TABLE_OPT_FEC_FORMAT,
+ params->table, 1 + VERITY_TABLE_OPT_FEC_ARGS, mode_flag, params->ecc_dev,
+ params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots);
+ } else {
+ res = snprintf(buf, bufsize,
+ "%s %u " VERITY_TABLE_OPT_FEC_FORMAT,
+ params->table, VERITY_TABLE_OPT_FEC_ARGS, params->ecc_dev,
+ params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots);
+ }
+ } else if (mode_flag) {
+ res = snprintf(buf, bufsize, "%s 2 " VERITY_TABLE_OPT_IGNZERO " %s", params->table,
+ mode_flag);
+ } else {
+ res = strlcpy(buf, params->table, bufsize);
+ }
+
+ if (res < 0 || (size_t)res >= bufsize) {
+ ERROR("Error building verity table; insufficient buffer size?\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool format_legacy_verity_table(char *buf, const size_t bufsize,
+ const struct verity_table_params *params)
+{
+ int res;
+
+ if (params->mode == VERITY_MODE_EIO) {
+ res = strlcpy(buf, params->table, bufsize);
+ } else {
+ res = snprintf(buf, bufsize, "%s %d", params->table, params->mode);
+ }
+
+ if (res < 0 || (size_t)res >= bufsize) {
+ ERROR("Error building verity table; insufficient buffer size?\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int load_verity_table(struct dm_ioctl *io, char *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;
@@ -390,35 +295,32 @@
struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
- // set tgt arguments here
+ // set tgt arguments
io->target_count = 1;
- tgt->status=0;
- tgt->sector_start=0;
- tgt->length=device_size/512;
+ tgt->status = 0;
+ tgt->sector_start = 0;
+ tgt->length = device_size / 512;
strcpy(tgt->target_type, "verity");
- // build the verity params here
+ // build the verity params
verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
bufsize = DM_BUF_SIZE - (verity_params - buffer);
- if (mode == VERITY_MODE_EIO) {
- // allow operation with older dm-verity drivers that are unaware
- // of the mode parameter by omitting it; this also means that we
- // cannot use logging mode with these drivers, they always cause
- // an I/O error for corrupted blocks
- strcpy(verity_params, table);
- } else if (snprintf(verity_params, bufsize, "%s %d", table, mode) < 0) {
+ if (!format(verity_params, bufsize, params)) {
+ ERROR("Failed to format verity parameters\n");
return -1;
}
+ INFO("loading verity table: '%s'", verity_params);
+
// set next target boundary
verity_params += strlen(verity_params) + 1;
- verity_params = (char*) (((unsigned long)verity_params + 7) & ~8);
+ verity_params = (char*)(((unsigned long)verity_params + 7) & ~8);
tgt->next = verity_params - buffer;
// send the ioctl to load the verity table
if (ioctl(fd, DM_TABLE_LOAD, io)) {
- ERROR("Error loading verity table (%s)", strerror(errno));
+ ERROR("Error loading verity table (%s)\n", strerror(errno));
return -1;
}
@@ -703,28 +605,31 @@
static int compare_last_signature(struct fstab_rec *fstab, int *match)
{
char tag[METADATA_TAG_MAX_LENGTH + 1];
- char *signature = NULL;
int fd = -1;
int rc = -1;
+ off64_t offset = 0;
+ struct fec_handle *f = NULL;
+ struct fec_verity_metadata verity;
uint8_t curr[SHA256_DIGEST_SIZE];
uint8_t prev[SHA256_DIGEST_SIZE];
- off64_t offset = 0;
- uint64_t device_size;
*match = 1;
- // get verity filesystem size
- if (get_fs_size(fstab->fs_type, fstab->blk_device, &device_size) < 0) {
- ERROR("Failed to get filesystem size\n");
+ if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
+ FEC_DEFAULT_ROOTS) == -1) {
+ ERROR("Failed to open '%s' (%s)\n", fstab->blk_device,
+ strerror(errno));
+ return rc;
+ }
+
+ // read verity metadata
+ if (fec_verity_get_metadata(f, &verity) == -1) {
+ ERROR("Failed to get verity metadata '%s' (%s)\n", fstab->blk_device,
+ strerror(errno));
goto out;
}
- if (read_verity_metadata(device_size, fstab->blk_device, &signature, NULL) < 0) {
- ERROR("Failed to read verity signature from %s\n", fstab->mount_point);
- goto out;
- }
-
- SHA256_hash(signature, RSANUMBYTES, curr);
+ SHA256_hash(verity.signature, RSANUMBYTES, curr);
if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s",
basename(fstab->mount_point)) >= (int)sizeof(tag)) {
@@ -766,12 +671,7 @@
rc = 0;
out:
- free(signature);
-
- if (fd != -1) {
- close(fd);
- }
-
+ fec_close(f);
return rc;
}
@@ -884,6 +784,7 @@
{
alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
bool use_state = true;
+ bool use_state_for_device = true;
char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
char *mount_point;
char propbuf[PROPERTY_VALUE_MAX];
@@ -928,10 +829,12 @@
continue;
}
+ use_state_for_device = use_state;
+
if (use_state) {
if (get_verity_state_offset(&fstab->recs[i], &offset) < 0 ||
read_verity_state(fstab->recs[i].verity_loc, offset, &mode) < 0) {
- continue;
+ use_state_for_device = false;
}
}
@@ -946,7 +849,7 @@
status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
- if (use_state && *status == 'C') {
+ if (use_state_for_device && *status == 'C') {
if (write_verity_state(fstab->recs[i].verity_loc, offset,
VERITY_MODE_LOGGING) < 0) {
continue;
@@ -972,87 +875,105 @@
return rc;
}
-int fs_mgr_setup_verity(struct fstab_rec *fstab) {
-
+int fs_mgr_setup_verity(struct fstab_rec *fstab)
+{
int retval = FS_MGR_SETUP_VERITY_FAIL;
int fd = -1;
- int mode;
-
- char *verity_blk_name = 0;
- char *verity_table = 0;
- char *verity_table_signature = 0;
- int verity_table_length = 0;
- uint64_t device_size = 0;
+ char *invalid_table = NULL;
+ char *verity_blk_name = NULL;
+ struct fec_handle *f = NULL;
+ struct fec_verity_metadata verity;
+ struct verity_table_params params;
alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
struct dm_ioctl *io = (struct dm_ioctl *) buffer;
char *mount_point = basename(fstab->mount_point);
- // get verity filesystem size
- if (get_fs_size(fstab->fs_type, fstab->blk_device, &device_size) < 0) {
+ if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
+ FEC_DEFAULT_ROOTS) < 0) {
+ ERROR("Failed to open '%s' (%s)\n", fstab->blk_device,
+ strerror(errno));
return retval;
}
- // read the verity block at the end of the block device
- // send error code up the chain so we can detect attempts to disable verity
- retval = read_verity_metadata(device_size,
- fstab->blk_device,
- &verity_table_signature,
- &verity_table);
- if (retval < 0) {
+ // read verity metadata
+ if (fec_verity_get_metadata(f, &verity) < 0) {
+ ERROR("Failed to get verity metadata '%s' (%s)\n", fstab->blk_device,
+ strerror(errno));
goto out;
}
- retval = FS_MGR_SETUP_VERITY_FAIL;
- verity_table_length = strlen(verity_table);
+#ifdef ALLOW_ADBD_DISABLE_VERITY
+ if (verity.disabled) {
+ retval = FS_MGR_SETUP_VERITY_DISABLED;
+ INFO("Attempt to cleanly disable verity - only works in USERDEBUG\n");
+ goto out;
+ }
+#endif
+
+ // read ecc metadata
+ if (fec_ecc_get_metadata(f, ¶ms.ecc) < 0) {
+ params.ecc.valid = false;
+ }
+
+ params.ecc_dev = fstab->blk_device;
// get the device mapper fd
if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
- ERROR("Error opening device mapper (%s)", strerror(errno));
+ ERROR("Error opening device mapper (%s)\n", strerror(errno));
goto out;
}
// create the device
if (create_verity_device(io, mount_point, fd) < 0) {
- ERROR("Couldn't create verity device!");
+ ERROR("Couldn't create verity device!\n");
goto out;
}
// get the name of the device file
if (get_verity_device_name(io, mount_point, fd, &verity_blk_name) < 0) {
- ERROR("Couldn't get verity device number!");
+ ERROR("Couldn't get verity device number!\n");
goto out;
}
- if (load_verity_state(fstab, &mode) < 0) {
+ 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
* restart loop, and no corrupted data will be exposed to userspace
* without a warning. */
- mode = VERITY_MODE_EIO;
+ params.mode = VERITY_MODE_EIO;
}
// verify the signature on the table
- if (verify_table(verity_table_signature,
- verity_table,
- verity_table_length) < 0) {
- if (mode == VERITY_MODE_LOGGING) {
+ if (verify_table(verity.signature, verity.table,
+ verity.table_length) < 0) {
+ if (params.mode == VERITY_MODE_LOGGING) {
// the user has been warned, allow mounting without dm-verity
retval = FS_MGR_SETUP_VERITY_SUCCESS;
goto out;
}
// invalidate root hash and salt to trigger device-specific recovery
- if (invalidate_table(verity_table, verity_table_length) < 0) {
+ invalid_table = strdup(verity.table);
+
+ if (!invalid_table ||
+ invalidate_table(invalid_table, verity.table_length) < 0) {
goto out;
}
+
+ params.table = invalid_table;
+ } else {
+ params.table = verity.table;
}
- INFO("Enabling dm-verity for %s (mode %d)\n", mount_point, mode);
+ INFO("Enabling dm-verity for %s (mode %d)\n", mount_point, params.mode);
// load the verity mapping table
- if (load_verity_table(io, mount_point, device_size, fd, verity_table,
- mode) < 0) {
+ if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
+ format_verity_table) < 0 &&
+ // try the legacy format for backwards compatibility
+ load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
+ format_legacy_verity_table) < 0) {
goto out;
}
@@ -1081,8 +1002,8 @@
close(fd);
}
- free(verity_table);
- free(verity_table_signature);
+ fec_close(f);
+ free(invalid_table);
free(verity_blk_name);
return retval;
diff --git a/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
new file mode 100644
index 0000000..9ee4abe
--- /dev/null
+++ b/include/ziparchive/zip_writer.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 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 LIBZIPARCHIVE_ZIPWRITER_H_
+#define LIBZIPARCHIVE_ZIPWRITER_H_
+
+#include "base/macros.h"
+#include <utils/Compat.h>
+
+#include <cstdio>
+#include <string>
+#include <ctime>
+#include <vector>
+
+/**
+ * Writes a Zip file via a stateful interface.
+ *
+ * Example:
+ *
+ * FILE* file = fopen("path/to/zip.zip", "wb");
+ *
+ * ZipWriter writer(file);
+ *
+ * writer.StartEntry("test.txt", ZipWriter::kCompress | ZipWriter::kAlign);
+ * writer.WriteBytes(buffer, bufferLen);
+ * writer.WriteBytes(buffer2, bufferLen2);
+ * writer.FinishEntry();
+ *
+ * writer.StartEntry("empty.txt", 0);
+ * writer.FinishEntry();
+ *
+ * writer.Finish();
+ *
+ * fclose(file);
+ */
+class ZipWriter {
+public:
+ enum {
+ /**
+ * Flag to compress the zip entry using deflate.
+ */
+ kCompress = 0x01,
+
+ /**
+ * Flag to align the zip entry data on a 32bit boundary. Useful for
+ * mmapping the data at runtime.
+ */
+ kAlign32 = 0x02,
+ };
+
+ static const char* ErrorCodeString(int32_t error_code);
+
+ /**
+ * Create a ZipWriter that will write into a FILE stream. The file should be opened with
+ * open mode of "wb" or "w+b". ZipWriter does not take ownership of the file stream. The
+ * caller is responsible for closing the file.
+ */
+ explicit ZipWriter(FILE* f);
+
+ // Move constructor.
+ ZipWriter(ZipWriter&& zipWriter);
+
+ // Move assignment.
+ ZipWriter& operator=(ZipWriter&& zipWriter);
+
+ /**
+ * Starts a new zip entry with the given path and flags.
+ * Flags can be a bitwise OR of ZipWriter::kCompress and ZipWriter::kAlign.
+ * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t StartEntry(const char* path, size_t flags);
+
+ /**
+ * Same as StartEntry(const char*, size_t), but sets a last modified time for the entry.
+ */
+ int32_t StartEntryWithTime(const char* path, size_t flags, time_t time);
+
+ /**
+ * Writes bytes to the zip file for the previously started zip entry.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t WriteBytes(const void* data, size_t len);
+
+ /**
+ * Finish a zip entry started with StartEntry(const char*, size_t) or
+ * StartEntryWithTime(const char*, size_t, time_t). This must be called before
+ * any new zip entries are started, or before Finish() is called.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t FinishEntry();
+
+ /**
+ * Writes the Central Directory Headers and flushes the zip file stream.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t Finish();
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ZipWriter);
+
+ int32_t HandleError(int32_t error_code);
+
+ struct FileInfo {
+ std::string path;
+ uint16_t compression_method;
+ uint32_t crc32;
+ uint32_t compressed_size;
+ uint32_t uncompressed_size;
+ uint16_t last_mod_time;
+ uint16_t last_mod_date;
+ uint32_t local_file_header_offset;
+ };
+
+ enum class State {
+ kWritingZip,
+ kWritingEntry,
+ kDone,
+ kError,
+ };
+
+ FILE* file_;
+ off64_t current_offset_;
+ State state_;
+ std::vector<FileInfo> files_;
+};
+
+#endif /* LIBZIPARCHIVE_ZIPWRITER_H_ */
diff --git a/init/Android.mk b/init/Android.mk
index c1cdc1a..8e45a7a 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -84,6 +84,8 @@
LOCAL_STATIC_LIBRARIES := \
libinit \
libfs_mgr \
+ libfec \
+ libfec_rs \
libsquashfs_utils \
liblogwrap \
libcutils \
@@ -94,6 +96,7 @@
libc \
libselinux \
libmincrypt \
+ libcrypto_static \
libc++_static \
libdl \
libsparse_static \
diff --git a/libcutils/tests/MemsetTest.cpp b/libcutils/tests/MemsetTest.cpp
index 45efc51..a98485f 100644
--- a/libcutils/tests/MemsetTest.cpp
+++ b/libcutils/tests/MemsetTest.cpp
@@ -20,6 +20,8 @@
#include <sys/mman.h>
#include <sys/types.h>
+#include <memory>
+
#include <cutils/memory.h>
#include <gtest/gtest.h>
@@ -127,14 +129,14 @@
min_incr = 2;
value |= value << 16;
}
- uint32_t* expected_buf = new uint32_t[MAX_TEST_SIZE/sizeof(uint32_t)];
+ std::unique_ptr<uint32_t[]> expected_buf(new uint32_t[MAX_TEST_SIZE/sizeof(uint32_t)]);
for (size_t i = 0; i < MAX_TEST_SIZE/sizeof(uint32_t); i++) {
expected_buf[i] = value;
}
// Allocate one large buffer with lots of extra space so that we can
// guarantee that all possible alignments will fit.
- uint8_t *buf = new uint8_t[3*MAX_TEST_SIZE];
+ std::unique_ptr<uint8_t[]> buf(new uint8_t[3*MAX_TEST_SIZE]);
uint8_t *buf_align;
for (size_t i = 0; i < num_aligns; i++) {
size_t incr = min_incr;
@@ -142,7 +144,7 @@
incr = GetIncrement(len, min_incr);
buf_align = reinterpret_cast<uint8_t*>(GetAlignedPtr(
- buf+FENCEPOST_LENGTH, align[i][0], align[i][1]));
+ buf.get()+FENCEPOST_LENGTH, align[i][0], align[i][1]));
SetFencepost(&buf_align[-FENCEPOST_LENGTH]);
SetFencepost(&buf_align[len]);
@@ -153,15 +155,13 @@
} else {
android_memset32(reinterpret_cast<uint32_t*>(buf_align), value, len);
}
- ASSERT_EQ(0, memcmp(expected_buf, buf_align, len))
+ ASSERT_EQ(0, memcmp(expected_buf.get(), buf_align, len))
<< "Failed size " << len << " align " << align[i][0] << " " << align[i][1] << "\n";
VerifyFencepost(&buf_align[-FENCEPOST_LENGTH]);
VerifyFencepost(&buf_align[len]);
}
}
- delete expected_buf;
- delete buf;
}
TEST(libcutils, android_memset16_non_zero) {
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
index 608ff1c..8ff94d4 100644
--- a/libziparchive/Android.mk
+++ b/libziparchive/Android.mk
@@ -15,7 +15,12 @@
LOCAL_PATH := $(call my-dir)
-source_files := zip_archive.cc
+source_files := zip_archive.cc zip_writer.cc
+test_files := zip_archive_test.cc zip_writer_test.cc entry_name_utils_test.cc
+
+# Incorrectly warns when C++11 empty brace {} initializer is used.
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
+common_cpp_flags := -Wno-missing-field-initializers
include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := .cc
@@ -24,7 +29,7 @@
LOCAL_SHARED_LIBRARIES := libutils libbase
LOCAL_MODULE:= libziparchive
LOCAL_CFLAGS := -Werror -Wall
-LOCAL_CPPFLAGS := -Wold-style-cast
+LOCAL_CPPFLAGS := -Wold-style-cast $(common_cpp_flags)
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
@@ -34,6 +39,8 @@
LOCAL_MODULE:= libziparchive-host
LOCAL_CFLAGS := -Werror
LOCAL_CFLAGS_windows := -mno-ms-bitfields
+LOCAL_CPPFLAGS := $(common_cpp_flags)
+
LOCAL_MULTILIB := both
LOCAL_MODULE_HOST_OS := darwin linux windows
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -45,6 +52,7 @@
LOCAL_SHARED_LIBRARIES := libz-host liblog libbase
LOCAL_MODULE:= libziparchive-host
LOCAL_CFLAGS := -Werror
+LOCAL_CPPFLAGS := $(common_cpp_flags)
LOCAL_MULTILIB := both
include $(BUILD_HOST_SHARED_LIBRARY)
@@ -53,7 +61,8 @@
LOCAL_MODULE := ziparchive-tests
LOCAL_CPP_EXTENSION := .cc
LOCAL_CFLAGS := -Werror
-LOCAL_SRC_FILES := zip_archive_test.cc entry_name_utils_test.cc
+LOCAL_CPPFLAGS := $(common_cpp_flags)
+LOCAL_SRC_FILES := $(test_files)
LOCAL_SHARED_LIBRARIES := liblog libbase
LOCAL_STATIC_LIBRARIES := libziparchive libz libutils
include $(BUILD_NATIVE_TEST)
@@ -61,10 +70,9 @@
include $(CLEAR_VARS)
LOCAL_MODULE := ziparchive-tests-host
LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS += \
- -Werror \
- -Wno-unnamed-type-template-args
-LOCAL_SRC_FILES := zip_archive_test.cc entry_name_utils_test.cc
+LOCAL_CFLAGS := -Werror
+LOCAL_CPPFLAGS := -Wno-unnamed-type-template-args $(common_cpp_flags)
+LOCAL_SRC_FILES := $(test_files)
LOCAL_SHARED_LIBRARIES := libziparchive-host liblog libbase
LOCAL_STATIC_LIBRARIES := \
libz \
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 3716343..f1e13a7 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -39,6 +39,7 @@
#include "zlib.h"
#include "entry_name_utils-inl.h"
+#include "zip_archive_common.h"
#include "ziparchive/zip_archive.h"
using android::base::get_unaligned;
@@ -49,161 +50,6 @@
#define O_BINARY 0
#endif
-// The "end of central directory" (EOCD) record. Each archive
-// contains exactly once such record which appears at the end of
-// the archive. It contains archive wide information like the
-// number of entries in the archive and the offset to the central
-// directory of the offset.
-struct EocdRecord {
- static const uint32_t kSignature = 0x06054b50;
-
- // End of central directory signature, should always be
- // |kSignature|.
- uint32_t eocd_signature;
- // The number of the current "disk", i.e, the "disk" that this
- // central directory is on.
- //
- // This implementation assumes that each archive spans a single
- // disk only. i.e, that disk_num == 1.
- uint16_t disk_num;
- // The disk where the central directory starts.
- //
- // This implementation assumes that each archive spans a single
- // disk only. i.e, that cd_start_disk == 1.
- uint16_t cd_start_disk;
- // The number of central directory records on this disk.
- //
- // This implementation assumes that each archive spans a single
- // disk only. i.e, that num_records_on_disk == num_records.
- uint16_t num_records_on_disk;
- // The total number of central directory records.
- uint16_t num_records;
- // The size of the central directory (in bytes).
- uint32_t cd_size;
- // The offset of the start of the central directory, relative
- // to the start of the file.
- uint32_t cd_start_offset;
- // Length of the central directory comment.
- uint16_t comment_length;
- private:
- EocdRecord() = default;
- DISALLOW_COPY_AND_ASSIGN(EocdRecord);
-} __attribute__((packed));
-
-// A structure representing the fixed length fields for a single
-// record in the central directory of the archive. In addition to
-// the fixed length fields listed here, each central directory
-// record contains a variable length "file_name" and "extra_field"
-// whose lengths are given by |file_name_length| and |extra_field_length|
-// respectively.
-struct CentralDirectoryRecord {
- static const uint32_t kSignature = 0x02014b50;
-
- // The start of record signature. Must be |kSignature|.
- uint32_t record_signature;
- // Tool version. Ignored by this implementation.
- uint16_t version_made_by;
- // Tool version. Ignored by this implementation.
- uint16_t version_needed;
- // The "general purpose bit flags" for this entry. The only
- // flag value that we currently check for is the "data descriptor"
- // flag.
- uint16_t gpb_flags;
- // The compression method for this entry, one of |kCompressStored|
- // and |kCompressDeflated|.
- uint16_t compression_method;
- // The file modification time and date for this entry.
- uint16_t last_mod_time;
- uint16_t last_mod_date;
- // The CRC-32 checksum for this entry.
- uint32_t crc32;
- // The compressed size (in bytes) of this entry.
- uint32_t compressed_size;
- // The uncompressed size (in bytes) of this entry.
- uint32_t uncompressed_size;
- // The length of the entry file name in bytes. The file name
- // will appear immediately after this record.
- uint16_t file_name_length;
- // The length of the extra field info (in bytes). This data
- // will appear immediately after the entry file name.
- uint16_t extra_field_length;
- // The length of the entry comment (in bytes). This data will
- // appear immediately after the extra field.
- uint16_t comment_length;
- // The start disk for this entry. Ignored by this implementation).
- uint16_t file_start_disk;
- // File attributes. Ignored by this implementation.
- uint16_t internal_file_attributes;
- // File attributes. Ignored by this implementation.
- uint32_t external_file_attributes;
- // The offset to the local file header for this entry, from the
- // beginning of this archive.
- uint32_t local_file_header_offset;
- private:
- CentralDirectoryRecord() = default;
- DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
-} __attribute__((packed));
-
-// The local file header for a given entry. This duplicates information
-// present in the central directory of the archive. It is an error for
-// the information here to be different from the central directory
-// information for a given entry.
-struct LocalFileHeader {
- static const uint32_t kSignature = 0x04034b50;
-
- // The local file header signature, must be |kSignature|.
- uint32_t lfh_signature;
- // Tool version. Ignored by this implementation.
- uint16_t version_needed;
- // The "general purpose bit flags" for this entry. The only
- // flag value that we currently check for is the "data descriptor"
- // flag.
- uint16_t gpb_flags;
- // The compression method for this entry, one of |kCompressStored|
- // and |kCompressDeflated|.
- uint16_t compression_method;
- // The file modification time and date for this entry.
- uint16_t last_mod_time;
- uint16_t last_mod_date;
- // The CRC-32 checksum for this entry.
- uint32_t crc32;
- // The compressed size (in bytes) of this entry.
- uint32_t compressed_size;
- // The uncompressed size (in bytes) of this entry.
- uint32_t uncompressed_size;
- // The length of the entry file name in bytes. The file name
- // will appear immediately after this record.
- uint16_t file_name_length;
- // The length of the extra field info (in bytes). This data
- // will appear immediately after the entry file name.
- uint16_t extra_field_length;
- private:
- LocalFileHeader() = default;
- DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
-} __attribute__((packed));
-
-struct DataDescriptor {
- // The *optional* data descriptor start signature.
- static const uint32_t kOptSignature = 0x08074b50;
-
- // CRC-32 checksum of the entry.
- uint32_t crc32;
- // Compressed size of the entry.
- uint32_t compressed_size;
- // Uncompressed size of the entry.
- uint32_t uncompressed_size;
- private:
- DataDescriptor() = default;
- DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
-} __attribute__((packed));
-
-
-static const uint32_t kGPBDDFlagMask = 0x0008; // mask value that signifies that the entry has a DD
-
-// The maximum size of a central directory or a file
-// comment in bytes.
-static const uint32_t kMaxCommentLen = 65535;
-
// The maximum number of bytes to scan backwards for the EOCD start.
static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
diff --git a/libziparchive/zip_archive_common.h b/libziparchive/zip_archive_common.h
new file mode 100644
index 0000000..7f20d51
--- /dev/null
+++ b/libziparchive/zip_archive_common.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2015 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 LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
+#define LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
+
+#include "base/macros.h"
+
+#include <inttypes.h>
+
+// The "end of central directory" (EOCD) record. Each archive
+// contains exactly once such record which appears at the end of
+// the archive. It contains archive wide information like the
+// number of entries in the archive and the offset to the central
+// directory of the offset.
+struct EocdRecord {
+ static const uint32_t kSignature = 0x06054b50;
+
+ // End of central directory signature, should always be
+ // |kSignature|.
+ uint32_t eocd_signature;
+ // The number of the current "disk", i.e, the "disk" that this
+ // central directory is on.
+ //
+ // This implementation assumes that each archive spans a single
+ // disk only. i.e, that disk_num == 1.
+ uint16_t disk_num;
+ // The disk where the central directory starts.
+ //
+ // This implementation assumes that each archive spans a single
+ // disk only. i.e, that cd_start_disk == 1.
+ uint16_t cd_start_disk;
+ // The number of central directory records on this disk.
+ //
+ // This implementation assumes that each archive spans a single
+ // disk only. i.e, that num_records_on_disk == num_records.
+ uint16_t num_records_on_disk;
+ // The total number of central directory records.
+ uint16_t num_records;
+ // The size of the central directory (in bytes).
+ uint32_t cd_size;
+ // The offset of the start of the central directory, relative
+ // to the start of the file.
+ uint32_t cd_start_offset;
+ // Length of the central directory comment.
+ uint16_t comment_length;
+ private:
+ EocdRecord() = default;
+ DISALLOW_COPY_AND_ASSIGN(EocdRecord);
+} __attribute__((packed));
+
+// A structure representing the fixed length fields for a single
+// record in the central directory of the archive. In addition to
+// the fixed length fields listed here, each central directory
+// record contains a variable length "file_name" and "extra_field"
+// whose lengths are given by |file_name_length| and |extra_field_length|
+// respectively.
+struct CentralDirectoryRecord {
+ static const uint32_t kSignature = 0x02014b50;
+
+ // The start of record signature. Must be |kSignature|.
+ uint32_t record_signature;
+ // Tool version. Ignored by this implementation.
+ uint16_t version_made_by;
+ // Tool version. Ignored by this implementation.
+ uint16_t version_needed;
+ // The "general purpose bit flags" for this entry. The only
+ // flag value that we currently check for is the "data descriptor"
+ // flag.
+ uint16_t gpb_flags;
+ // The compression method for this entry, one of |kCompressStored|
+ // and |kCompressDeflated|.
+ uint16_t compression_method;
+ // The file modification time and date for this entry.
+ uint16_t last_mod_time;
+ uint16_t last_mod_date;
+ // The CRC-32 checksum for this entry.
+ uint32_t crc32;
+ // The compressed size (in bytes) of this entry.
+ uint32_t compressed_size;
+ // The uncompressed size (in bytes) of this entry.
+ uint32_t uncompressed_size;
+ // The length of the entry file name in bytes. The file name
+ // will appear immediately after this record.
+ uint16_t file_name_length;
+ // The length of the extra field info (in bytes). This data
+ // will appear immediately after the entry file name.
+ uint16_t extra_field_length;
+ // The length of the entry comment (in bytes). This data will
+ // appear immediately after the extra field.
+ uint16_t comment_length;
+ // The start disk for this entry. Ignored by this implementation).
+ uint16_t file_start_disk;
+ // File attributes. Ignored by this implementation.
+ uint16_t internal_file_attributes;
+ // File attributes. Ignored by this implementation.
+ uint32_t external_file_attributes;
+ // The offset to the local file header for this entry, from the
+ // beginning of this archive.
+ uint32_t local_file_header_offset;
+ private:
+ CentralDirectoryRecord() = default;
+ DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
+} __attribute__((packed));
+
+// The local file header for a given entry. This duplicates information
+// present in the central directory of the archive. It is an error for
+// the information here to be different from the central directory
+// information for a given entry.
+struct LocalFileHeader {
+ static const uint32_t kSignature = 0x04034b50;
+
+ // The local file header signature, must be |kSignature|.
+ uint32_t lfh_signature;
+ // Tool version. Ignored by this implementation.
+ uint16_t version_needed;
+ // The "general purpose bit flags" for this entry. The only
+ // flag value that we currently check for is the "data descriptor"
+ // flag.
+ uint16_t gpb_flags;
+ // The compression method for this entry, one of |kCompressStored|
+ // and |kCompressDeflated|.
+ uint16_t compression_method;
+ // The file modification time and date for this entry.
+ uint16_t last_mod_time;
+ uint16_t last_mod_date;
+ // The CRC-32 checksum for this entry.
+ uint32_t crc32;
+ // The compressed size (in bytes) of this entry.
+ uint32_t compressed_size;
+ // The uncompressed size (in bytes) of this entry.
+ uint32_t uncompressed_size;
+ // The length of the entry file name in bytes. The file name
+ // will appear immediately after this record.
+ uint16_t file_name_length;
+ // The length of the extra field info (in bytes). This data
+ // will appear immediately after the entry file name.
+ uint16_t extra_field_length;
+ private:
+ LocalFileHeader() = default;
+ DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
+} __attribute__((packed));
+
+struct DataDescriptor {
+ // The *optional* data descriptor start signature.
+ static const uint32_t kOptSignature = 0x08074b50;
+
+ // CRC-32 checksum of the entry.
+ uint32_t crc32;
+ // Compressed size of the entry.
+ uint32_t compressed_size;
+ // Uncompressed size of the entry.
+ uint32_t uncompressed_size;
+ private:
+ DataDescriptor() = default;
+ DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
+} __attribute__((packed));
+
+// mask value that signifies that the entry has a DD
+static const uint32_t kGPBDDFlagMask = 0x0008;
+
+// The maximum size of a central directory or a file
+// comment in bytes.
+static const uint32_t kMaxCommentLen = 65535;
+
+#endif /* LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_ */
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
new file mode 100644
index 0000000..de75d1e
--- /dev/null
+++ b/libziparchive/zip_writer.cc
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2015 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 "entry_name_utils-inl.h"
+#include "zip_archive_common.h"
+#include "ziparchive/zip_writer.h"
+
+#include <cassert>
+#include <cstdio>
+#include <memory>
+#include <zlib.h>
+
+/* Zip compression methods we support */
+enum {
+ kCompressStored = 0, // no compression
+ kCompressDeflated = 8, // standard deflate
+};
+
+// No error, operation completed successfully.
+static const int32_t kNoError = 0;
+
+// The ZipWriter is in a bad state.
+static const int32_t kInvalidState = -1;
+
+// There was an IO error while writing to disk.
+static const int32_t kIoError = -2;
+
+// The zip entry name was invalid.
+static const int32_t kInvalidEntryName = -3;
+
+static const char* sErrorCodes[] = {
+ "Invalid state",
+ "IO error",
+ "Invalid entry name",
+};
+
+const char* ZipWriter::ErrorCodeString(int32_t error_code) {
+ if (error_code < 0 && (-error_code) < static_cast<int32_t>(arraysize(sErrorCodes))) {
+ return sErrorCodes[-error_code];
+ }
+ return nullptr;
+}
+
+ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip) {
+}
+
+ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
+ current_offset_(writer.current_offset_),
+ state_(writer.state_),
+ files_(std::move(writer.files_)) {
+ writer.file_ = nullptr;
+ writer.state_ = State::kError;
+}
+
+ZipWriter& ZipWriter::operator=(ZipWriter&& writer) {
+ file_ = writer.file_;
+ current_offset_ = writer.current_offset_;
+ state_ = writer.state_;
+ files_ = std::move(writer.files_);
+ writer.file_ = nullptr;
+ writer.state_ = State::kError;
+ return *this;
+}
+
+int32_t ZipWriter::HandleError(int32_t error_code) {
+ state_ = State::kError;
+ return error_code;
+}
+
+int32_t ZipWriter::StartEntry(const char* path, size_t flags) {
+ return StartEntryWithTime(path, flags, time_t());
+}
+
+static void ExtractTimeAndDate(time_t when, uint16_t* out_time, uint16_t* out_date) {
+ /* round up to an even number of seconds */
+ when = static_cast<time_t>((static_cast<unsigned long>(when) + 1) & (~1));
+
+ struct tm* ptm;
+#if !defined(_WIN32)
+ struct tm tm_result;
+ ptm = localtime_r(&when, &tm_result);
+#else
+ ptm = localtime(&when);
+#endif
+
+ int year = ptm->tm_year;
+ if (year < 80) {
+ year = 80;
+ }
+
+ *out_date = (year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday;
+ *out_time = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
+}
+
+int32_t ZipWriter::StartEntryWithTime(const char* path, size_t flags, time_t time) {
+ if (state_ != State::kWritingZip) {
+ return kInvalidState;
+ }
+
+ FileInfo fileInfo = {};
+ fileInfo.path = std::string(path);
+ fileInfo.local_file_header_offset = current_offset_;
+
+ if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(fileInfo.path.data()),
+ fileInfo.path.size())) {
+ return kInvalidEntryName;
+ }
+
+ LocalFileHeader header = {};
+ header.lfh_signature = LocalFileHeader::kSignature;
+
+ // Set this flag to denote that a DataDescriptor struct will appear after the data,
+ // containing the crc and size fields.
+ header.gpb_flags |= kGPBDDFlagMask;
+
+ // For now, ignore the ZipWriter::kCompress flag.
+ fileInfo.compression_method = kCompressStored;
+ header.compression_method = fileInfo.compression_method;
+
+ ExtractTimeAndDate(time, &fileInfo.last_mod_time, &fileInfo.last_mod_date);
+ header.last_mod_time = fileInfo.last_mod_time;
+ header.last_mod_date = fileInfo.last_mod_date;
+
+ header.file_name_length = fileInfo.path.size();
+
+ off64_t offset = current_offset_ + sizeof(header) + fileInfo.path.size();
+ if ((flags & ZipWriter::kAlign32) && (offset & 0x03)) {
+ // Pad the extra field so the data will be aligned.
+ uint16_t padding = 4 - (offset % 4);
+ header.extra_field_length = padding;
+ offset += padding;
+ }
+
+ if (fwrite(&header, sizeof(header), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+
+ if (fwrite(path, sizeof(*path), fileInfo.path.size(), file_) != fileInfo.path.size()) {
+ return HandleError(kIoError);
+ }
+
+ if (fwrite("\0\0\0", 1, header.extra_field_length, file_) != header.extra_field_length) {
+ return HandleError(kIoError);
+ }
+
+ files_.emplace_back(std::move(fileInfo));
+
+ current_offset_ = offset;
+ state_ = State::kWritingEntry;
+ return kNoError;
+}
+
+int32_t ZipWriter::WriteBytes(const void* data, size_t len) {
+ if (state_ != State::kWritingEntry) {
+ return HandleError(kInvalidState);
+ }
+
+ FileInfo& currentFile = files_.back();
+ if (currentFile.compression_method & kCompressDeflated) {
+ // TODO(adamlesinski): Implement compression using zlib deflate.
+ assert(false);
+ } else {
+ if (fwrite(data, 1, len, file_) != len) {
+ return HandleError(kIoError);
+ }
+ currentFile.crc32 = crc32(currentFile.crc32, reinterpret_cast<const Bytef*>(data), len);
+ currentFile.compressed_size += len;
+ current_offset_ += len;
+ }
+
+ currentFile.uncompressed_size += len;
+ return kNoError;
+}
+
+int32_t ZipWriter::FinishEntry() {
+ if (state_ != State::kWritingEntry) {
+ return kInvalidState;
+ }
+
+ const uint32_t sig = DataDescriptor::kOptSignature;
+ if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
+ state_ = State::kError;
+ return kIoError;
+ }
+
+ FileInfo& currentFile = files_.back();
+ DataDescriptor dd = {};
+ dd.crc32 = currentFile.crc32;
+ dd.compressed_size = currentFile.compressed_size;
+ dd.uncompressed_size = currentFile.uncompressed_size;
+ if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+
+ current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
+ state_ = State::kWritingZip;
+ return kNoError;
+}
+
+int32_t ZipWriter::Finish() {
+ if (state_ != State::kWritingZip) {
+ return kInvalidState;
+ }
+
+ off64_t startOfCdr = current_offset_;
+ for (FileInfo& file : files_) {
+ CentralDirectoryRecord cdr = {};
+ cdr.record_signature = CentralDirectoryRecord::kSignature;
+ cdr.gpb_flags |= kGPBDDFlagMask;
+ cdr.compression_method = file.compression_method;
+ cdr.last_mod_time = file.last_mod_time;
+ cdr.last_mod_date = file.last_mod_date;
+ cdr.crc32 = file.crc32;
+ cdr.compressed_size = file.compressed_size;
+ cdr.uncompressed_size = file.uncompressed_size;
+ cdr.file_name_length = file.path.size();
+ cdr.local_file_header_offset = file.local_file_header_offset;
+ if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+
+ if (fwrite(file.path.data(), 1, file.path.size(), file_) != file.path.size()) {
+ return HandleError(kIoError);
+ }
+
+ current_offset_ += sizeof(cdr) + file.path.size();
+ }
+
+ EocdRecord er = {};
+ er.eocd_signature = EocdRecord::kSignature;
+ er.disk_num = 1;
+ er.cd_start_disk = 1;
+ er.num_records_on_disk = files_.size();
+ er.num_records = files_.size();
+ er.cd_size = current_offset_ - startOfCdr;
+ er.cd_start_offset = startOfCdr;
+
+ if (fwrite(&er, sizeof(er), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+
+ if (fflush(file_) != 0) {
+ return HandleError(kIoError);
+ }
+
+ current_offset_ += sizeof(er);
+ state_ = State::kDone;
+ return kNoError;
+}
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
new file mode 100644
index 0000000..5269730
--- /dev/null
+++ b/libziparchive/zip_writer_test.cc
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2015 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 "ziparchive/zip_archive.h"
+#include "ziparchive/zip_writer.h"
+
+#include <base/test_utils.h>
+#include <gtest/gtest.h>
+#include <memory>
+
+struct zipwriter : public ::testing::Test {
+ TemporaryFile* temp_file_;
+ int fd_;
+ FILE* file_;
+
+ void SetUp() override {
+ temp_file_ = new TemporaryFile();
+ fd_ = temp_file_->fd;
+ file_ = fdopen(fd_, "w");
+ ASSERT_NE(file_, nullptr);
+ }
+
+ void TearDown() override {
+ fclose(file_);
+ delete temp_file_;
+ }
+};
+
+TEST_F(zipwriter, WriteUncompressedZipWithOneFile) {
+ ZipWriter writer(file_);
+
+ const char* expected = "hello";
+
+ ASSERT_EQ(writer.StartEntry("file.txt", 0), 0);
+ ASSERT_EQ(writer.WriteBytes("he", 2), 0);
+ ASSERT_EQ(writer.WriteBytes("llo", 3), 0);
+ ASSERT_EQ(writer.FinishEntry(), 0);
+ ASSERT_EQ(writer.Finish(), 0);
+
+ ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+
+ ZipEntry data;
+ ASSERT_EQ(FindEntry(handle, ZipString("file.txt"), &data), 0);
+ EXPECT_EQ(data.compressed_length, strlen(expected));
+ EXPECT_EQ(data.uncompressed_length, strlen(expected));
+ EXPECT_EQ(data.method, kCompressStored);
+
+ char buffer[6];
+ EXPECT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer)),
+ 0);
+ buffer[5] = 0;
+
+ EXPECT_STREQ(expected, buffer);
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipWithMultipleFiles) {
+ ZipWriter writer(file_);
+
+ ASSERT_EQ(writer.StartEntry("file.txt", 0), 0);
+ ASSERT_EQ(writer.WriteBytes("he", 2), 0);
+ ASSERT_EQ(writer.FinishEntry(), 0);
+
+ ASSERT_EQ(writer.StartEntry("file/file.txt", 0), 0);
+ ASSERT_EQ(writer.WriteBytes("llo", 3), 0);
+ ASSERT_EQ(writer.FinishEntry(), 0);
+
+ ASSERT_EQ(writer.StartEntry("file/file2.txt", 0), 0);
+ ASSERT_EQ(writer.FinishEntry(), 0);
+
+ ASSERT_EQ(writer.Finish(), 0);
+
+ ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+
+ char buffer[4];
+ ZipEntry data;
+
+ ASSERT_EQ(FindEntry(handle, ZipString("file.txt"), &data), 0);
+ EXPECT_EQ(data.method, kCompressStored);
+ EXPECT_EQ(data.compressed_length, 2u);
+ EXPECT_EQ(data.uncompressed_length, 2u);
+ ASSERT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)),
+ 0);
+ buffer[2] = 0;
+ EXPECT_STREQ("he", buffer);
+
+ ASSERT_EQ(FindEntry(handle, ZipString("file/file.txt"), &data), 0);
+ EXPECT_EQ(data.method, kCompressStored);
+ EXPECT_EQ(data.compressed_length, 3u);
+ EXPECT_EQ(data.uncompressed_length, 3u);
+ ASSERT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)),
+ 0);
+ buffer[3] = 0;
+ EXPECT_STREQ("llo", buffer);
+
+ ASSERT_EQ(FindEntry(handle, ZipString("file/file2.txt"), &data), 0);
+ EXPECT_EQ(data.method, kCompressStored);
+ EXPECT_EQ(data.compressed_length, 0u);
+ EXPECT_EQ(data.uncompressed_length, 0u);
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipWithAlignedFile) {
+ ZipWriter writer(file_);
+
+ ASSERT_EQ(writer.StartEntry("align.txt", ZipWriter::kAlign32), 0);
+ ASSERT_EQ(writer.WriteBytes("he", 2), 0);
+ ASSERT_EQ(writer.FinishEntry(), 0);
+ ASSERT_EQ(writer.Finish(), 0);
+
+ ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+
+ ZipEntry data;
+ ASSERT_EQ(FindEntry(handle, ZipString("align.txt"), &data), 0);
+ EXPECT_EQ(data.offset & 0x03, 0);
+}
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 1b68386..1de8e64 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -225,8 +225,11 @@
unsigned long maxSize = log_buffer_size(id);
if (sizes > maxSize) {
size_t sizeOver = sizes - ((maxSize * 9) / 10);
- size_t elements = stats.elements(id);
- size_t minElements = elements / 10;
+ size_t elements = stats.realElements(id);
+ size_t minElements = elements / 100;
+ if (minElements < minPrune) {
+ minElements = minPrune;
+ }
unsigned long pruneRows = elements * sizeOver / sizes;
if (pruneRows < minElements) {
pruneRows = minElements;
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 5223b40..7ed92e9 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -86,6 +86,7 @@
private:
+ static constexpr size_t minPrune = 4;
static constexpr size_t maxPrune = 256;
void maybePrune(log_id_t id);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index c3b10ad..fdb2576 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -27,6 +27,7 @@
log_id_for_each(id) {
mSizes[id] = 0;
mElements[id] = 0;
+ mDroppedElements[id] = 0;
mSizesTotal[id] = 0;
mElementsTotal[id] = 0;
}
@@ -93,6 +94,9 @@
unsigned short size = element->getMsgLen();
mSizes[log_id] -= size;
--mElements[log_id];
+ if (element->getDropped()) {
+ --mDroppedElements[log_id];
+ }
if (log_id == LOG_ID_KERNEL) {
return;
@@ -119,6 +123,7 @@
log_id_t log_id = element->getLogId();
unsigned short size = element->getMsgLen();
mSizes[log_id] -= size;
+ ++mDroppedElements[log_id];
uidTable[log_id].drop(element->getUid(), element);
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 8b90c53..6943820 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -374,6 +374,7 @@
class LogStatistics {
size_t mSizes[LOG_ID_MAX];
size_t mElements[LOG_ID_MAX];
+ size_t mDroppedElements[LOG_ID_MAX];
size_t mSizesTotal[LOG_ID_MAX];
size_t mElementsTotal[LOG_ID_MAX];
bool enable;
@@ -404,7 +405,11 @@
// entry->setDropped(1) must follow this call
void drop(LogBufferElement *entry);
// Correct for coalescing two entries referencing dropped content
- void erase(LogBufferElement *e) { --mElements[e->getLogId()]; }
+ void erase(LogBufferElement *element) {
+ log_id_t log_id = element->getLogId();
+ --mElements[log_id];
+ --mDroppedElements[log_id];
+ }
std::unique_ptr<const UidEntry *[]> sort(size_t len, log_id id) {
return uidTable[id].sort(len);
@@ -413,6 +418,9 @@
// fast track current value by id only
size_t sizes(log_id_t id) const { return mSizes[id]; }
size_t elements(log_id_t id) const { return mElements[id]; }
+ size_t realElements(log_id_t id) const {
+ return mElements[id] - mDroppedElements[id];
+ }
size_t sizesTotal(log_id_t id) const { return mSizesTotal[id]; }
size_t elementsTotal(log_id_t id) const { return mElementsTotal[id]; }
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
index 3deb7d3..8dbaad9 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -69,11 +69,11 @@
libchrome-dbus \
libchromeos-http \
libchromeos-dbus \
- libcutils \
libdbus \
libmetrics \
libprotobuf-cpp-lite \
librootdev \
+ libupdate_engine_client \
libweaved \
# Shared library for metrics.
diff --git a/metricsd/constants.h b/metricsd/constants.h
index 8a00fc4..7e1e116 100644
--- a/metricsd/constants.h
+++ b/metricsd/constants.h
@@ -27,10 +27,9 @@
static const char kFailedUploadCountName[] = "failed_upload_count";
static const char kDefaultVersion[] = "0.0.0.0";
-// System properties used.
-static const char kProductIdProperty[] = "ro.product.product_id";
-static const char kChannelProperty[] = "ro.product.channel";
-static const char kProductVersionProperty[] = "ro.product.version";
+// Build time properties name.
+static const char kProductId[] = "product_id";
+static const char kProductVersion[] = "product_version";
} // namespace metrics
#endif // METRICS_CONSTANTS_H_
diff --git a/metricsd/metrics_daemon.cc b/metricsd/metrics_daemon.cc
index 2ea5bbd..ed786e1 100644
--- a/metricsd/metrics_daemon.cc
+++ b/metricsd/metrics_daemon.cc
@@ -28,7 +28,7 @@
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <cutils/properties.h>
+#include <brillo/osrelease_reader.h>
#include <dbus/dbus.h>
#include <dbus/message.h>
@@ -140,7 +140,7 @@
version_cumulative_cpu_use_->Set(0);
}
- return chromeos::DBusDaemon::Run();
+ return brillo::DBusDaemon::Run();
}
void MetricsDaemon::RunUploaderTest() {
@@ -153,23 +153,19 @@
}
uint32_t MetricsDaemon::GetOsVersionHash() {
- static uint32_t cached_version_hash = 0;
- static bool version_hash_is_cached = false;
- if (version_hash_is_cached)
- return cached_version_hash;
- version_hash_is_cached = true;
-
- char version[PROPERTY_VALUE_MAX];
- // The version might not be set for development devices. In this case, use the
- // zero version.
- property_get(metrics::kProductVersionProperty, version,
- metrics::kDefaultVersion);
-
- cached_version_hash = base::Hash(version);
- if (testing_) {
- cached_version_hash = 42; // return any plausible value for the hash
+ brillo::OsReleaseReader reader;
+ reader.Load();
+ string version;
+ if (!reader.GetString(metrics::kProductVersion, &version)) {
+ LOG(ERROR) << "failed to read the product version.";
+ version = metrics::kDefaultVersion;
}
- return cached_version_hash;
+
+ uint32_t version_hash = base::Hash(version);
+ if (testing_) {
+ version_hash = 42; // return any plausible value for the hash
+ }
+ return version_hash;
}
void MetricsDaemon::Init(bool testing,
@@ -242,8 +238,8 @@
}
int MetricsDaemon::OnInit() {
- int return_code = dbus_enabled_ ? chromeos::DBusDaemon::OnInit() :
- chromeos::Daemon::OnInit();
+ int return_code = dbus_enabled_ ? brillo::DBusDaemon::OnInit() :
+ brillo::Daemon::OnInit();
if (return_code != EX_OK)
return return_code;
@@ -326,7 +322,7 @@
<< error.name << ": " << error.message;
}
}
- chromeos::DBusDaemon::OnShutdown(return_code);
+ brillo::DBusDaemon::OnShutdown(return_code);
}
void MetricsDaemon::OnEnableMetrics(const std::weak_ptr<weaved::Command>& cmd) {
@@ -368,7 +364,7 @@
if (!device_)
return;
- chromeos::VariantDictionary state_change{
+ brillo::VariantDictionary state_change{
{ "_metrics._AnalyticsReportingState",
metrics_lib_->AreMetricsEnabled() ? "enabled" : "disabled" }
};
diff --git a/metricsd/metrics_daemon.h b/metricsd/metrics_daemon.h
index 146108a..3d691c5 100644
--- a/metricsd/metrics_daemon.h
+++ b/metricsd/metrics_daemon.h
@@ -26,7 +26,7 @@
#include <base/files/file_path.h>
#include <base/memory/scoped_ptr.h>
#include <base/time/time.h>
-#include <chromeos/daemons/dbus_daemon.h>
+#include <brillo/daemons/dbus_daemon.h>
#include <libweaved/command.h>
#include <libweaved/device.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
@@ -39,7 +39,7 @@
using chromeos_metrics::PersistentInteger;
-class MetricsDaemon : public chromeos::DBusDaemon {
+class MetricsDaemon : public brillo::DBusDaemon {
public:
MetricsDaemon();
~MetricsDaemon();
diff --git a/metricsd/metrics_daemon.rc b/metricsd/metrics_daemon.rc
index 0e1fcd5..0ee577e 100644
--- a/metricsd/metrics_daemon.rc
+++ b/metricsd/metrics_daemon.rc
@@ -1,4 +1,4 @@
-on boot
+on post-fs-data
mkdir /data/misc/metrics 0770 system system
service metrics_daemon /system/bin/metrics_daemon --uploader -nodaemon
diff --git a/metricsd/metrics_daemon_main.cc b/metricsd/metrics_daemon_main.cc
index df13944..50c279d 100644
--- a/metricsd/metrics_daemon_main.cc
+++ b/metricsd/metrics_daemon_main.cc
@@ -18,8 +18,8 @@
#include <base/command_line.h>
#include <base/logging.h>
#include <base/strings/string_util.h>
-#include <chromeos/flag_helper.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/flag_helper.h>
+#include <brillo/syslog_logging.h>
#include <rootdev.h>
#include "constants.h"
@@ -79,11 +79,11 @@
metrics::kMetricsDirectory,
"Root of the configuration files (testing only)");
- chromeos::FlagHelper::Init(argc, argv, "Chromium OS Metrics Daemon");
+ brillo::FlagHelper::Init(argc, argv, "Chromium OS Metrics Daemon");
// Also log to stderr when not running as daemon.
- chromeos::InitLog(chromeos::kLogToSyslog | chromeos::kLogHeader |
- (FLAGS_daemon ? 0 : chromeos::kLogToStderr));
+ brillo::InitLog(brillo::kLogToSyslog | brillo::kLogHeader |
+ (FLAGS_daemon ? 0 : brillo::kLogToStderr));
if (FLAGS_daemon && daemon(0, 0) != 0) {
return errno;
diff --git a/metricsd/metrics_daemon_test.cc b/metricsd/metrics_daemon_test.cc
index 3a8fc3a..d3c9a23 100644
--- a/metricsd/metrics_daemon_test.cc
+++ b/metricsd/metrics_daemon_test.cc
@@ -20,7 +20,7 @@
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/string_number_conversions.h>
-#include <chromeos/flag_helper.h>
+#include <brillo/flag_helper.h>
#include <gtest/gtest.h>
#include "constants.h"
@@ -43,7 +43,7 @@
class MetricsDaemonTest : public testing::Test {
protected:
virtual void SetUp() {
- chromeos::FlagHelper::Init(0, nullptr, "");
+ brillo::FlagHelper::Init(0, nullptr, "");
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
scaling_max_freq_path_ = temp_dir_.path().Append("scaling_max");
cpu_max_freq_path_ = temp_dir_.path().Append("cpu_freq_max");
diff --git a/metricsd/uploader/sender_http.cc b/metricsd/uploader/sender_http.cc
index 953afc1..4b572a6 100644
--- a/metricsd/uploader/sender_http.cc
+++ b/metricsd/uploader/sender_http.cc
@@ -20,8 +20,8 @@
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
-#include <chromeos/http/http_utils.h>
-#include <chromeos/mime_utils.h>
+#include <brillo/http/http_utils.h>
+#include <brillo/mime_utils.h>
HttpSender::HttpSender(const std::string server_url)
: server_url_(server_url) {}
@@ -31,14 +31,14 @@
const std::string hash =
base::HexEncode(content_hash.data(), content_hash.size());
- chromeos::http::HeaderList headers = {{"X-Chrome-UMA-Log-SHA1", hash}};
- chromeos::ErrorPtr error;
- auto response = chromeos::http::PostTextAndBlock(
+ brillo::http::HeaderList headers = {{"X-Chrome-UMA-Log-SHA1", hash}};
+ brillo::ErrorPtr error;
+ auto response = brillo::http::PostTextAndBlock(
server_url_,
content,
- chromeos::mime::application::kWwwFormUrlEncoded,
+ brillo::mime::application::kWwwFormUrlEncoded,
headers,
- chromeos::http::Transport::CreateDefault(),
+ brillo::http::Transport::CreateDefault(),
&error);
if (!response || response->ExtractDataAsString() != "OK") {
if (error) {
diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc
index 1d87be5..1995510 100644
--- a/metricsd/uploader/system_profile_cache.cc
+++ b/metricsd/uploader/system_profile_cache.cc
@@ -21,8 +21,9 @@
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
-#include <cutils/properties.h>
+#include <brillo/osrelease_reader.h>
#include <string>
+#include <update_engine/client.h>
#include <vector>
#include "constants.h"
@@ -73,16 +74,27 @@
CHECK(!initialized_)
<< "this should be called only once in the metrics_daemon lifetime.";
- profile_.product_id = GetProperty(metrics::kProductIdProperty);
+ brillo::OsReleaseReader reader;
+ std::string channel;
+ if (testing_) {
+ reader.LoadTestingOnly(metrics_directory_);
+ channel = "unknown";
+ } else {
+ reader.Load();
+ auto client = update_engine::UpdateEngineClient::CreateInstance();
+ if (!client->GetChannel(&channel)) {
+ LOG(ERROR) << "failed to read the current channel from update engine.";
+ }
+ }
- if (profile_.product_id.empty()) {
- LOG(ERROR) << "System property " << metrics::kProductIdProperty
- << " is not set.";
+ if (!reader.GetString(metrics::kProductId, &profile_.product_id)) {
+ LOG(ERROR) << "product_id is not set.";
return false;
}
- std::string channel = GetProperty(metrics::kChannelProperty);
- profile_.version = GetProperty(metrics::kProductVersionProperty);
+ if (!reader.GetString(metrics::kProductVersion, &profile_.version)) {
+ LOG(ERROR) << "failed to read the product version";
+ }
if (channel.empty() || profile_.version.empty()) {
// If the channel or version is missing, the image is not official.
@@ -154,18 +166,6 @@
return guid;
}
-std::string SystemProfileCache::GetProperty(const std::string& name) {
- if (testing_) {
- std::string content;
- base::ReadFileToString(metrics_directory_.Append(name), &content);
- return content;
- } else {
- char value[PROPERTY_VALUE_MAX];
- property_get(name.data(), value, "");
- return std::string(value);
- }
-}
-
metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString(
const std::string& channel) {
if (channel == "stable") {
diff --git a/metricsd/uploader/system_profile_cache.h b/metricsd/uploader/system_profile_cache.h
index 3410157..97fb33a 100644
--- a/metricsd/uploader/system_profile_cache.h
+++ b/metricsd/uploader/system_profile_cache.h
@@ -76,11 +76,6 @@
// Initializes |profile_| only if it has not been yet initialized.
bool InitializeOrCheck();
- // Gets a system property as a string.
- // When |testing_| is true, reads the value from |metrics_directory_|/|name|
- // instead.
- std::string GetProperty(const std::string& name);
-
bool initialized_;
bool testing_;
base::FilePath metrics_directory_;
diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
index 305fd0c..236376a 100644
--- a/metricsd/uploader/upload_service_test.cc
+++ b/metricsd/uploader/upload_service_test.cc
@@ -58,9 +58,11 @@
}
void SetTestingProperty(const std::string& name, const std::string& value) {
+ base::FilePath filepath = dir_.path().Append("etc/os-release.d").Append(name);
+ ASSERT_TRUE(base::CreateDirectory(filepath.DirName()));
ASSERT_EQ(
value.size(),
- base::WriteFile(dir_.path().Append(name), value.data(), value.size()));
+ base::WriteFile(filepath, value.data(), value.size()));
}
base::ScopedTempDir dir_;
@@ -225,9 +227,8 @@
SenderMock* sender = new SenderMock();
upload_service_->sender_.reset(sender);
- SetTestingProperty(metrics::kChannelProperty, "beta");
- SetTestingProperty(metrics::kProductIdProperty, "hello");
- SetTestingProperty(metrics::kProductVersionProperty, "1.2.3.4");
+ SetTestingProperty(metrics::kProductId, "hello");
+ SetTestingProperty(metrics::kProductVersion, "1.2.3.4");
scoped_ptr<metrics::MetricSample> histogram =
metrics::MetricSample::SparseHistogramSample("myhistogram", 1);
@@ -242,8 +243,6 @@
EXPECT_TRUE(sender->is_good_proto());
EXPECT_EQ(1, sender->last_message_proto().histogram_event().size());
- EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_BETA,
- sender->last_message_proto().system_profile().channel());
EXPECT_NE(0, sender->last_message_proto().client_id());
EXPECT_NE(0, sender->last_message_proto().system_profile().build_timestamp());
EXPECT_NE(0, sender->last_message_proto().session_id());
@@ -269,7 +268,7 @@
}
TEST_F(UploadServiceTest, SessionIdIncrementedAtInitialization) {
- SetTestingProperty(metrics::kProductIdProperty, "hello");
+ SetTestingProperty(metrics::kProductId, "hello");
SystemProfileCache cache(true, dir_.path());
cache.Initialize();
int session_id = cache.profile_.session_id;