Merge "Disable container overflow detect on target."
diff --git a/adb/Android.mk b/adb/Android.mk
index 4ee1ced..2538e2e 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -294,11 +294,15 @@
libadbd \
libbase \
libfs_mgr \
+ libfec \
+ libfec_rs \
+ libselinux \
liblog \
libmincrypt \
- libselinux \
libext4_utils_static \
+ libsquashfs_utils \
libcutils \
libbase \
+ libcrypto_static
include $(BUILD_EXECUTABLE)
diff --git a/adb/adb_io_test.cpp b/adb/adb_io_test.cpp
index 84e3db6..6928a90 100644
--- a/adb/adb_io_test.cpp
+++ b/adb/adb_io_test.cpp
@@ -43,7 +43,7 @@
ASSERT_NE(-1, tf.fd);
ASSERT_TRUE(android::base::WriteStringToFd(expected, tf.fd)) << strerror(errno);
- ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
// Test reading the whole file.
char buf[sizeof(expected)] = {};
@@ -57,7 +57,7 @@
ASSERT_NE(-1, tf.fd);
ASSERT_TRUE(android::base::WriteStringToFd(expected, tf.fd)) << strerror(errno);
- ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
// Test that not having enough data will fail.
char buf[sizeof(expected) + 1] = {};
@@ -71,7 +71,7 @@
ASSERT_NE(-1, tf.fd);
ASSERT_TRUE(android::base::WriteStringToFd(input, tf.fd)) << strerror(errno);
- ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
// Test reading a partial file.
char buf[sizeof(input) - 1] = {};
@@ -90,7 +90,7 @@
// Test writing the whole string to the file.
ASSERT_TRUE(WriteFdExactly(tf.fd, expected, sizeof(expected)))
<< strerror(errno);
- ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
std::string s;
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
@@ -104,7 +104,7 @@
// Test writing a partial string to the file.
ASSERT_TRUE(WriteFdExactly(tf.fd, buf, sizeof(buf) - 2)) << strerror(errno);
- ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
std::string expected(buf);
expected.pop_back();
@@ -130,7 +130,7 @@
// Test writing a partial string to the file.
ASSERT_TRUE(WriteFdExactly(tf.fd, str)) << strerror(errno);
- ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
std::string s;
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
@@ -143,7 +143,7 @@
// Test writing a partial string to the file.
ASSERT_TRUE(WriteFdFmt(tf.fd, "Foo%s%d", "bar", 123)) << strerror(errno);
- ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
std::string s;
ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
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/set_verity_enable_state_service.cpp b/adb/set_verity_enable_state_service.cpp
index fd1740d..f5188e9 100644
--- a/adb/set_verity_enable_state_service.cpp
+++ b/adb/set_verity_enable_state_service.cpp
@@ -28,10 +28,11 @@
#include "adb.h"
#include "adb_io.h"
-#include "ext4_sb.h"
#include "fs_mgr.h"
#include "remount_service.h"
+#include "fec/io.h"
+
#define FSTAB_PREFIX "/fstab."
struct fstab *fstab;
@@ -41,115 +42,50 @@
static const bool kAllowDisableVerity = false;
#endif
-static int get_target_device_size(int fd, const 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 = adb_open(blk_device, O_RDONLY | O_CLOEXEC);
- if (data_device < 0) {
- WriteFdFmt(fd, "Error opening block device (%s)\n", strerror(errno));
- return -1;
- }
-
- if (lseek64(data_device, 1024, SEEK_SET) < 0) {
- WriteFdFmt(fd, "Error seeking to superblock\n");
- adb_close(data_device);
- return -1;
- }
-
- if (adb_read(data_device, &sb, sizeof(sb)) != sizeof(sb)) {
- WriteFdFmt(fd, "Error reading superblock\n");
- adb_close(data_device);
- return -1;
- }
-
- ext4_parse_sb(&sb, &info);
- *device_size = info.len;
-
- adb_close(data_device);
- return 0;
-}
-
/* Turn verity on/off */
static int set_verity_enabled_state(int fd, const char *block_device,
const char* mount_point, bool enable)
{
- uint32_t magic_number;
- const uint32_t new_magic = enable ? VERITY_METADATA_MAGIC_NUMBER
- : VERITY_METADATA_MAGIC_DISABLE;
- uint64_t device_length = 0;
- int device = -1;
- int retval = -1;
-
if (!make_block_device_writable(block_device)) {
WriteFdFmt(fd, "Could not make block device %s writable (%s).\n",
block_device, strerror(errno));
- goto errout;
+ return -1;
}
- device = adb_open(block_device, O_RDWR | O_CLOEXEC);
- if (device == -1) {
+ fec::io fh(block_device, O_RDWR);
+
+ if (!fh) {
WriteFdFmt(fd, "Could not open block device %s (%s).\n", block_device, strerror(errno));
- WriteFdFmt(fd, "Maybe run adb remount?\n");
- goto errout;
+ WriteFdFmt(fd, "Maybe run adb root?\n");
+ return -1;
}
- // find the start of the verity metadata
- if (get_target_device_size(fd, (char*)block_device, &device_length) < 0) {
- WriteFdFmt(fd, "Could not get target device size.\n");
- goto errout;
+ fec_verity_metadata metadata;
+
+ if (!fh.get_verity_metadata(metadata)) {
+ WriteFdFmt(fd, "Couldn't find verity metadata!\n");
+ return -1;
}
- if (lseek64(device, device_length, SEEK_SET) < 0) {
- WriteFdFmt(fd, "Could not seek to start of verity metadata block.\n");
- goto errout;
- }
-
- // check the magic number
- if (adb_read(device, &magic_number, sizeof(magic_number)) != sizeof(magic_number)) {
- WriteFdFmt(fd, "Couldn't read magic number!\n");
- goto errout;
- }
-
- if (!enable && magic_number == VERITY_METADATA_MAGIC_DISABLE) {
+ if (!enable && metadata.disabled) {
WriteFdFmt(fd, "Verity already disabled on %s\n", mount_point);
- goto errout;
+ return -1;
}
- if (enable && magic_number == VERITY_METADATA_MAGIC_NUMBER) {
+ if (enable && !metadata.disabled) {
WriteFdFmt(fd, "Verity already enabled on %s\n", mount_point);
- goto errout;
+ return -1;
}
- if (magic_number != VERITY_METADATA_MAGIC_NUMBER
- && magic_number != VERITY_METADATA_MAGIC_DISABLE) {
- WriteFdFmt(fd, "Couldn't find verity metadata at offset %" PRIu64 "!\n", device_length);
- goto errout;
- }
-
- if (lseek64(device, device_length, SEEK_SET) < 0) {
- WriteFdFmt(fd, "Could not seek to start of verity metadata block.\n");
- goto errout;
- }
-
- if (adb_write(device, &new_magic, sizeof(new_magic)) != sizeof(new_magic)) {
+ if (!fh.set_verity_status(enable)) {
WriteFdFmt(fd, "Could not set verity %s flag on device %s with error %s\n",
enable ? "enabled" : "disabled",
block_device, strerror(errno));
- goto errout;
+ return -1;
}
WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
- retval = 0;
-errout:
- if (device != -1)
- adb_close(device);
- return retval;
+ return 0;
}
void set_verity_enabled_state_service(int fd, void* cookie)
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..51d09a6 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -319,6 +319,9 @@
#define getcwd adb_getcwd
+char* adb_strerror(int err);
+#define strerror adb_strerror
+
// Convert from UTF-8 to UTF-16, typically used to convert char strings into
// wchar_t strings that can be passed to wchar_t-based OS and C Runtime APIs
// on Windows.
@@ -488,6 +491,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..fd75320 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -556,6 +556,82 @@
return 0;
}
+// Overrides strerror() to handle error codes not supported by the Windows C
+// Runtime (MSVCRT.DLL).
+char* adb_strerror(int err) {
+ // sysdeps.h defines strerror to adb_strerror, but in this function, we
+ // want to call the real C Runtime strerror().
+#pragma push_macro("strerror")
+#undef strerror
+ const int saved_err = errno; // Save because we overwrite it later.
+
+ // Lookup the string for an unknown error.
+ char* errmsg = strerror(-1);
+ char unknown_error[(errmsg == nullptr ? 0 : strlen(errmsg)) + 1];
+ strcpy(unknown_error, errmsg == nullptr ? "" : errmsg);
+
+ // Lookup the string for this error to see if the C Runtime has it.
+ errmsg = strerror(err);
+ if ((errmsg != nullptr) && strcmp(errmsg, unknown_error)) {
+ // The CRT returned an error message and it is different than the error
+ // message for an unknown error, so it is probably valid, so use it.
+ } else {
+ // Check if we have a string for this error code.
+ const char* custom_msg = nullptr;
+ switch (err) {
+#pragma push_macro("ERR")
+#undef ERR
+#define ERR(errnum, desc) case errnum: custom_msg = desc; break
+ // These error strings are from AOSP bionic/libc/include/sys/_errdefs.h.
+ // Note that these cannot be longer than 94 characters because we
+ // pass this to _strerror() which has that requirement.
+ ERR(ECONNRESET, "Connection reset by peer");
+ ERR(EHOSTUNREACH, "No route to host");
+ ERR(ENETDOWN, "Network is down");
+ ERR(ENETRESET, "Network dropped connection because of reset");
+ ERR(ENOBUFS, "No buffer space available");
+ ERR(ENOPROTOOPT, "Protocol not available");
+ ERR(ENOTCONN, "Transport endpoint is not connected");
+ ERR(ENOTSOCK, "Socket operation on non-socket");
+ ERR(EOPNOTSUPP, "Operation not supported on transport endpoint");
+#pragma pop_macro("ERR")
+ }
+
+ if (custom_msg != nullptr) {
+ // Use _strerror() to write our string into the writable per-thread
+ // buffer used by strerror()/_strerror(). _strerror() appends the
+ // msg for the current value of errno, so set errno to a consistent
+ // value for every call so that our code-path is always the same.
+ errno = 0;
+ errmsg = _strerror(custom_msg);
+ const size_t custom_msg_len = strlen(custom_msg);
+ // Just in case _strerror() returned a read-only string, check if
+ // the returned string starts with our custom message because that
+ // implies that the string is not read-only.
+ if ((errmsg != nullptr) &&
+ !strncmp(custom_msg, errmsg, custom_msg_len)) {
+ // _strerror() puts other text after our custom message, so
+ // remove that by terminating after our message.
+ errmsg[custom_msg_len] = '\0';
+ } else {
+ // For some reason nullptr was returned or a pointer to a
+ // read-only string was returned, so fallback to whatever
+ // strerror() can muster (probably "Unknown error" or some
+ // generic CRT error string).
+ errmsg = strerror(err);
+ }
+ } else {
+ // We don't have a custom message, so use whatever strerror(err)
+ // returned earlier.
+ }
+ }
+
+ errno = saved_err; // restore
+
+ return errmsg;
+#pragma pop_macro("strerror")
+}
+
/**************************************************************************/
/**************************************************************************/
/***** *****/
@@ -567,18 +643,38 @@
#undef setsockopt
static void _socket_set_errno( const DWORD err ) {
- // The Windows C Runtime (MSVCRT.DLL) strerror() does not support a lot of
- // POSIX and socket error codes, so this can only meaningfully map so much.
+ // Because the Windows C Runtime (MSVCRT.DLL) strerror() does not support a
+ // lot of POSIX and socket error codes, some of the resulting error codes
+ // are mapped to strings by adb_strerror() above.
switch ( err ) {
case 0: errno = 0; break;
+ // Don't map WSAEINTR since that is only for Winsock 1.1 which we don't use.
+ // case WSAEINTR: errno = EINTR; break;
+ case WSAEFAULT: errno = EFAULT; break;
+ case WSAEINVAL: errno = EINVAL; break;
+ case WSAEMFILE: errno = EMFILE; break;
// Mapping WSAEWOULDBLOCK to EAGAIN is absolutely critical because
// non-blocking sockets can cause an error code of WSAEWOULDBLOCK and
// callers check specifically for EAGAIN.
case WSAEWOULDBLOCK: errno = EAGAIN; break;
- case WSAEINTR: errno = EINTR; break;
- case WSAEFAULT: errno = EFAULT; break;
- case WSAEINVAL: errno = EINVAL; break;
- case WSAEMFILE: errno = EMFILE; break;
+ case WSAENOTSOCK: errno = ENOTSOCK; break;
+ case WSAENOPROTOOPT: errno = ENOPROTOOPT; break;
+ case WSAEOPNOTSUPP: errno = EOPNOTSUPP; break;
+ case WSAENETDOWN: errno = ENETDOWN; break;
+ case WSAENETRESET: errno = ENETRESET; break;
+ // Map WSAECONNABORTED to EPIPE instead of ECONNABORTED because POSIX seems
+ // to use EPIPE for these situations and there are some callers that look
+ // for EPIPE.
+ case WSAECONNABORTED: errno = EPIPE; break;
+ case WSAECONNRESET: errno = ECONNRESET; break;
+ case WSAENOBUFS: errno = ENOBUFS; break;
+ case WSAENOTCONN: errno = ENOTCONN; break;
+ // Don't map WSAETIMEDOUT because we don't currently use SO_RCVTIMEO or
+ // SO_SNDTIMEO which would cause WSAETIMEDOUT to be returned. Future
+ // considerations: Reportedly send() can return zero on timeout, and POSIX
+ // code may expect EAGAIN instead of ETIMEDOUT on timeout.
+ // case WSAETIMEDOUT: errno = ETIMEDOUT; break;
+ case WSAEHOSTUNREACH: errno = EHOSTUNREACH; break;
default:
errno = EINVAL;
D( "_socket_set_errno: mapping Windows error code %lu to errno %d",
@@ -655,8 +751,12 @@
int result = send(f->fh_socket, reinterpret_cast<const char*>(buf), len, 0);
if (result == SOCKET_ERROR) {
const DWORD err = WSAGetLastError();
- D("send fd %d failed: %s", _fh_to_int(f),
- SystemErrorCodeToString(err).c_str());
+ // WSAEWOULDBLOCK is normal with a non-blocking socket, so don't trace
+ // that to reduce spam and confusion.
+ if (err != WSAEWOULDBLOCK) {
+ D("send fd %d failed: %s", _fh_to_int(f),
+ SystemErrorCodeToString(err).c_str());
+ }
_socket_set_errno(err);
result = -1;
} else {
@@ -3265,6 +3365,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/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
index cc3ac5c..66d1ba8 100755
--- a/adb/sysdeps_win32_test.cpp
+++ b/adb/sysdeps_win32_test.cpp
@@ -67,3 +67,29 @@
EXPECT_GT(strlen(path_val), 0);
}
}
+
+void TestAdbStrError(int err, const char* expected) {
+ errno = 12345;
+ const char* result = adb_strerror(err);
+ // Check that errno is not overwritten.
+ EXPECT_EQ(12345, errno);
+ EXPECT_STREQ(expected, result);
+}
+
+TEST(sysdeps_win32, adb_strerror) {
+ // Test an error code that should not have a mapped string. Use an error
+ // code that is not used by the internal implementation of adb_strerror().
+ TestAdbStrError(-2, "Unknown error");
+ // adb_strerror() uses -1 internally, so test that it can still be passed
+ // as a parameter.
+ TestAdbStrError(-1, "Unknown error");
+ // Test very big, positive unknown error.
+ TestAdbStrError(1000000, "Unknown error");
+ // Test success case.
+ TestAdbStrError(0, "No error");
+ // Test error that regular strerror() should have a string for.
+ TestAdbStrError(EPERM, "Operation not permitted");
+ // Test error that regular strerror() doesn't have a string for, but that
+ // adb_strerror() returns.
+ TestAdbStrError(ECONNRESET, "Connection reset by peer");
+}
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/base/logging_test.cpp b/base/logging_test.cpp
index 9cf1aad..70f9952 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -139,7 +139,7 @@
{
CapturedStderr cap;
LOG(WARNING) << "foobar";
- ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
std::string output;
android::base::ReadFdToString(cap.fd(), &output);
@@ -155,7 +155,7 @@
{
CapturedStderr cap;
LOG(INFO) << "foobar";
- ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
std::string output;
android::base::ReadFdToString(cap.fd(), &output);
@@ -171,7 +171,7 @@
{
CapturedStderr cap;
LOG(DEBUG) << "foobar";
- ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
std::string output;
android::base::ReadFdToString(cap.fd(), &output);
@@ -182,7 +182,7 @@
android::base::ScopedLogSeverity severity(android::base::DEBUG);
CapturedStderr cap;
LOG(DEBUG) << "foobar";
- ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
std::string output;
android::base::ReadFdToString(cap.fd(), &output);
@@ -202,7 +202,7 @@
LOG(INFO) << (errno = 67890);
EXPECT_EQ(12345, errno) << "errno was not restored";
- ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
std::string output;
android::base::ReadFdToString(cap.fd(), &output);
@@ -245,7 +245,7 @@
CapturedStderr cap;
errno = ENOENT;
PLOG(INFO) << "foobar";
- ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
std::string output;
android::base::ReadFdToString(cap.fd(), &output);
@@ -264,7 +264,7 @@
CapturedStderr cap;
errno = ENOENT;
UNIMPLEMENTED(ERROR);
- ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+ ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
std::string output;
android::base::ReadFdToString(cap.fd(), &output);
diff --git a/crash_reporter/Android.mk b/crash_reporter/Android.mk
index 8756956..5dd9418 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,23 @@
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):
+ $(hide)mkdir -p $(dir $@)
+ 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 5fb0d7a..57c1d40 100644
--- a/crash_reporter/crash_reporter.rc
+++ b/crash_reporter/crash_reporter.rc
@@ -1,11 +1,11 @@
on property:crash_reporter.coredump.enabled=1
write /proc/sys/kernel/core_pattern \
- "|/system/bin/crash_reporter --user=%P:%s:%u:%e"
+ "|/system/bin/crash_reporter --user=%P:%s:%u:%g:%e"
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 e27c905..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>
@@ -51,17 +52,17 @@
static const char kCoreTempFolder[] = "/data/misc/crash_reporter/tmp";
-// Define an otherwise invalid value that represents an unknown UID.
+// Define an otherwise invalid value that represents an unknown UID and GID.
static const uid_t kUnknownUid = -1;
+static const gid_t kUnknownGid = -1;
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;
@@ -117,22 +118,6 @@
}
}
-// Return the string that should be used for the kernel's core_pattern file.
-// Note that if you change the format of the enabled pattern, you'll probably
-// also need to change the ParseCrashAttributes() function below, the
-// user_collector_test.cc unittest, and the logging_UserCrash.py autotest.
-std::string UserCollector::GetPattern(bool enabled) const {
- if (enabled) {
- // Combine the four crash attributes into one parameter to try to reduce
- // the size of the invocation line for crash_reporter, since the kernel
- // has a fixed-sized (128B) buffer for it (before parameter expansion).
- // Note that the kernel does not support quoted arguments in core_pattern.
- return StringPrintf("|%s --user=%%P:%%s:%%u:%%e", our_path_.c_str());
- } else {
- return "core";
- }
-}
-
bool UserCollector::SetUpInternal(bool enabled) {
CHECK(initialized_);
LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling";
@@ -202,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)) {
@@ -382,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());
@@ -520,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);
@@ -554,15 +556,18 @@
bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes,
pid_t *pid, int *signal, uid_t *uid,
+ gid_t *gid,
std::string *kernel_supplied_name) {
- pcrecpp::RE re("(\\d+):(\\d+):(\\d+):(.*)");
- if (re.FullMatch(crash_attributes, pid, signal, uid, kernel_supplied_name))
+ pcrecpp::RE re("(\\d+):(\\d+):(\\d+):(\\d+):(.*)");
+ if (re.FullMatch(crash_attributes, pid, signal, uid, gid,
+ kernel_supplied_name))
return true;
LOG(INFO) << "Falling back to parsing crash attributes '"
- << crash_attributes << "' without UID";
+ << crash_attributes << "' without UID and GID";
pcrecpp::RE re_without_uid("(\\d+):(\\d+):(.*)");
*uid = kUnknownUid;
+ *gid = kUnknownGid;
return re_without_uid.FullMatch(crash_attributes, pid, signal,
kernel_supplied_name);
}
@@ -595,10 +600,11 @@
pid_t pid = 0;
int signal = 0;
uid_t supplied_ruid = kUnknownUid;
+ gid_t supplied_rgid = kUnknownGid;
std::string kernel_supplied_name;
if (!ParseCrashAttributes(crash_attributes, &pid, &signal, &supplied_ruid,
- &kernel_supplied_name)) {
+ &supplied_rgid, &kernel_supplied_name)) {
LOG(ERROR) << "Invalid parameter: --user=" << crash_attributes;
return false;
}
@@ -606,7 +612,7 @@
// Switch to the group and user that ran the crashing binary in order to
// access their /proc files. Do not set suid/sgid, so that we can switch
// back after copying the necessary files.
- if (setresgid(supplied_ruid, supplied_ruid, -1) != 0) {
+ if (setresgid(supplied_rgid, supplied_rgid, -1) != 0) {
PLOG(FATAL) << "Unable to set real group ID to access process files";
}
if (setresuid(supplied_ruid, supplied_ruid, -1) != 0) {
diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h
index 8c38aa2..7261ed4 100644
--- a/crash_reporter/user_collector.h
+++ b/crash_reporter/user_collector.h
@@ -105,7 +105,6 @@
// crash_reporter-user-collection signature.
std::string GetErrorTypeSignature(ErrorType error_type) const;
- std::string GetPattern(bool enabled) const;
bool SetUpInternal(bool enabled);
// Returns, via |line|, the first line in |lines| that starts with |prefix|.
@@ -166,7 +165,7 @@
ErrorType ConvertAndEnqueueCrash(pid_t pid, const std::string &exec_name,
uid_t supplied_ruid, bool *out_of_capacity);
bool ParseCrashAttributes(const std::string &crash_attributes,
- pid_t *pid, int *signal, uid_t *uid,
+ pid_t *pid, int *signal, uid_t *uid, gid_t *gid,
std::string *kernel_supplied_name);
bool ShouldDump(bool has_owner_consent,
diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc
index 4419e7c..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:
@@ -98,35 +98,38 @@
pid_t pid;
int signal;
uid_t uid;
+ gid_t gid;
std::string exec_name;
- EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:1000:foobar",
- &pid, &signal, &uid, &exec_name));
+ EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:1000:2000:foobar",
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_EQ(123456, pid);
EXPECT_EQ(11, signal);
EXPECT_EQ(1000, uid);
+ EXPECT_EQ(2000, gid);
EXPECT_EQ("foobar", exec_name);
EXPECT_TRUE(collector_.ParseCrashAttributes("4321:6:barfoo",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_EQ(4321, pid);
EXPECT_EQ(6, signal);
EXPECT_EQ(-1, uid);
+ EXPECT_EQ(-1, gid);
EXPECT_EQ("barfoo", exec_name);
EXPECT_FALSE(collector_.ParseCrashAttributes("123456:11",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:exec:extra",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_EQ("exec:extra", exec_name);
EXPECT_FALSE(collector_.ParseCrashAttributes("12345p:11:foobar",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_FALSE(collector_.ParseCrashAttributes("123456:1 :foobar",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_FALSE(collector_.ParseCrashAttributes("123456::foobar",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
}
TEST_F(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent) {
@@ -219,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/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 599995c..1287fb9 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -130,31 +130,44 @@
"dump_backtrace"
};
-static bool selinux_action_allowed(int s, pid_t tid, debugger_action_t action)
+static int audit_callback(void* data, security_class_t /* cls */, char* buf, size_t len)
+{
+ struct debugger_request_t* req = reinterpret_cast<debugger_request_t*>(data);
+
+ if (!req) {
+ ALOGE("No debuggerd request audit data");
+ return 0;
+ }
+
+ snprintf(buf, len, "pid=%d uid=%d gid=%d", req->pid, req->uid, req->gid);
+ return 0;
+}
+
+static bool selinux_action_allowed(int s, debugger_request_t* request)
{
char *scon = NULL, *tcon = NULL;
const char *tclass = "debuggerd";
const char *perm;
bool allowed = false;
- if (action <= 0 || action >= (sizeof(debuggerd_perms)/sizeof(debuggerd_perms[0]))) {
- ALOGE("SELinux: No permission defined for debugger action %d", action);
+ if (request->action <= 0 || request->action >= (sizeof(debuggerd_perms)/sizeof(debuggerd_perms[0]))) {
+ ALOGE("SELinux: No permission defined for debugger action %d", request->action);
return false;
}
- perm = debuggerd_perms[action];
+ perm = debuggerd_perms[request->action];
if (getpeercon(s, &scon) < 0) {
ALOGE("Cannot get peer context from socket\n");
goto out;
}
- if (getpidcon(tid, &tcon) < 0) {
- ALOGE("Cannot get context for tid %d\n", tid);
+ if (getpidcon(request->tid, &tcon) < 0) {
+ ALOGE("Cannot get context for tid %d\n", request->tid);
goto out;
}
- allowed = (selinux_check_access(scon, tcon, tclass, perm, NULL) == 0);
+ allowed = (selinux_check_access(scon, tcon, tclass, perm, reinterpret_cast<void*>(request)) == 0);
out:
freecon(scon);
@@ -225,7 +238,7 @@
return -1;
}
- if (!selinux_action_allowed(fd, out_request->tid, out_request->action))
+ if (!selinux_action_allowed(fd, out_request))
return -1;
} else {
// No one else is allowed to dump arbitrary processes.
@@ -515,7 +528,7 @@
return 1;
fcntl(s, F_SETFD, FD_CLOEXEC);
- ALOGI("debuggerd: " __DATE__ " " __TIME__ "\n");
+ ALOGI("debuggerd: starting\n");
for (;;) {
sockaddr addr;
@@ -566,6 +579,8 @@
int main(int argc, char** argv) {
union selinux_callback cb;
if (argc == 1) {
+ cb.func_audit = audit_callback;
+ selinux_set_callback(SELINUX_CB_AUDIT, cb);
cb.func_log = selinux_log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
return do_server();
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index 5e6c6f9..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.c 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
-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
-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_priv.h b/fs_mgr/fs_mgr_priv.h
index 367ab6e..ba0e097 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -20,6 +20,8 @@
#include <cutils/klog.h>
#include <fs_mgr.h>
+__BEGIN_DECLS
+
#define INFO(x...) KLOG_INFO("fs_mgr", x)
#define WARNING(x...) KLOG_WARNING("fs_mgr", x)
#define ERROR(x...) KLOG_ERROR("fs_mgr", x)
@@ -86,4 +88,6 @@
int fs_mgr_set_blk_ro(const char *blockdev);
int fs_mgr_update_for_slotselect(struct fstab *fstab);
+__END_DECLS
+
#endif /* __CORE_FS_MGR_PRIV_H */
diff --git a/fs_mgr/fs_mgr_priv_verity.h b/fs_mgr/fs_mgr_priv_verity.h
index f90e596..cd673f3 100644
--- a/fs_mgr/fs_mgr_priv_verity.h
+++ b/fs_mgr/fs_mgr_priv_verity.h
@@ -14,7 +14,14 @@
* limitations under the License.
*/
+#include <sys/cdefs.h>
+
#define FS_MGR_SETUP_VERITY_DISABLED -2
#define FS_MGR_SETUP_VERITY_FAIL -1
#define FS_MGR_SETUP_VERITY_SUCCESS 0
+
+__BEGIN_DECLS
+
int fs_mgr_setup_verity(struct fstab_rec *fstab);
+
+__END_DECLS
diff --git a/fs_mgr/fs_mgr_verity.c b/fs_mgr/fs_mgr_verity.cpp
similarity index 70%
rename from fs_mgr/fs_mgr_verity.c
rename to fs_mgr/fs_mgr_verity.cpp
index eddc3e4..c358982 100644
--- a/fs_mgr/fs_mgr_verity.c
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -29,6 +29,7 @@
#include <libgen.h>
#include <time.h>
+#include <base/file.h>
#include <private/android_filesystem_config.h>
#include <cutils/properties.h>
#include <logwrap/logwrap.h>
@@ -37,19 +38,27 @@
#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"
#include "fs_mgr_priv_verity.h"
#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"
@@ -74,18 +83,15 @@
extern struct fs_info info;
-static RSAPublicKey *load_key(char *path)
+static RSAPublicKey *load_key(const char *path)
{
- FILE *f;
- RSAPublicKey *key;
-
- key = malloc(sizeof(RSAPublicKey));
+ RSAPublicKey* key = static_cast<RSAPublicKey*>(malloc(sizeof(RSAPublicKey)));
if (!key) {
ERROR("Can't malloc key\n");
return NULL;
}
- f = fopen(path, "r");
+ FILE* f = fopen(path, "r");
if (!f) {
ERROR("Can't open '%s'\n", path);
free(key);
@@ -93,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;
@@ -110,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];
@@ -122,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;
}
@@ -143,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++] == ' ') {
@@ -170,181 +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 (TEMP_FAILURE_RETRY(read(data_device, &sb, sizeof(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 (TEMP_FAILURE_RETRY(read(device, &magic_number, sizeof(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 (TEMP_FAILURE_RETRY(read(device, &protocol_version,
- sizeof(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 (TEMP_FAILURE_RETRY(read(device, *signature, RSANUMBYTES)) != 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 (TEMP_FAILURE_RETRY(read(device, &table_length, sizeof(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 = malloc(table_length + 1);
- if (!*table) {
- ERROR("Couldn't allocate memory for verity table!\n");
- goto out;
- }
- if (TEMP_FAILURE_RETRY(read(device, *table, table_length)) !=
- (ssize_t)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);
@@ -384,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;
@@ -395,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;
}
@@ -485,7 +382,7 @@
goto out;
}
- if (TEMP_FAILURE_RETRY(read(fd, buffer, size)) != size) {
+ if (!android::base::ReadFully(fd, buffer, size)) {
ERROR("Failed to read %zd bytes from %s (%s)\n", size, fname,
strerror(errno));
goto out;
@@ -708,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)) {
@@ -771,12 +671,7 @@
rc = 0;
out:
- free(signature);
-
- if (fd != -1) {
- close(fd);
- }
-
+ fec_close(f);
return rc;
}
@@ -887,8 +782,9 @@
int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback)
{
- _Alignas(struct dm_ioctl) char buffer[DM_BUF_SIZE];
+ 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];
@@ -933,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;
}
}
@@ -951,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;
@@ -977,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 *invalid_table = NULL;
+ char *verity_blk_name = NULL;
+ struct fec_handle *f = NULL;
+ struct fec_verity_metadata verity;
+ struct verity_table_params params;
- char *verity_blk_name = 0;
- char *verity_table = 0;
- char *verity_table_signature = 0;
- int verity_table_length = 0;
- uint64_t device_size = 0;
-
- _Alignas(struct dm_ioctl) char buffer[DM_BUF_SIZE];
+ 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;
}
@@ -1086,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/healthd/Android.mk b/healthd/Android.mk
index 07e1d73..27c985c 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -35,7 +35,7 @@
LOCAL_C_INCLUDES := bootable/recovery
-LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libminui libpng libz libutils libstdc++ libcutils liblog libm libc
+LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libminui libpng libz libutils libcutils liblog libm libc
ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
LOCAL_STATIC_LIBRARIES += libsuspend
diff --git a/include/binderwrapper/binder_wrapper.h b/include/binderwrapper/binder_wrapper.h
index e57cf83..921c4ed 100644
--- a/include/binderwrapper/binder_wrapper.h
+++ b/include/binderwrapper/binder_wrapper.h
@@ -17,6 +17,8 @@
#ifndef SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
+#include <sys/types.h>
+
#include <string>
#include <base/callback.h>
@@ -68,6 +70,10 @@
// Unregisters the callback, if any, for |binder|.
virtual bool UnregisterForDeathNotifications(const sp<IBinder>& binder) = 0;
+ // When called while in a transaction, returns the caller's UID or PID.
+ virtual uid_t GetCallingUid() = 0;
+ virtual pid_t GetCallingPid() = 0;
+
private:
static BinderWrapper* instance_;
};
diff --git a/include/binderwrapper/stub_binder_wrapper.h b/include/binderwrapper/stub_binder_wrapper.h
index 6e198ae..01c9648 100644
--- a/include/binderwrapper/stub_binder_wrapper.h
+++ b/include/binderwrapper/stub_binder_wrapper.h
@@ -77,6 +77,9 @@
}
void clear_local_binders() { local_binders_.clear(); }
+ void set_calling_uid(uid_t uid) { calling_uid_ = uid; }
+ void set_calling_pid(pid_t pid) { calling_pid_ = pid; }
+
// Sets the binder to return when |service_name| is passed to GetService() or
// WaitForService().
void SetBinderForService(const std::string& service_name,
@@ -97,6 +100,8 @@
bool RegisterForDeathNotifications(const sp<IBinder>& binder,
const base::Closure& callback) override;
bool UnregisterForDeathNotifications(const sp<IBinder>& binder) override;
+ uid_t GetCallingUid() override;
+ pid_t GetCallingPid() override;
private:
using ServiceMap = std::map<std::string, sp<IBinder>>;
@@ -116,6 +121,10 @@
// death.
std::map<sp<IBinder>, base::Closure> death_callbacks_;
+ // Values to return from GetCallingUid() and GetCallingPid();
+ uid_t calling_uid_;
+ pid_t calling_pid_;
+
DISALLOW_COPY_AND_ASSIGN(StubBinderWrapper);
};
diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h
index 7818b4e..20a379e 100644
--- a/include/utils/LruCache.h
+++ b/include/utils/LruCache.h
@@ -99,17 +99,29 @@
TValue mNullValue;
public:
+ // To be used like:
+ // while (it.next()) {
+ // it.value(); it.key();
+ // }
class Iterator {
public:
- Iterator(const LruCache<TKey, TValue>& cache): mCache(cache), mIterator(mCache.mSet->begin()) {
+ Iterator(const LruCache<TKey, TValue>& cache):
+ mCache(cache), mIterator(mCache.mSet->begin()), mBeginReturned(false) {
}
bool next() {
if (mIterator == mCache.mSet->end()) {
return false;
}
- std::advance(mIterator, 1);
- return mIterator != mCache.mSet->end();
+ if (!mBeginReturned) {
+ // mIterator has been initialized to the beginning and
+ // hasn't been returned. Do not advance:
+ mBeginReturned = true;
+ } else {
+ std::advance(mIterator, 1);
+ }
+ bool ret = (mIterator != mCache.mSet->end());
+ return ret;
}
const TValue& value() const {
@@ -122,6 +134,7 @@
private:
const LruCache<TKey, TValue>& mCache;
typename LruCacheSet::iterator mIterator;
+ bool mBeginReturned;
};
};
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..d6cb4e5 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -84,16 +84,19 @@
LOCAL_STATIC_LIBRARIES := \
libinit \
libfs_mgr \
+ libfec \
+ libfec_rs \
libsquashfs_utils \
liblogwrap \
libcutils \
libbase \
libext4_utils_static \
libutils \
- liblog \
libc \
libselinux \
+ liblog \
libmincrypt \
+ libcrypto_static \
libc++_static \
libdl \
libsparse_static \
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index a768762..e1e0c48 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -89,7 +89,7 @@
if (out == NULL) {
return;
}
- fprintf(out, "version = Android init 0.8 " __TIME__ "\n");
+ fprintf(out, "version = Android init 0.8\n");
fprintf(out, "title = Boot chart for Android (%s)\n", date);
fprintf(out, "system.uname = %s %s %s %s\n", uts.sysname, uts.release, uts.version, uts.machine);
fprintf(out, "system.release = %s\n", fingerprint.c_str());
diff --git a/init/init.cpp b/init/init.cpp
index ee1351d..a898b03 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -467,7 +467,16 @@
}
static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
- snprintf(buf, len, "property=%s", !data ? "NULL" : (char *)data);
+
+ property_audit_data *d = reinterpret_cast<property_audit_data*>(data);
+
+ if (!d || !d->name || !d->cr) {
+ ERROR("audit_callback invoked with null data arguments!");
+ return 0;
+ }
+
+ snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name,
+ d->cr->pid, d->cr->uid, d->cr->gid);
return 0;
}
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 7194820..ebf768e 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -90,10 +90,11 @@
}
}
-static int check_mac_perms(const char *name, char *sctx)
+static int check_mac_perms(const char *name, char *sctx, struct ucred *cr)
{
char *tctx = NULL;
int result = 0;
+ property_audit_data audit_data;
if (!sctx)
goto err;
@@ -104,7 +105,10 @@
if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0)
goto err;
- if (selinux_check_access(sctx, tctx, "property_service", "set", (void*) name) == 0)
+ audit_data.name = name;
+ audit_data.cr = cr;
+
+ if (selinux_check_access(sctx, tctx, "property_service", "set", reinterpret_cast<void*>(&audit_data)) == 0)
result = 1;
freecon(tctx);
@@ -112,7 +116,7 @@
return result;
}
-static int check_control_mac_perms(const char *name, char *sctx)
+static int check_control_mac_perms(const char *name, char *sctx, struct ucred *cr)
{
/*
* Create a name prefix out of ctl.<service name>
@@ -126,19 +130,19 @@
if (ret < 0 || (size_t) ret >= sizeof(ctl_name))
return 0;
- return check_mac_perms(ctl_name, sctx);
+ return check_mac_perms(ctl_name, sctx, cr);
}
/*
* Checks permissions for setting system properties.
* Returns 1 if uid allowed, 0 otherwise.
*/
-static int check_perms(const char *name, char *sctx)
+static int check_perms(const char *name, char *sctx, struct ucred *cr)
{
if(!strncmp(name, "ro.", 3))
name +=3;
- return check_mac_perms(name, sctx);
+ return check_mac_perms(name, sctx, cr);
}
std::string property_get(const char* name) {
@@ -321,14 +325,14 @@
// Keep the old close-socket-early behavior when handling
// ctl.* properties.
close(s);
- if (check_control_mac_perms(msg.value, source_ctx)) {
+ if (check_control_mac_perms(msg.value, source_ctx, &cr)) {
handle_control_message((char*) msg.name + 4, (char*) msg.value);
} else {
ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
}
} else {
- if (check_perms(msg.name, source_ctx)) {
+ if (check_perms(msg.name, source_ctx, &cr)) {
property_set((char*) msg.name, (char*) msg.value);
} else {
ERROR("sys_prop: permission denied uid:%d name:%s\n",
diff --git a/init/property_service.h b/init/property_service.h
index 51d7404..1a48fb1 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -18,9 +18,15 @@
#define _INIT_PROPERTY_H
#include <stddef.h>
+#include <sys/socket.h>
#include <sys/system_properties.h>
#include <string>
+struct property_audit_data {
+ ucred *cr;
+ const char* name;
+};
+
extern void property_init(void);
extern void property_load_boot_defaults(void);
extern void load_persist_props(void);
diff --git a/libbinderwrapper/real_binder_wrapper.cc b/libbinderwrapper/real_binder_wrapper.cc
index adff19b..1c51822 100644
--- a/libbinderwrapper/real_binder_wrapper.cc
+++ b/libbinderwrapper/real_binder_wrapper.cc
@@ -19,6 +19,7 @@
#include <base/logging.h>
#include <binder/Binder.h>
#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
namespace android {
@@ -111,4 +112,12 @@
return true;
}
+uid_t RealBinderWrapper::GetCallingUid() {
+ return IPCThreadState::self()->getCallingUid();
+}
+
+pid_t RealBinderWrapper::GetCallingPid() {
+ return IPCThreadState::self()->getCallingPid();
+}
+
} // namespace android
diff --git a/libbinderwrapper/real_binder_wrapper.h b/libbinderwrapper/real_binder_wrapper.h
index 8e281f2..ea08371 100644
--- a/libbinderwrapper/real_binder_wrapper.h
+++ b/libbinderwrapper/real_binder_wrapper.h
@@ -38,6 +38,8 @@
bool RegisterForDeathNotifications(const sp<IBinder>& binder,
const base::Closure& callback) override;
bool UnregisterForDeathNotifications(const sp<IBinder>& binder) override;
+ uid_t GetCallingUid() override;
+ pid_t GetCallingPid() override;
private:
class DeathRecipient;
diff --git a/libbinderwrapper/stub_binder_wrapper.cc b/libbinderwrapper/stub_binder_wrapper.cc
index 1d24681..87c6ab7 100644
--- a/libbinderwrapper/stub_binder_wrapper.cc
+++ b/libbinderwrapper/stub_binder_wrapper.cc
@@ -22,7 +22,9 @@
namespace android {
-StubBinderWrapper::StubBinderWrapper() = default;
+StubBinderWrapper::StubBinderWrapper()
+ : calling_uid_(-1),
+ calling_pid_(-1) {}
StubBinderWrapper::~StubBinderWrapper() = default;
@@ -73,4 +75,12 @@
return true;
}
+uid_t StubBinderWrapper::GetCallingUid() {
+ return calling_uid_;
+}
+
+pid_t StubBinderWrapper::GetCallingPid() {
+ return calling_pid_;
+}
+
} // namespace android
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/libpixelflinger/include/private/pixelflinger/ggl_context.h b/libpixelflinger/include/private/pixelflinger/ggl_context.h
index d43655c..a18b2f7 100644
--- a/libpixelflinger/include/private/pixelflinger/ggl_context.h
+++ b/libpixelflinger/include/private/pixelflinger/ggl_context.h
@@ -234,7 +234,7 @@
// ----------------------------------------------------------------------------
-class needs_filter_t;
+struct needs_filter_t;
struct needs_t {
inline int match(const needs_filter_t& filter);
inline bool operator == (const needs_t& rhs) const {
diff --git a/libpixelflinger/trap.cpp b/libpixelflinger/trap.cpp
index 80efeff..ea53625 100644
--- a/libpixelflinger/trap.cpp
+++ b/libpixelflinger/trap.cpp
@@ -563,10 +563,10 @@
c->init_y(c, miny);
for (int32_t y = miny; y < maxy; y++) {
- register int32_t ex0 = ey0;
- register int32_t ex1 = ey1;
- register int32_t ex2 = ey2;
- register int32_t xl, xr;
+ int32_t ex0 = ey0;
+ int32_t ex1 = ey1;
+ int32_t ex2 = ey2;
+ int32_t xl, xr;
for (xl=minx ; xl<maxx ; xl++) {
if (ex0>0 && ex1>0 && ex2>0)
break; // all strictly positive
diff --git a/libsparse/simg2img.c b/libsparse/simg2img.c
index 95e9b5b..b9b438e 100644
--- a/libsparse/simg2img.c
+++ b/libsparse/simg2img.c
@@ -40,7 +40,6 @@
int in;
int out;
int i;
- int ret;
struct sparse_file *s;
if (argc < 3) {
@@ -71,10 +70,12 @@
exit(-1);
}
- lseek(out, SEEK_SET, 0);
+ if (lseek(out, 0, SEEK_SET) == -1) {
+ perror("lseek failed");
+ exit(EXIT_FAILURE);
+ }
- ret = sparse_file_write(s, out, false, false, false);
- if (ret < 0) {
+ if (sparse_file_write(s, out, false, false, false) < 0) {
fprintf(stderr, "Cannot write output file\n");
exit(-1);
}
diff --git a/libsparse/sparse_read.c b/libsparse/sparse_read.c
index 9b10293..ec63850 100644
--- a/libsparse/sparse_read.c
+++ b/libsparse/sparse_read.c
@@ -18,6 +18,7 @@
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
+#include <inttypes.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
@@ -233,7 +234,7 @@
ret = process_raw_chunk(s, chunk_data_size, fd, offset,
chunk_header->chunk_sz, cur_block, crc_ptr);
if (ret < 0) {
- verbose_error(s->verbose, ret, "data block at %lld", offset);
+ verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
return ret;
}
return chunk_header->chunk_sz;
@@ -241,7 +242,7 @@
ret = process_fill_chunk(s, chunk_data_size, fd,
chunk_header->chunk_sz, cur_block, crc_ptr);
if (ret < 0) {
- verbose_error(s->verbose, ret, "fill block at %lld", offset);
+ verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
return ret;
}
return chunk_header->chunk_sz;
@@ -250,7 +251,7 @@
chunk_header->chunk_sz, cur_block, crc_ptr);
if (chunk_data_size != 0) {
if (ret < 0) {
- verbose_error(s->verbose, ret, "skip block at %lld", offset);
+ verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
return ret;
}
}
@@ -258,13 +259,13 @@
case CHUNK_TYPE_CRC32:
ret = process_crc32_chunk(fd, chunk_data_size, *crc_ptr);
if (ret < 0) {
- verbose_error(s->verbose, -EINVAL, "crc block at %lld",
+ verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64,
offset);
return ret;
}
return 0;
default:
- verbose_error(s->verbose, -EINVAL, "unknown block %04X at %lld",
+ verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64,
chunk_header->chunk_type, offset);
}
diff --git a/libutils/LinearTransform.cpp b/libutils/LinearTransform.cpp
index b7d28d4..138ce8b 100644
--- a/libutils/LinearTransform.cpp
+++ b/libutils/LinearTransform.cpp
@@ -21,11 +21,24 @@
#include <utils/LinearTransform.h>
+// disable sanitize as these functions may intentionally overflow (see comments below).
+// the ifdef can be removed when host builds use clang.
+#if defined(__clang__)
+#define ATTRIBUTE_NO_SANITIZE_INTEGER __attribute__((no_sanitize("integer")))
+#else
+#define ATTRIBUTE_NO_SANITIZE_INTEGER
+#endif
+
namespace android {
-template<class T> static inline T ABS(T x) { return (x < 0) ? -x : x; }
+// sanitize failure with T = int32_t and x = 0x80000000
+template<class T>
+ATTRIBUTE_NO_SANITIZE_INTEGER
+static inline T ABS(T x) { return (x < 0) ? -x : x; }
// Static math methods involving linear transformations
+// remote sanitize failure on overflow case.
+ATTRIBUTE_NO_SANITIZE_INTEGER
static bool scale_u64_to_u64(
uint64_t val,
uint32_t N,
@@ -109,6 +122,8 @@
return true;
}
+// at least one known sanitize failure (see comment below)
+ATTRIBUTE_NO_SANITIZE_INTEGER
static bool linear_transform_s64_to_s64(
int64_t val,
int64_t basis1,
@@ -172,7 +187,7 @@
// (scaled_signbit XOR res_signbit)
if (is_neg)
- scaled = -scaled;
+ scaled = -scaled; // known sanitize failure
res = scaled + basis2;
if ((scaled ^ basis2 ^ INT64_MIN) & (scaled ^ res) & INT64_MIN)
@@ -250,6 +265,8 @@
template void LinearTransform::reduce<uint64_t>(uint64_t* N, uint64_t* D);
template void LinearTransform::reduce<uint32_t>(uint32_t* N, uint32_t* D);
+// sanitize failure if *N = 0x80000000
+ATTRIBUTE_NO_SANITIZE_INTEGER
void LinearTransform::reduce(int32_t* N, uint32_t* D) {
if (N && D && *D) {
if (*N < 0) {
diff --git a/libutils/tests/LruCache_test.cpp b/libutils/tests/LruCache_test.cpp
index 2ed84d7..580b980 100644
--- a/libutils/tests/LruCache_test.cpp
+++ b/libutils/tests/LruCache_test.cpp
@@ -293,4 +293,110 @@
EXPECT_EQ(3, callback.callbackCount);
}
+TEST_F(LruCacheTest, IteratorCheck) {
+ LruCache<int, int> cache(100);
+
+ cache.put(1, 4);
+ cache.put(2, 5);
+ cache.put(3, 6);
+ EXPECT_EQ(3U, cache.size());
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ int v = it.value();
+ // Check we haven't seen the value before.
+ EXPECT_TRUE(returnedValues.find(v) == returnedValues.end());
+ returnedValues.insert(v);
+ }
+ EXPECT_EQ(std::unordered_set<int>({4, 5, 6}), returnedValues);
+}
+
+TEST_F(LruCacheTest, EmptyCacheIterator) {
+ // Check that nothing crashes...
+ LruCache<int, int> cache(100);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>(), returnedValues);
+}
+
+TEST_F(LruCacheTest, OneElementCacheIterator) {
+ // Check that nothing crashes...
+ LruCache<int, int> cache(100);
+ cache.put(1, 2);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>({ 2 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, OneElementCacheRemove) {
+ LruCache<int, int> cache(100);
+ cache.put(1, 2);
+
+ cache.remove(1);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>({ }), returnedValues);
+}
+
+TEST_F(LruCacheTest, Remove) {
+ LruCache<int, int> cache(100);
+ cache.put(1, 4);
+ cache.put(2, 5);
+ cache.put(3, 6);
+
+ cache.remove(2);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>({ 4, 6 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, RemoveYoungest) {
+ LruCache<int, int> cache(100);
+ cache.put(1, 4);
+ cache.put(2, 5);
+ cache.put(3, 6);
+
+ cache.remove(3);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>({ 4, 5 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, RemoveNonMember) {
+ LruCache<int, int> cache(100);
+ cache.put(1, 4);
+ cache.put(2, 5);
+ cache.put(3, 6);
+
+ cache.remove(7);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>({ 4, 5, 6 }), returnedValues);
+}
+
}
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/Android.mk b/logd/Android.mk
index c00061b..feca8d5 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -27,7 +27,8 @@
libsysutils \
liblog \
libcutils \
- libbase
+ libbase \
+ libpackagelistparser
# This is what we want to do:
# event_logtags = $(shell \
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 808d17a..2f7cbfb 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -183,7 +183,7 @@
static const char comm_str[] = " comm=\"";
const char *comm = strstr(str, comm_str);
const char *estr = str + strlen(str);
- char *commfree = NULL;
+ const char *commfree = NULL;
if (comm) {
estr = comm;
comm += sizeof(comm_str) - 1;
@@ -231,7 +231,7 @@
// end scope for main buffer
}
- free(commfree);
+ free(const_cast<char *>(commfree));
free(str);
if (notify) {
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 5cfc02f..1de8e64 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -217,7 +217,7 @@
return len;
}
-// Prune at most 10% of the log entries or 256, whichever is less.
+// Prune at most 10% of the log entries or maxPrune, whichever is less.
//
// mLogElementsLock must be held when this function is called.
void LogBuffer::maybePrune(log_id_t id) {
@@ -225,21 +225,24 @@
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) {
+ if (pruneRows < minElements) {
pruneRows = minElements;
}
- if (pruneRows > 256) {
- pruneRows = 256;
+ if (pruneRows > maxPrune) {
+ pruneRows = maxPrune;
}
prune(id, pruneRows);
}
}
LogBufferElementCollection::iterator LogBuffer::erase(
- LogBufferElementCollection::iterator it, bool engageStats) {
+ LogBufferElementCollection::iterator it, bool coalesce) {
LogBufferElement *e = *it;
log_id_t id = e->getLogId();
@@ -248,10 +251,10 @@
mLastWorstUid[id].erase(f);
}
it = mLogElements.erase(it);
- if (engageStats) {
- stats.subtract(e);
- } else {
+ if (coalesce) {
stats.erase(e);
+ } else {
+ stats.subtract(e);
}
delete e;
@@ -286,7 +289,7 @@
public:
- bool merge(LogBufferElement *e, unsigned short dropped) {
+ bool coalesce(LogBufferElement *e, unsigned short dropped) {
LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid());
LogBufferElementMap::iterator it = map.find(key.getKey());
if (it != map.end()) {
@@ -454,7 +457,7 @@
it = mLogElements.begin();
// Perform at least one mandatory garbage collection cycle in following
// - clear leading chatty tags
- // - merge chatty tags
+ // - coalesce chatty tags
// - check age-out of preserved logs
bool gc = pruneRows <= 1;
if (!gc && (worst != (uid_t) -1)) {
@@ -493,9 +496,8 @@
continue;
}
- // merge any drops
- if (dropped && last.merge(e, dropped)) {
- it = erase(it, false);
+ if (dropped && last.coalesce(e, dropped)) {
+ it = erase(it, true);
continue;
}
@@ -526,7 +528,6 @@
break;
}
- // unmerged drop message
if (dropped) {
last.add(e);
if ((!gc && (e->getUid() == worst))
@@ -560,8 +561,8 @@
} else {
stats.drop(e);
e->setDropped(1);
- if (last.merge(e, 1)) {
- it = erase(it, false);
+ if (last.coalesce(e, 1)) {
+ it = erase(it, true);
} else {
last.add(e);
if (!gc || (mLastWorstUid[id].find(worst)
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index d8e0b90..7ed92e9 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -78,17 +78,21 @@
std::string formatPrune() { return mPrune.format(); }
// helper must be protected directly or implicitly by lock()/unlock()
- char *pidToName(pid_t pid) { return stats.pidToName(pid); }
+ const char *pidToName(pid_t pid) { return stats.pidToName(pid); }
uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
- char *uidToName(uid_t uid) { return stats.uidToName(uid); }
+ const char *uidToName(uid_t uid) { return stats.uidToName(uid); }
void lock() { pthread_mutex_lock(&mLogElementsLock); }
void unlock() { pthread_mutex_unlock(&mLogElementsLock); }
private:
+
+ static constexpr size_t minPrune = 4;
+ static constexpr size_t maxPrune = 256;
+
void maybePrune(log_id_t id);
bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
LogBufferElementCollection::iterator erase(
- LogBufferElementCollection::iterator it, bool engageStats = true);
+ LogBufferElementCollection::iterator it, bool coalesce = false);
};
#endif // _LOGD_LOG_BUFFER_H__
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 150ce22..c4c302b 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -113,9 +113,9 @@
static const char format_uid[] = "uid=%u%s%s expire %u line%s";
parent->lock();
- char *name = parent->uidToName(mUid);
+ const char *name = parent->uidToName(mUid);
parent->unlock();
- char *commName = android::tidToName(mTid);
+ const char *commName = android::tidToName(mTid);
if (!commName && (mTid != mPid)) {
commName = android::tidToName(mPid);
}
@@ -128,28 +128,28 @@
size_t len = strlen(name + 1);
if (!strncmp(name + 1, commName + 1, len)) {
if (commName[len + 1] == '\0') {
- free(commName);
+ free(const_cast<char *>(commName));
commName = NULL;
} else {
- free(name);
+ free(const_cast<char *>(name));
name = NULL;
}
}
}
if (name) {
- char *p = NULL;
- asprintf(&p, "(%s)", name);
- if (p) {
- free(name);
- name = p;
+ char *buf = NULL;
+ asprintf(&buf, "(%s)", name);
+ if (buf) {
+ free(const_cast<char *>(name));
+ name = buf;
}
}
if (commName) {
- char *p = NULL;
- asprintf(&p, " %s", commName);
- if (p) {
- free(commName);
- commName = p;
+ char *buf = NULL;
+ asprintf(&buf, " %s", commName);
+ if (buf) {
+ free(const_cast<char *>(commName));
+ commName = buf;
}
}
// identical to below to calculate the buffer size required
@@ -166,18 +166,19 @@
buffer = static_cast<char *>(calloc(1, hdrLen + len + 1));
if (!buffer) {
- free(name);
- free(commName);
+ free(const_cast<char *>(name));
+ free(const_cast<char *>(commName));
return 0;
}
size_t retval = hdrLen + len;
if (mLogId == LOG_ID_EVENTS) {
- android_log_event_string_t *e = reinterpret_cast<android_log_event_string_t *>(buffer);
+ android_log_event_string_t *event =
+ reinterpret_cast<android_log_event_string_t *>(buffer);
- e->header.tag = htole32(LOGD_LOG_TAG);
- e->type = EVENT_TYPE_STRING;
- e->length = htole32(len);
+ event->header.tag = htole32(LOGD_LOG_TAG);
+ event->type = EVENT_TYPE_STRING;
+ event->length = htole32(len);
} else {
++retval;
buffer[0] = ANDROID_LOG_INFO;
@@ -187,8 +188,8 @@
snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "",
commName ? commName : "",
mDropped, (mDropped > 1) ? "s" : "");
- free(name);
- free(commName);
+ free(const_cast<char *>(name));
+ free(const_cast<char *>(commName));
return retval;
}
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index a9c3086..fdb2576 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -14,15 +14,12 @@
* limitations under the License.
*/
-#include <algorithm> // std::max
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
-#include <base/stringprintf.h>
#include <log/logger.h>
-#include <private/android_filesystem_config.h>
#include "LogStatistics.h"
@@ -30,6 +27,7 @@
log_id_for_each(id) {
mSizes[id] = 0;
mElements[id] = 0;
+ mDroppedElements[id] = 0;
mSizesTotal[id] = 0;
mElementsTotal[id] = 0;
}
@@ -63,9 +61,9 @@
}
-void LogStatistics::add(LogBufferElement *e) {
- log_id_t log_id = e->getLogId();
- unsigned short size = e->getMsgLen();
+void LogStatistics::add(LogBufferElement *element) {
+ log_id_t log_id = element->getLogId();
+ unsigned short size = element->getMsgLen();
mSizes[log_id] += size;
++mElements[log_id];
@@ -76,65 +74,69 @@
return;
}
- uidTable[log_id].add(e->getUid(), e);
+ uidTable[log_id].add(element->getUid(), element);
if (!enable) {
return;
}
- pidTable.add(e->getPid(), e);
- tidTable.add(e->getTid(), e);
+ pidTable.add(element->getPid(), element);
+ tidTable.add(element->getTid(), element);
- uint32_t tag = e->getTag();
+ uint32_t tag = element->getTag();
if (tag) {
- tagTable.add(tag, e);
+ tagTable.add(tag, element);
}
}
-void LogStatistics::subtract(LogBufferElement *e) {
- log_id_t log_id = e->getLogId();
- unsigned short size = e->getMsgLen();
+void LogStatistics::subtract(LogBufferElement *element) {
+ log_id_t log_id = element->getLogId();
+ 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;
}
- uidTable[log_id].subtract(e->getUid(), e);
+ uidTable[log_id].subtract(element->getUid(), element);
if (!enable) {
return;
}
- pidTable.subtract(e->getPid(), e);
- tidTable.subtract(e->getTid(), e);
+ pidTable.subtract(element->getPid(), element);
+ tidTable.subtract(element->getTid(), element);
- uint32_t tag = e->getTag();
+ uint32_t tag = element->getTag();
if (tag) {
- tagTable.subtract(tag, e);
+ tagTable.subtract(tag, element);
}
}
// Atomically set an entry to drop
// entry->setDropped(1) must follow this call, caller should do this explicitly.
-void LogStatistics::drop(LogBufferElement *e) {
- log_id_t log_id = e->getLogId();
- unsigned short size = e->getMsgLen();
+void LogStatistics::drop(LogBufferElement *element) {
+ log_id_t log_id = element->getLogId();
+ unsigned short size = element->getMsgLen();
mSizes[log_id] -= size;
+ ++mDroppedElements[log_id];
- uidTable[log_id].drop(e->getUid(), e);
+ uidTable[log_id].drop(element->getUid(), element);
if (!enable) {
return;
}
- pidTable.drop(e->getPid(), e);
- tidTable.drop(e->getTid(), e);
+ pidTable.drop(element->getPid(), element);
+ tidTable.drop(element->getTid(), element);
}
// caller must own and free character string
-char *LogStatistics::uidToName(uid_t uid) {
+const char *LogStatistics::uidToName(uid_t uid) const {
// Local hard coded favourites
if (uid == AID_LOGD) {
return strdup("auditd");
@@ -152,7 +154,7 @@
// Parse /data/system/packages.list
uid_t userId = uid % AID_USER;
- char *name = android::uidToName(userId);
+ const char *name = android::uidToName(userId);
if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) {
name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP));
}
@@ -161,17 +163,17 @@
}
// report uid -> pid(s) -> pidToName if unique
- for(pidTable_t::iterator it = pidTable.begin(); it != pidTable.end(); ++it) {
+ for(pidTable_t::const_iterator it = pidTable.begin(); it != pidTable.end(); ++it) {
const PidEntry &entry = it->second;
if (entry.getUid() == uid) {
- const char *n = entry.getName();
+ const char *nameTmp = entry.getName();
- if (n) {
+ if (nameTmp) {
if (!name) {
- name = strdup(n);
- } else if (fast<strcmp>(name, n)) {
- free(name);
+ name = strdup(nameTmp);
+ } else if (fast<strcmp>(name, nameTmp)) {
+ free(const_cast<char *>(name));
name = NULL;
break;
}
@@ -183,28 +185,152 @@
return name;
}
-static std::string format_line(
- const std::string &name,
- const std::string &size,
- const std::string &pruned) {
- static const size_t pruned_len = 6;
- static const size_t total_len = 70 + pruned_len;
-
- ssize_t drop_len = std::max(pruned.length() + 1, pruned_len);
- ssize_t size_len = std::max(size.length() + 1,
- total_len - name.length() - drop_len - 1);
-
- if (pruned.length()) {
- return android::base::StringPrintf("%s%*s%*s\n", name.c_str(),
- (int)size_len, size.c_str(),
- (int)drop_len, pruned.c_str());
- } else {
- return android::base::StringPrintf("%s%*s\n", name.c_str(),
- (int)size_len, size.c_str());
- }
+std::string UidEntry::formatHeader(const std::string &name, log_id_t id) const {
+ bool isprune = worstUidEnabledForLogid(id);
+ return formatLine(android::base::StringPrintf(
+ name.c_str(), android_log_id_to_name(id)),
+ std::string("Size"),
+ std::string(isprune ? "Pruned" : ""))
+ + formatLine(std::string("UID PACKAGE"),
+ std::string("BYTES"),
+ std::string(isprune ? "NUM" : ""));
}
-std::string LogStatistics::format(uid_t uid, unsigned int logMask) {
+std::string UidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
+ uid_t uid = getKey();
+ std::string name = android::base::StringPrintf("%u", uid);
+ const char *nameTmp = stat.uidToName(uid);
+ if (nameTmp) {
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(6 - name.length(), (size_t)1),
+ "", nameTmp);
+ free(const_cast<char *>(nameTmp));
+ }
+
+ std::string size = android::base::StringPrintf("%zu", getSizes());
+
+ std::string pruned = "";
+ size_t dropped = getDropped();
+ if (dropped) {
+ pruned = android::base::StringPrintf("%zu", dropped);
+ }
+
+ return formatLine(name, size, pruned);
+}
+
+std::string PidEntry::formatHeader(const std::string &name, log_id_t /* id */) const {
+ return formatLine(name,
+ std::string("Size"),
+ std::string("Pruned"))
+ + formatLine(std::string(" PID/UID COMMAND LINE"),
+ std::string("BYTES"),
+ std::string("NUM"));
+}
+
+std::string PidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
+ uid_t uid = getUid();
+ std::string name = android::base::StringPrintf("%5u/%u",
+ getKey(), uid);
+ const char *nameTmp = getName();
+ if (nameTmp) {
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+ "", nameTmp);
+ } else if ((nameTmp = stat.uidToName(uid))) {
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+ "", nameTmp);
+ free(const_cast<char *>(nameTmp));
+ }
+
+ std::string size = android::base::StringPrintf("%zu",
+ getSizes());
+
+ std::string pruned = "";
+ size_t dropped = getDropped();
+ if (dropped) {
+ pruned = android::base::StringPrintf("%zu", dropped);
+ }
+
+ return formatLine(name, size, pruned);
+}
+
+std::string TidEntry::formatHeader(const std::string &name, log_id_t /* id */) const {
+ return formatLine(name,
+ std::string("Size"),
+ std::string("Pruned"))
+ + formatLine(std::string(" TID/UID COMM"),
+ std::string("BYTES"),
+ std::string("NUM"));
+}
+
+std::string TidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
+ uid_t uid = getUid();
+ std::string name = android::base::StringPrintf("%5u/%u",
+ getKey(), uid);
+ const char *nameTmp = getName();
+ if (nameTmp) {
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+ "", nameTmp);
+ } else if ((nameTmp = stat.uidToName(uid))) {
+ // if we do not have a PID name, lets punt to try UID name?
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+ "", nameTmp);
+ free(const_cast<char *>(nameTmp));
+ // We tried, better to not have a name at all, we still
+ // have TID/UID by number to report in any case.
+ }
+
+ std::string size = android::base::StringPrintf("%zu",
+ getSizes());
+
+ std::string pruned = "";
+ size_t dropped = getDropped();
+ if (dropped) {
+ pruned = android::base::StringPrintf("%zu", dropped);
+ }
+
+ return formatLine(name, size, pruned);
+}
+
+std::string TagEntry::formatHeader(const std::string &name, log_id_t id) const {
+ bool isprune = worstUidEnabledForLogid(id);
+ return formatLine(name,
+ std::string("Size"),
+ std::string(isprune ? "Prune" : ""))
+ + formatLine(std::string(" TAG/UID TAGNAME"),
+ std::string("BYTES"),
+ std::string(isprune ? "NUM" : ""));
+}
+
+std::string TagEntry::format(const LogStatistics & /* stat */, log_id_t /* id */) const {
+ std::string name;
+ uid_t uid = getUid();
+ if (uid == (uid_t)-1) {
+ name = android::base::StringPrintf("%7u",
+ getKey());
+ } else {
+ name = android::base::StringPrintf("%7u/%u",
+ getKey(), uid);
+ }
+ const char *nameTmp = getName();
+ if (nameTmp) {
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(14 - name.length(), (size_t)1),
+ "", nameTmp);
+ }
+
+ std::string size = android::base::StringPrintf("%zu",
+ getSizes());
+
+ std::string pruned = "";
+
+ return formatLine(name, size, pruned);
+}
+
+std::string LogStatistics::format(uid_t uid, unsigned int logMask) const {
static const unsigned short spaces_total = 19;
// Report on total logging, current and for all time
@@ -227,7 +353,7 @@
}
spaces = 4;
- output += android::base::StringPrintf("\nTotal");
+ output += "\nTotal";
log_id_for_each(id) {
if (!(logMask & (1 << id))) {
@@ -244,7 +370,7 @@
}
spaces = 6;
- output += android::base::StringPrintf("\nNow");
+ output += "\nNow";
log_id_for_each(id) {
if (!(logMask & (1 << id))) {
@@ -266,245 +392,30 @@
// Report on Chattiest
+ std::string name;
+
// Chattiest by application (UID)
- static const size_t maximum_sorted_entries = 32;
log_id_for_each(id) {
if (!(logMask & (1 << id))) {
continue;
}
- bool headerPrinted = false;
- std::unique_ptr<const UidEntry *[]> sorted = sort(maximum_sorted_entries, id);
- ssize_t index = -1;
- while ((index = uidTable_t::next(index, sorted, maximum_sorted_entries)) >= 0) {
- const UidEntry *entry = sorted[index];
- uid_t u = entry->getKey();
- if ((uid != AID_ROOT) && (u != uid)) {
- continue;
- }
-
- if (!headerPrinted) {
- output += android::base::StringPrintf("\n\n");
- std::string name;
- if (uid == AID_ROOT) {
- name = android::base::StringPrintf(
- "Chattiest UIDs in %s log buffer:",
- android_log_id_to_name(id));
- } else {
- name = android::base::StringPrintf(
- "Logging for your UID in %s log buffer:",
- android_log_id_to_name(id));
- }
- std::string size = "Size";
- std::string pruned = "Pruned";
- if (!worstUidEnabledForLogid(id)) {
- pruned = "";
- }
- output += format_line(name, size, pruned);
-
- name = "UID PACKAGE";
- size = "BYTES";
- pruned = "LINES";
- if (!worstUidEnabledForLogid(id)) {
- pruned = "";
- }
- output += format_line(name, size, pruned);
-
- headerPrinted = true;
- }
-
- std::string name = android::base::StringPrintf("%u", u);
- char *n = uidToName(u);
- if (n) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(6 - name.length(), (size_t)1),
- "", n);
- free(n);
- }
-
- std::string size = android::base::StringPrintf("%zu",
- entry->getSizes());
-
- std::string pruned = "";
- size_t dropped = entry->getDropped();
- if (dropped) {
- pruned = android::base::StringPrintf("%zu", dropped);
- }
-
- output += format_line(name, size, pruned);
- }
+ name = (uid == AID_ROOT)
+ ? "Chattiest UIDs in %s log buffer:"
+ : "Logging for your UID in %s log buffer:";
+ output += uidTable[id].format(*this, uid, name, id);
}
if (enable) {
- // Pid table
- bool headerPrinted = false;
- std::unique_ptr<const PidEntry *[]> sorted = pidTable.sort(maximum_sorted_entries);
- ssize_t index = -1;
- while ((index = pidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
- const PidEntry *entry = sorted[index];
- uid_t u = entry->getUid();
- if ((uid != AID_ROOT) && (u != uid)) {
- continue;
- }
-
- if (!headerPrinted) {
- output += android::base::StringPrintf("\n\n");
- std::string name;
- if (uid == AID_ROOT) {
- name = android::base::StringPrintf("Chattiest PIDs:");
- } else {
- name = android::base::StringPrintf("Logging for this PID:");
- }
- std::string size = "Size";
- std::string pruned = "Pruned";
- output += format_line(name, size, pruned);
-
- name = " PID/UID COMMAND LINE";
- size = "BYTES";
- pruned = "LINES";
- output += format_line(name, size, pruned);
-
- headerPrinted = true;
- }
-
- std::string name = android::base::StringPrintf("%5u/%u",
- entry->getKey(), u);
- const char *n = entry->getName();
- if (n) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
- "", n);
- } else {
- char *un = uidToName(u);
- if (un) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
- "", un);
- free(un);
- }
- }
-
- std::string size = android::base::StringPrintf("%zu",
- entry->getSizes());
-
- std::string pruned = "";
- size_t dropped = entry->getDropped();
- if (dropped) {
- pruned = android::base::StringPrintf("%zu", dropped);
- }
-
- output += format_line(name, size, pruned);
- }
- }
-
- if (enable) {
- // Tid table
- bool headerPrinted = false;
- // sort() returns list of references, unique_ptr makes sure self-delete
- std::unique_ptr<const TidEntry *[]> sorted = tidTable.sort(maximum_sorted_entries);
- ssize_t index = -1;
- while ((index = tidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
- const TidEntry *entry = sorted[index];
- uid_t u = entry->getUid();
- if ((uid != AID_ROOT) && (u != uid)) {
- continue;
- }
-
- if (!headerPrinted) { // Only print header if we have table to print
- output += android::base::StringPrintf("\n\n");
- std::string name = "Chattiest TIDs:";
- std::string size = "Size";
- std::string pruned = "Pruned";
- output += format_line(name, size, pruned);
-
- name = " TID/UID COMM";
- size = "BYTES";
- pruned = "LINES";
- output += format_line(name, size, pruned);
-
- headerPrinted = true;
- }
-
- std::string name = android::base::StringPrintf("%5u/%u",
- entry->getKey(), u);
- const char *n = entry->getName();
- if (n) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
- "", n);
- } else {
- // if we do not have a PID name, lets punt to try UID name?
- char *un = uidToName(u);
- if (un) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
- "", un);
- free(un);
- }
- // We tried, better to not have a name at all, we still
- // have TID/UID by number to report in any case.
- }
-
- std::string size = android::base::StringPrintf("%zu",
- entry->getSizes());
-
- std::string pruned = "";
- size_t dropped = entry->getDropped();
- if (dropped) {
- pruned = android::base::StringPrintf("%zu", dropped);
- }
-
- output += format_line(name, size, pruned);
- }
+ name = (uid == AID_ROOT) ? "Chattiest PIDs:" : "Logging for this PID:";
+ output += pidTable.format(*this, uid, name);
+ name = "Chattiest TIDs:";
+ output += tidTable.format(*this, uid, name);
}
if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
- // Tag table
- bool headerPrinted = false;
- std::unique_ptr<const TagEntry *[]> sorted = tagTable.sort(maximum_sorted_entries);
- ssize_t index = -1;
- while ((index = tagTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
- const TagEntry *entry = sorted[index];
- uid_t u = entry->getUid();
- if ((uid != AID_ROOT) && (u != uid)) {
- continue;
- }
-
- std::string pruned = "";
-
- if (!headerPrinted) {
- output += android::base::StringPrintf("\n\n");
- std::string name = "Chattiest events log buffer TAGs:";
- std::string size = "Size";
- output += format_line(name, size, pruned);
-
- name = " TAG/UID TAGNAME";
- size = "BYTES";
- output += format_line(name, size, pruned);
-
- headerPrinted = true;
- }
-
- std::string name;
- if (u == (uid_t)-1) {
- name = android::base::StringPrintf("%7u",
- entry->getKey());
- } else {
- name = android::base::StringPrintf("%7u/%u",
- entry->getKey(), u);
- }
- const char *n = entry->getName();
- if (n) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(14 - name.length(), (size_t)1),
- "", n);
- }
-
- std::string size = android::base::StringPrintf("%zu",
- entry->getSizes());
-
- output += format_line(name, size, pruned);
- }
+ name = "Chattiest events log buffer TAGs:";
+ output += tagTable.format(*this, uid, name, LOG_ID_EVENTS);
}
return output;
@@ -536,8 +447,10 @@
}
// caller must free character string
-char *LogStatistics::pidToName(pid_t pid) {
- const char *name = pidTable.add(pid)->second.getName();
+const char *LogStatistics::pidToName(pid_t pid) const {
+ // An inconvenient truth ... getName() can alter the object
+ pidTable_t &writablePidTable = const_cast<pidTable_t &>(pidTable);
+ const char *name = writablePidTable.add(pid)->second.getName();
if (!name) {
return NULL;
}
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 5a44d59..6943820 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -21,9 +21,13 @@
#include <stdlib.h>
#include <sys/types.h>
+#include <algorithm> // std::max
+#include <string> // std::string
#include <unordered_map>
+#include <base/stringprintf.h>
#include <log/log.h>
+#include <private/android_filesystem_config.h>
#include "LogBufferElement.h"
#include "LogUtils.h"
@@ -31,6 +35,8 @@
#define log_id_for_each(i) \
for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t) (i + 1))
+class LogStatistics;
+
template <typename TKey, typename TEntry>
class LogHashtable {
@@ -39,50 +45,43 @@
public:
typedef typename std::unordered_map<TKey, TEntry>::iterator iterator;
+ typedef typename std::unordered_map<TKey, TEntry>::const_iterator const_iterator;
- std::unique_ptr<const TEntry *[]> sort(size_t n) {
- if (!n) {
+ std::unique_ptr<const TEntry *[]> sort(size_t len) const {
+ if (!len) {
std::unique_ptr<const TEntry *[]> sorted(NULL);
return sorted;
}
- const TEntry **retval = new const TEntry* [n];
- memset(retval, 0, sizeof(*retval) * n);
+ const TEntry **retval = new const TEntry* [len];
+ memset(retval, 0, sizeof(*retval) * len);
- for(iterator it = map.begin(); it != map.end(); ++it) {
+ for(const_iterator it = map.begin(); it != map.end(); ++it) {
const TEntry &entry = it->second;
- size_t s = entry.getSizes();
- ssize_t i = n - 1;
- while ((!retval[i] || (s > retval[i]->getSizes())) && (--i >= 0))
+ size_t sizes = entry.getSizes();
+ ssize_t index = len - 1;
+ while ((!retval[index] || (sizes > retval[index]->getSizes()))
+ && (--index >= 0))
;
- if (++i < (ssize_t)n) {
- size_t b = n - i - 1;
- if (b) {
- memmove(&retval[i+1], &retval[i], b * sizeof(retval[0]));
+ if (++index < (ssize_t)len) {
+ size_t num = len - index - 1;
+ if (num) {
+ memmove(&retval[index + 1], &retval[index],
+ num * sizeof(retval[0]));
}
- retval[i] = &entry;
+ retval[index] = &entry;
}
}
std::unique_ptr<const TEntry *[]> sorted(retval);
return sorted;
}
- // Iteration handler for the sort method output
- static ssize_t next(ssize_t index, std::unique_ptr<const TEntry *[]> &sorted, size_t n) {
- ++index;
- if (!sorted.get() || (index < 0) || (n <= (size_t)index) || !sorted[index]
- || (sorted[index]->getSizes() <= (sorted[0]->getSizes() / 100))) {
- return -1;
- }
- return index;
- }
-
- inline iterator add(TKey key, LogBufferElement *e) {
+ inline iterator add(TKey key, LogBufferElement *element) {
iterator it = map.find(key);
if (it == map.end()) {
- it = map.insert(std::make_pair(key, TEntry(e))).first;
+ it = map.insert(std::make_pair(key, TEntry(element))).first;
} else {
- it->second.add(e);
+ it->second.add(element);
}
return it;
}
@@ -97,65 +96,138 @@
return it;
}
- void subtract(TKey key, LogBufferElement *e) {
+ void subtract(TKey key, LogBufferElement *element) {
iterator it = map.find(key);
- if ((it != map.end()) && it->second.subtract(e)) {
+ if ((it != map.end()) && it->second.subtract(element)) {
map.erase(it);
}
}
- inline void drop(TKey key, LogBufferElement *e) {
+ inline void drop(TKey key, LogBufferElement *element) {
iterator it = map.find(key);
if (it != map.end()) {
- it->second.drop(e);
+ it->second.drop(element);
}
}
inline iterator begin() { return map.begin(); }
+ inline const_iterator begin() const { return map.begin(); }
inline iterator end() { return map.end(); }
+ inline const_iterator end() const { return map.end(); }
+ std::string format(
+ const LogStatistics &stat,
+ uid_t uid,
+ const std::string &name = std::string(""),
+ log_id_t id = LOG_ID_MAX) const {
+ static const size_t maximum_sorted_entries = 32;
+ std::string output;
+ std::unique_ptr<const TEntry *[]> sorted = sort(maximum_sorted_entries);
+
+ if (!sorted.get()) {
+ return output;
+ }
+ bool headerPrinted = false;
+ for (size_t index = 0; index < maximum_sorted_entries; ++index) {
+ const TEntry *entry = sorted[index];
+ if (!entry) {
+ break;
+ }
+ if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
+ break;
+ }
+ if ((uid != AID_ROOT) && (uid != entry->getUid())) {
+ continue;
+ }
+ if (!headerPrinted) {
+ output += "\n\n";
+ output += entry->formatHeader(name, id);
+ headerPrinted = true;
+ }
+ output += entry->format(stat, id);
+ }
+ return output;
+ }
};
+namespace EntryBaseConstants {
+ static constexpr size_t pruned_len = 14;
+ static constexpr size_t total_len = 80;
+}
+
struct EntryBase {
size_t size;
EntryBase():size(0) { }
- EntryBase(LogBufferElement *e):size(e->getMsgLen()) { }
+ EntryBase(LogBufferElement *element):size(element->getMsgLen()) { }
size_t getSizes() const { return size; }
- inline void add(LogBufferElement *e) { size += e->getMsgLen(); }
- inline bool subtract(LogBufferElement *e) { size -= e->getMsgLen(); return !size; }
+ inline void add(LogBufferElement *element) { size += element->getMsgLen(); }
+ inline bool subtract(LogBufferElement *element) {
+ size -= element->getMsgLen();
+ return !size;
+ }
+
+ static std::string formatLine(
+ const std::string &name,
+ const std::string &size,
+ const std::string &pruned) {
+ ssize_t drop_len = std::max(pruned.length() + 1,
+ EntryBaseConstants::pruned_len);
+ ssize_t size_len = std::max(size.length() + 1,
+ EntryBaseConstants::total_len
+ - name.length() - drop_len - 1);
+
+ if (pruned.length()) {
+ return android::base::StringPrintf("%s%*s%*s\n", name.c_str(),
+ (int)size_len, size.c_str(),
+ (int)drop_len, pruned.c_str());
+ } else {
+ return android::base::StringPrintf("%s%*s\n", name.c_str(),
+ (int)size_len, size.c_str());
+ }
+ }
};
struct EntryBaseDropped : public EntryBase {
size_t dropped;
EntryBaseDropped():dropped(0) { }
- EntryBaseDropped(LogBufferElement *e):EntryBase(e),dropped(e->getDropped()){ }
+ EntryBaseDropped(LogBufferElement *element):
+ EntryBase(element),
+ dropped(element->getDropped()){
+ }
size_t getDropped() const { return dropped; }
- inline void add(LogBufferElement *e) {
- dropped += e->getDropped();
- EntryBase::add(e);
+ inline void add(LogBufferElement *element) {
+ dropped += element->getDropped();
+ EntryBase::add(element);
}
- inline bool subtract(LogBufferElement *e) {
- dropped -= e->getDropped();
- return EntryBase::subtract(e) && !dropped;
+ inline bool subtract(LogBufferElement *element) {
+ dropped -= element->getDropped();
+ return EntryBase::subtract(element) && !dropped;
}
- inline void drop(LogBufferElement *e) {
+ inline void drop(LogBufferElement *element) {
dropped += 1;
- EntryBase::subtract(e);
+ EntryBase::subtract(element);
}
};
struct UidEntry : public EntryBaseDropped {
const uid_t uid;
- UidEntry(LogBufferElement *e):EntryBaseDropped(e),uid(e->getUid()) { }
+ UidEntry(LogBufferElement *element):
+ EntryBaseDropped(element),
+ uid(element->getUid()) {
+ }
inline const uid_t&getKey() const { return uid; }
+ inline const uid_t&getUid() const { return uid; }
+
+ std::string formatHeader(const std::string &name, log_id_t id) const;
+ std::string format(const LogStatistics &stat, log_id_t id) const;
};
namespace android {
@@ -167,51 +239,54 @@
uid_t uid;
char *name;
- PidEntry(pid_t p):
- EntryBaseDropped(),
- pid(p),
- uid(android::pidToUid(p)),
- name(android::pidToName(pid)) { }
- PidEntry(LogBufferElement *e):
- EntryBaseDropped(e),
- pid(e->getPid()),
- uid(e->getUid()),
- name(android::pidToName(e->getPid())) { }
- PidEntry(const PidEntry &c):
- EntryBaseDropped(c),
- pid(c.pid),
- uid(c.uid),
- name(c.name ? strdup(c.name) : NULL) { }
+ PidEntry(pid_t pid):
+ EntryBaseDropped(),
+ pid(pid),
+ uid(android::pidToUid(pid)),
+ name(android::pidToName(pid)) {
+ }
+ PidEntry(LogBufferElement *element):
+ EntryBaseDropped(element),
+ pid(element->getPid()),
+ uid(element->getUid()),
+ name(android::pidToName(pid)) {
+ }
+ PidEntry(const PidEntry &element):
+ EntryBaseDropped(element),
+ pid(element.pid),
+ uid(element.uid),
+ name(element.name ? strdup(element.name) : NULL) {
+ }
~PidEntry() { free(name); }
const pid_t&getKey() const { return pid; }
const uid_t&getUid() const { return uid; }
const char*getName() const { return name; }
- inline void add(pid_t p) {
+ inline void add(pid_t newPid) {
if (name && !fast<strncmp>(name, "zygote", 6)) {
free(name);
name = NULL;
}
if (!name) {
- char *n = android::pidToName(p);
- if (n) {
- name = n;
- }
+ name = android::pidToName(newPid);
}
}
- inline void add(LogBufferElement *e) {
- uid_t u = e->getUid();
- if (getUid() != u) {
- uid = u;
+ inline void add(LogBufferElement *element) {
+ uid_t incomingUid = element->getUid();
+ if (getUid() != incomingUid) {
+ uid = incomingUid;
free(name);
- name = android::pidToName(e->getPid());
+ name = android::pidToName(element->getPid());
} else {
- add(e->getPid());
+ add(element->getPid());
}
- EntryBaseDropped::add(e);
+ EntryBaseDropped::add(element);
}
+
+ std::string formatHeader(const std::string &name, log_id_t id) const;
+ std::string format(const LogStatistics &stat, log_id_t id) const;
};
struct TidEntry : public EntryBaseDropped {
@@ -219,79 +294,87 @@
uid_t uid;
char *name;
- TidEntry(pid_t t):
- EntryBaseDropped(),
- tid(t),
- uid(android::pidToUid(t)),
- name(android::tidToName(tid)) { }
- TidEntry(LogBufferElement *e):
- EntryBaseDropped(e),
- tid(e->getTid()),
- uid(e->getUid()),
- name(android::tidToName(e->getTid())) { }
- TidEntry(const TidEntry &c):
- EntryBaseDropped(c),
- tid(c.tid),
- uid(c.uid),
- name(c.name ? strdup(c.name) : NULL) { }
+ TidEntry(pid_t tid):
+ EntryBaseDropped(),
+ tid(tid),
+ uid(android::pidToUid(tid)),
+ name(android::tidToName(tid)) {
+ }
+ TidEntry(LogBufferElement *element):
+ EntryBaseDropped(element),
+ tid(element->getTid()),
+ uid(element->getUid()),
+ name(android::tidToName(tid)) {
+ }
+ TidEntry(const TidEntry &element):
+ EntryBaseDropped(element),
+ tid(element.tid),
+ uid(element.uid),
+ name(element.name ? strdup(element.name) : NULL) {
+ }
~TidEntry() { free(name); }
const pid_t&getKey() const { return tid; }
const uid_t&getUid() const { return uid; }
const char*getName() const { return name; }
- inline void add(pid_t t) {
+ inline void add(pid_t incomingTid) {
if (name && !fast<strncmp>(name, "zygote", 6)) {
free(name);
name = NULL;
}
if (!name) {
- char *n = android::tidToName(t);
- if (n) {
- name = n;
- }
+ name = android::tidToName(incomingTid);
}
}
- inline void add(LogBufferElement *e) {
- uid_t u = e->getUid();
- if (getUid() != u) {
- uid = u;
+ inline void add(LogBufferElement *element) {
+ uid_t incomingUid = element->getUid();
+ if (getUid() != incomingUid) {
+ uid = incomingUid;
free(name);
- name = android::tidToName(e->getTid());
+ name = android::tidToName(element->getTid());
} else {
- add(e->getTid());
+ add(element->getTid());
}
- EntryBaseDropped::add(e);
+ EntryBaseDropped::add(element);
}
+
+ std::string formatHeader(const std::string &name, log_id_t id) const;
+ std::string format(const LogStatistics &stat, log_id_t id) const;
};
struct TagEntry : public EntryBase {
const uint32_t tag;
uid_t uid;
- TagEntry(LogBufferElement *e):
- EntryBase(e),
- tag(e->getTag()),
- uid(e->getUid()) { }
+ TagEntry(LogBufferElement *element):
+ EntryBase(element),
+ tag(element->getTag()),
+ uid(element->getUid()) {
+ }
const uint32_t&getKey() const { return tag; }
const uid_t&getUid() const { return uid; }
const char*getName() const { return android::tagToName(tag); }
- inline void add(LogBufferElement *e) {
- uid_t u = e->getUid();
- if (uid != u) {
+ inline void add(LogBufferElement *element) {
+ uid_t incomingUid = element->getUid();
+ if (uid != incomingUid) {
uid = -1;
}
- EntryBase::add(e);
+ EntryBase::add(element);
}
+
+ std::string formatHeader(const std::string &name, log_id_t id) const;
+ std::string format(const LogStatistics &stat, log_id_t id) const;
};
// Log Statistics
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;
@@ -321,23 +404,32 @@
void subtract(LogBufferElement *entry);
// entry->setDropped(1) must follow this call
void drop(LogBufferElement *entry);
- // Correct for merging two entries referencing dropped content
- void erase(LogBufferElement *e) { --mElements[e->getLogId()]; }
+ // Correct for coalescing two entries referencing dropped content
+ 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 n, log_id i) { return uidTable[i].sort(n); }
+ std::unique_ptr<const UidEntry *[]> sort(size_t len, log_id id) {
+ return uidTable[id].sort(len);
+ }
// 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]; }
- std::string format(uid_t uid, unsigned int logMask);
+ std::string format(uid_t uid, unsigned int logMask) const;
// helper (must be locked directly or implicitly by mLogElementsLock)
- char *pidToName(pid_t pid);
+ const char *pidToName(pid_t pid) const;
uid_t pidToUid(pid_t pid);
- char *uidToName(uid_t uid);
+ const char *uidToName(uid_t uid) const;
};
#endif // _LOGD_LOG_STATISTICS_H__
diff --git a/logd/main.cpp b/logd/main.cpp
index 60262e9..13dda78 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -33,12 +33,14 @@
#include <syslog.h>
#include <unistd.h>
+#include <cstdbool>
#include <memory>
#include <cutils/properties.h>
#include <cutils/sched_policy.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
+#include <packagelistparser/packagelistparser.h>
#include <private/android_filesystem_config.h>
#include <utils/threads.h>
@@ -166,6 +168,19 @@
static bool reinit_running = false;
static LogBuffer *logBuf = NULL;
+static bool package_list_parser_cb(pkg_info *info, void * /* userdata */) {
+
+ bool rc = true;
+ if (info->uid == uid) {
+ name = strdup(info->name);
+ // false to stop processing
+ rc = false;
+ }
+
+ packagelist_free(info);
+ return rc;
+}
+
static void *reinit_thread_start(void * /*obj*/) {
prctl(PR_SET_NAME, "logd.daemon");
set_sched_policy(0, SP_BACKGROUND);
@@ -180,31 +195,8 @@
if (uid) {
name = NULL;
- FILE *fp = fopen("/data/system/packages.list", "r");
- if (fp) {
- // This simple parser is sensitive to format changes in
- // frameworks/base/services/core/java/com/android/server/pm/Settings.java
- // A dependency note has been added to that file to correct
- // this parser.
+ packagelist_parse(package_list_parser_cb, NULL);
- char *buffer = NULL;
- size_t len;
- while (getline(&buffer, &len, fp) > 0) {
- char *userId = strchr(buffer, ' ');
- if (!userId) {
- continue;
- }
- *userId = '\0';
- unsigned long value = strtoul(userId + 1, NULL, 10);
- if (value != uid) {
- continue;
- }
- name = strdup(buffer);
- break;
- }
- free(buffer);
- fclose(fp);
- }
uid = 0;
sem_post(&uidName);
continue;
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
index afb45da..8dbaad9 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -69,12 +69,12 @@
libchrome-dbus \
libchromeos-http \
libchromeos-dbus \
- libcutils \
libdbus \
libmetrics \
libprotobuf-cpp-lite \
librootdev \
- libweaved-client \
+ 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 f83c5d4..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;
@@ -283,11 +279,15 @@
return EX_UNAVAILABLE;
}
- weaved_object_mgr_.reset(new com::android::Weave::ObjectManagerProxy{bus_});
- weaved_object_mgr_->SetCommandAddedCallback(
- base::Bind(&MetricsDaemon::OnWeaveCommand, base::Unretained(this)));
- weaved_object_mgr_->SetManagerAddedCallback(
+ device_ = weaved::Device::CreateInstance(
+ bus_,
base::Bind(&MetricsDaemon::UpdateWeaveState, base::Unretained(this)));
+ device_->AddCommandHandler(
+ "_metrics._enableAnalyticsReporting",
+ base::Bind(&MetricsDaemon::OnEnableMetrics, base::Unretained(this)));
+ device_->AddCommandHandler(
+ "_metrics._disableAnalyticsReporting",
+ base::Bind(&MetricsDaemon::OnDisableMetrics, base::Unretained(this)));
}
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
@@ -322,23 +322,14 @@
<< error.name << ": " << error.message;
}
}
- chromeos::DBusDaemon::OnShutdown(return_code);
+ brillo::DBusDaemon::OnShutdown(return_code);
}
-void MetricsDaemon::OnWeaveCommand(CommandProxy* command) {
- if (command->state() != "queued") {
+void MetricsDaemon::OnEnableMetrics(const std::weak_ptr<weaved::Command>& cmd) {
+ auto command = cmd.lock();
+ if (!command)
return;
- }
- VLOG(1) << "received weave command: " << command->name();
- if (command->name() == "_metrics._enableAnalyticsReporting") {
- OnEnableMetrics(command);
- } else if (command->name() == "_metrics._disableAnalyticsReporting") {
- OnDisableMetrics(command);
- }
-}
-
-void MetricsDaemon::OnEnableMetrics(CommandProxy* command) {
if (base::WriteFile(metrics_directory_.Append(metrics::kConsentFileName),
"", 0) != 0) {
PLOG(ERROR) << "Could not create the consent file.";
@@ -347,11 +338,16 @@
return;
}
- NotifyStateChanged();
+ UpdateWeaveState();
command->Complete({}, nullptr);
}
-void MetricsDaemon::OnDisableMetrics(CommandProxy* command) {
+void MetricsDaemon::OnDisableMetrics(
+ const std::weak_ptr<weaved::Command>& cmd) {
+ auto command = cmd.lock();
+ if (!command)
+ return;
+
if (!base::DeleteFile(metrics_directory_.Append(metrics::kConsentFileName),
false)) {
PLOG(ERROR) << "Could not delete the consent file.";
@@ -360,23 +356,20 @@
return;
}
- NotifyStateChanged();
+ UpdateWeaveState();
command->Complete({}, nullptr);
}
-void MetricsDaemon::NotifyStateChanged() {
- ManagerProxy* manager = weaved_object_mgr_->GetManagerProxy();
- if (manager)
- UpdateWeaveState(manager);
-}
+void MetricsDaemon::UpdateWeaveState() {
+ if (!device_)
+ return;
-void MetricsDaemon::UpdateWeaveState(ManagerProxy* manager) {
- chromeos::VariantDictionary state_change{
+ brillo::VariantDictionary state_change{
{ "_metrics._AnalyticsReportingState",
metrics_lib_->AreMetricsEnabled() ? "enabled" : "disabled" }
};
- if (!manager->UpdateState(state_change, nullptr)) {
+ if (!device_->SetStateProperties(state_change, nullptr)) {
LOG(ERROR) << "failed to update weave's state";
}
}
diff --git a/metricsd/metrics_daemon.h b/metricsd/metrics_daemon.h
index 0a8f6a5..3d691c5 100644
--- a/metricsd/metrics_daemon.h
+++ b/metricsd/metrics_daemon.h
@@ -26,8 +26,9 @@
#include <base/files/file_path.h>
#include <base/memory/scoped_ptr.h>
#include <base/time/time.h>
-#include <buffet/dbus-proxies.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
#include "collectors/averaged_statistics_collector.h"
@@ -38,7 +39,7 @@
using chromeos_metrics::PersistentInteger;
-class MetricsDaemon : public chromeos::DBusDaemon {
+class MetricsDaemon : public brillo::DBusDaemon {
public:
MetricsDaemon();
~MetricsDaemon();
@@ -122,20 +123,14 @@
DBusMessage* message,
void* user_data);
- // Callback for Weave commands.
- void OnWeaveCommand(com::android::Weave::CommandProxy* command);
-
// Enables metrics reporting.
- void OnEnableMetrics(com::android::Weave::CommandProxy* command);
+ void OnEnableMetrics(const std::weak_ptr<weaved::Command>& cmd);
// Disables metrics reporting.
- void OnDisableMetrics(com::android::Weave::CommandProxy* command);
+ void OnDisableMetrics(const std::weak_ptr<weaved::Command>& cmd);
// Updates the weave device state.
- void UpdateWeaveState(com::android::Weave::ManagerProxy* manager);
-
- // Tells Weave that the state has changed.
- void NotifyStateChanged();
+ void UpdateWeaveState();
// Updates the active use time and logs time between user-space
// process crashes.
@@ -317,7 +312,7 @@
std::string server_;
scoped_ptr<UploadService> upload_service_;
- scoped_ptr<com::android::Weave::ObjectManagerProxy> weaved_object_mgr_;
+ std::unique_ptr<weaved::Device> device_;
};
#endif // METRICS_METRICS_DAEMON_H_
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;
diff --git a/packagelistparser/Android.mk b/packagelistparser/Android.mk
new file mode 100644
index 0000000..802a3cb
--- /dev/null
+++ b/packagelistparser/Android.mk
@@ -0,0 +1,32 @@
+LOCAL_PATH:= $(call my-dir)
+
+#########################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libpackagelistparser
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := packagelistparser.c
+LOCAL_COPY_HEADERS_TO := packagelistparser
+LOCAL_COPY_HEADERS := packagelistparser.h
+LOCAL_SHARED_LIBRARIES := liblog
+
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
+
+include $(BUILD_SHARED_LIBRARY)
+
+#########################
+include $(CLEAR_VARS)
+
+
+LOCAL_MODULE := libpackagelistparser
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := packagelistparser.c
+LOCAL_COPY_HEADERS_TO := packagelistparser
+LOCAL_COPY_HEADERS := packagelistparser.h
+LOCAL_STATIC_LIBRARIES := liblog
+
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/packagelistparser/packagelistparser.c b/packagelistparser/packagelistparser.c
new file mode 100644
index 0000000..3e2539c
--- /dev/null
+++ b/packagelistparser/packagelistparser.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2015, Intel Corporation
+ * 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.
+ *
+ * Written by William Roberts <william.c.roberts@intel.com>
+ *
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/limits.h>
+
+#define LOG_TAG "packagelistparser"
+#include <utils/Log.h>
+
+#include "packagelistparser.h"
+
+#define CLOGE(fmt, ...) \
+ do {\
+ IF_ALOGE() {\
+ ALOGE(fmt, ##__VA_ARGS__);\
+ }\
+ } while(0)
+
+static size_t get_gid_cnt(const char *gids)
+{
+ size_t cnt;
+
+ if (*gids == '\0') {
+ return 0;
+ }
+
+ if (!strcmp(gids, "none")) {
+ return 0;
+ }
+
+ for (cnt = 1; gids[cnt]; gids[cnt] == ',' ? cnt++ : *gids++)
+ ;
+
+ return cnt;
+}
+
+static bool parse_gids(char *gids, gid_t *gid_list, size_t *cnt)
+{
+ gid_t gid;
+ char* token;
+ char *endptr;
+ size_t cmp = 0;
+
+ while ((token = strsep(&gids, ",\r\n"))) {
+
+ if (cmp > *cnt) {
+ return false;
+ }
+
+ gid = strtoul(token, &endptr, 10);
+ if (*endptr != '\0') {
+ return false;
+ }
+
+ /*
+ * if unsigned long is greater than size of gid_t,
+ * prevent a truncation based roll-over
+ */
+ if (gid > GID_MAX) {
+ CLOGE("A gid in field \"gid list\" greater than GID_MAX");
+ return false;
+ }
+
+ gid_list[cmp++] = gid;
+ }
+ return true;
+}
+
+extern bool packagelist_parse(pfn_on_package callback, void *userdata)
+{
+
+ FILE *fp;
+ char *cur;
+ char *next;
+ char *endptr;
+ unsigned long tmp;
+ ssize_t bytesread;
+
+ bool rc = false;
+ char *buf = NULL;
+ size_t buflen = 0;
+ unsigned long lineno = 1;
+ const char *errmsg = NULL;
+ struct pkg_info *pkg_info = NULL;
+
+ fp = fopen(PACKAGES_LIST_FILE, "re");
+ if (!fp) {
+ CLOGE("Could not open: \"%s\", error: \"%s\"\n", PACKAGES_LIST_FILE,
+ strerror(errno));
+ return false;
+ }
+
+ while ((bytesread = getline(&buf, &buflen, fp)) > 0) {
+
+ pkg_info = calloc(1, sizeof(*pkg_info));
+ if (!pkg_info) {
+ goto err;
+ }
+
+ next = buf;
+
+ cur = strsep(&next, " \t\r\n");
+ if (!cur) {
+ errmsg = "Could not get next token for \"package name\"";
+ goto err;
+ }
+
+ pkg_info->name = strdup(cur);
+ if (!pkg_info->name) {
+ goto err;
+ }
+
+ cur = strsep(&next, " \t\r\n");
+ if (!cur) {
+ errmsg = "Could not get next token for field \"uid\"";
+ goto err;
+ }
+
+ tmp = strtoul(cur, &endptr, 10);
+ if (*endptr != '\0') {
+ errmsg = "Could not convert field \"uid\" to integer value";
+ goto err;
+ }
+
+ /*
+ * if unsigned long is greater than size of uid_t,
+ * prevent a truncation based roll-over
+ */
+ if (tmp > UID_MAX) {
+ errmsg = "Field \"uid\" greater than UID_MAX";
+ goto err;
+ }
+
+ pkg_info->uid = (uid_t) tmp;
+
+ cur = strsep(&next, " \t\r\n");
+ if (!cur) {
+ errmsg = "Could not get next token for field \"debuggable\"";
+ goto err;
+ }
+
+ tmp = strtoul(cur, &endptr, 10);
+ if (*endptr != '\0') {
+ errmsg = "Could not convert field \"debuggable\" to integer value";
+ goto err;
+ }
+
+ /* should be a valid boolean of 1 or 0 */
+ if (!(tmp == 0 || tmp == 1)) {
+ errmsg = "Field \"debuggable\" is not 0 or 1 boolean value";
+ goto err;
+ }
+
+ pkg_info->debuggable = (bool) tmp;
+
+ cur = strsep(&next, " \t\r\n");
+ if (!cur) {
+ errmsg = "Could not get next token for field \"data dir\"";
+ goto err;
+ }
+
+ pkg_info->data_dir = strdup(cur);
+ if (!pkg_info->data_dir) {
+ goto err;
+ }
+
+ cur = strsep(&next, " \t\r\n");
+ if (!cur) {
+ errmsg = "Could not get next token for field \"seinfo\"";
+ goto err;
+ }
+
+ pkg_info->seinfo = strdup(cur);
+ if (!pkg_info->seinfo) {
+ goto err;
+ }
+
+ cur = strsep(&next, " \t\r\n");
+ if (!cur) {
+ errmsg = "Could not get next token for field \"gid(s)\"";
+ goto err;
+ }
+
+ /*
+ * Parse the gid list, could be in the form of none, single gid or list:
+ * none
+ * gid
+ * gid, gid ...
+ */
+ pkg_info->gids.cnt = get_gid_cnt(cur);
+ if (pkg_info->gids.cnt > 0) {
+
+ pkg_info->gids.gids = calloc(pkg_info->gids.cnt, sizeof(gid_t));
+ if (!pkg_info->gids.gids) {
+ goto err;
+ }
+
+ rc = parse_gids(cur, pkg_info->gids.gids, &pkg_info->gids.cnt);
+ if (!rc) {
+ errmsg = "Could not parse field \"gid list\"";
+ goto err;
+ }
+ }
+
+ rc = callback(pkg_info, userdata);
+ if (rc == false) {
+ /*
+ * We do not log this as this can be intentional from
+ * callback to abort processing. We go to out to not
+ * free the pkg_info
+ */
+ rc = true;
+ goto out;
+ }
+ lineno++;
+ }
+
+ rc = true;
+
+out:
+ free(buf);
+ fclose(fp);
+ return rc;
+
+err:
+ if (errmsg) {
+ CLOGE("Error Parsing \"%s\" on line: %lu for reason: %s",
+ PACKAGES_LIST_FILE, lineno, errmsg);
+ }
+ rc = false;
+ packagelist_free(pkg_info);
+ goto out;
+}
+
+void packagelist_free(pkg_info *info)
+{
+ if (info) {
+ free(info->name);
+ free(info->data_dir);
+ free(info->seinfo);
+ free(info->gids.gids);
+ free(info);
+ }
+}
diff --git a/packagelistparser/packagelistparser.h b/packagelistparser/packagelistparser.h
new file mode 100644
index 0000000..d602c26
--- /dev/null
+++ b/packagelistparser/packagelistparser.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015, Intel Corporation
+ * 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.
+ *
+ * Written by William Roberts <william.c.roberts@intel.com>
+ *
+ * This is a parser library for parsing the packages.list file generated
+ * by PackageManager service.
+ *
+ * This simple parser is sensitive to format changes in
+ * frameworks/base/services/core/java/com/android/server/pm/Settings.java
+ * A dependency note has been added to that file to correct
+ * this parser.
+ */
+
+#ifndef PACKAGELISTPARSER_H_
+#define PACKAGELISTPARSER_H_
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+/** The file containing the list of installed packages on the system */
+#define PACKAGES_LIST_FILE "/data/system/packages.list"
+
+typedef struct pkg_info pkg_info;
+typedef struct gid_list gid_list;
+
+struct gid_list {
+ size_t cnt;
+ gid_t *gids;
+};
+
+struct pkg_info {
+ char *name;
+ uid_t uid;
+ bool debuggable;
+ char *data_dir;
+ char *seinfo;
+ gid_list gids;
+ void *private_data;
+};
+
+/**
+ * Callback function to be used by packagelist_parse() routine.
+ * @param info
+ * The parsed package information
+ * @param userdata
+ * The supplied userdata pointer to packagelist_parse()
+ * @return
+ * true to keep processing, false to stop.
+ */
+typedef bool (*pfn_on_package)(pkg_info *info, void *userdata);
+
+/**
+ * Parses the file specified by PACKAGES_LIST_FILE and invokes the callback on
+ * each entry found. Once the callback is invoked, ownership of the pkg_info pointer
+ * is passed to the callback routine, thus they are required to perform any cleanup
+ * desired.
+ * @param callback
+ * The callback function called on each parsed line of the packages list.
+ * @param userdata
+ * An optional userdata supplied pointer to pass to the callback function.
+ * @return
+ * true on success false on failure.
+ */
+extern bool packagelist_parse(pfn_on_package callback, void *userdata);
+
+/**
+ * Frees a pkg_info structure.
+ * @param info
+ * The struct to free
+ */
+extern void packagelist_free(pkg_info *info);
+
+__END_DECLS
+
+#endif /* PACKAGELISTPARSER_H_ */
diff --git a/rootdir/init.rc b/rootdir/init.rc
index bd4d156..38c686d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -13,6 +13,9 @@
# Set init and its forked children's oom_adj.
write /proc/1/oom_score_adj -1000
+ # Disable sysrq from keyboard
+ write /proc/sys/kernel/sysrq 0
+
# Set the security context of /adb_keys if present.
restorecon /adb_keys
diff --git a/sdcard/Android.mk b/sdcard/Android.mk
index cb3a8fb..c5f3d1d 100644
--- a/sdcard/Android.mk
+++ b/sdcard/Android.mk
@@ -5,7 +5,6 @@
LOCAL_SRC_FILES := sdcard.c
LOCAL_MODULE := sdcard
LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror
-
-LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_SHARED_LIBRARIES := libcutils libpackagelistparser
include $(BUILD_EXECUTABLE)
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index d2d2315..b6bbe7e 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -24,6 +24,7 @@
#include <limits.h>
#include <linux/fuse.h>
#include <pthread.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -34,6 +35,7 @@
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/time.h>
+#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
@@ -41,6 +43,7 @@
#include <cutils/hashmap.h>
#include <cutils/log.h>
#include <cutils/multiuser.h>
+#include <packagelistparser/packagelistparser.h>
#include <private/android_filesystem_config.h>
@@ -103,9 +106,6 @@
* or that a reply has already been written. */
#define NO_STATUS 1
-/* Path to system-provided mapping of package name to appIds */
-static const char* const kPackagesListFile = "/data/system/packages.list";
-
/* Supplementary groups to execute with */
static const gid_t kGroups[1] = { AID_PACKAGE_INFO };
@@ -1636,35 +1636,27 @@
return true;
}
-static int read_package_list(struct fuse_global* global) {
+static bool package_parse_callback(pkg_info *info, void *userdata) {
+ struct fuse_global *global = (struct fuse_global *)userdata;
+
+ char* name = strdup(info->name);
+ hashmapPut(global->package_to_appid, name, (void*) (uintptr_t) info->uid);
+ packagelist_free(info);
+ return true;
+}
+
+static bool read_package_list(struct fuse_global* global) {
pthread_mutex_lock(&global->lock);
hashmapForEach(global->package_to_appid, remove_str_to_int, global->package_to_appid);
- FILE* file = fopen(kPackagesListFile, "r");
- if (!file) {
- ERROR("failed to open package list: %s\n", strerror(errno));
- pthread_mutex_unlock(&global->lock);
- return -1;
- }
-
- char buf[512];
- while (fgets(buf, sizeof(buf), file) != NULL) {
- char package_name[512];
- int appid;
- char gids[512];
-
- if (sscanf(buf, "%s %d %*d %*s %*s %s", package_name, &appid, gids) == 3) {
- char* package_name_dup = strdup(package_name);
- hashmapPut(global->package_to_appid, package_name_dup, (void*) (uintptr_t) appid);
- }
- }
-
+ bool rc = packagelist_parse(package_parse_callback, global);
TRACE("read_package_list: found %zu packages\n",
hashmapSize(global->package_to_appid));
- fclose(file);
+
pthread_mutex_unlock(&global->lock);
- return 0;
+
+ return rc;
}
static void watch_package_list(struct fuse_global* global) {
@@ -1680,11 +1672,11 @@
bool active = false;
while (1) {
if (!active) {
- int res = inotify_add_watch(nfd, kPackagesListFile, IN_DELETE_SELF);
+ int res = inotify_add_watch(nfd, PACKAGES_LIST_FILE, IN_DELETE_SELF);
if (res == -1) {
if (errno == ENOENT || errno == EACCES) {
/* Framework may not have created yet, sleep and retry */
- ERROR("missing packages.list; retrying\n");
+ ERROR("missing \"%s\"; retrying\n", PACKAGES_LIST_FILE);
sleep(3);
continue;
} else {
@@ -1695,8 +1687,8 @@
/* Watch above will tell us about any future changes, so
* read the current state. */
- if (read_package_list(global) == -1) {
- ERROR("read_package_list failed: %s\n", strerror(errno));
+ if (read_package_list(global) == false) {
+ ERROR("read_package_list failed\n");
return;
}
active = true;
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index 579d26e..52716e9 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -31,7 +31,6 @@
dd \
OUR_TOOLS := \
- df \
getevent \
iftop \
ioctl \
diff --git a/toolbox/df.c b/toolbox/df.c
deleted file mode 100644
index 9cd0743..0000000
--- a/toolbox/df.c
+++ /dev/null
@@ -1,85 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/statfs.h>
-
-static int ok = EXIT_SUCCESS;
-
-static void printsize(long long n)
-{
- char unit = 'K';
- long long t;
-
- n *= 10;
-
- if (n > 1024*1024*10) {
- n /= 1024;
- unit = 'M';
- }
-
- if (n > 1024*1024*10) {
- n /= 1024;
- unit = 'G';
- }
-
- t = (n + 512) / 1024;
- printf("%4lld.%1lld%c", t/10, t%10, unit);
-}
-
-static void df(char *s, int always) {
- struct statfs st;
-
- if (statfs(s, &st) < 0) {
- fprintf(stderr, "%s: %s\n", s, strerror(errno));
- ok = EXIT_FAILURE;
- } else {
- if (st.f_blocks == 0 && !always)
- return;
- printf("%-20s ", s);
- printsize((long long)st.f_blocks * (long long)st.f_bsize);
- printf(" ");
- printsize((long long)(st.f_blocks - (long long)st.f_bfree) * st.f_bsize);
- printf(" ");
- printsize((long long)st.f_bfree * (long long)st.f_bsize);
- printf(" %d\n", (int) st.f_bsize);
- }
-}
-
-int df_main(int argc, char *argv[]) {
- printf("Filesystem Size Used Free Blksize\n");
- if (argc == 1) {
- char s[2000];
- FILE *f = fopen("/proc/mounts", "r");
-
- while (fgets(s, 2000, f)) {
- char *c, *e = s;
-
- for (c = s; *c; c++) {
- if (*c == ' ') {
- e = c + 1;
- break;
- }
- }
-
- for (c = e; *c; c++) {
- if (*c == ' ') {
- *c = '\0';
- break;
- }
- }
-
- df(e, 0);
- }
-
- fclose(f);
- } else {
- int i;
-
- for (i = 1; i < argc; i++) {
- df(argv[i], 1);
- }
- }
-
- exit(ok);
-}