Merge "logd: use libpackageparser"
diff --git a/adb/Android.mk b/adb/Android.mk
index 4ee1ced..e10e3ef 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -294,11 +294,15 @@
libadbd \
libbase \
libfs_mgr \
+ libfec \
+ libfec_rs \
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/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_reporter.rc b/crash_reporter/crash_reporter.rc
index 7bfe0c2..57c1d40 100644
--- a/crash_reporter/crash_reporter.rc
+++ b/crash_reporter/crash_reporter.rc
@@ -5,7 +5,7 @@
on property:crash_reporter.coredump.enabled=0
write /proc/sys/kernel/core_pattern "core"
-on boot
+on post-fs-data
# Allow catching multiple unrelated concurrent crashes, but use a finite
# number to prevent infinitely recursing on crash handling.
write /proc/sys/kernel/core_pipe_limit 4
diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender
index 1f58792..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,7 +299,7 @@
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 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")"
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 713638d..1287fb9 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -528,7 +528,7 @@
return 1;
fcntl(s, F_SETFD, FD_CLOEXEC);
- ALOGI("debuggerd: " __DATE__ " " __TIME__ "\n");
+ ALOGI("debuggerd: starting\n");
for (;;) {
sockaddr addr;
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index b47199f..28fff3f 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -1,49 +1,59 @@
# Copyright 2011 The Android Open Source Project
LOCAL_PATH:= $(call my-dir)
+
+common_static_libraries := \
+ liblogwrap \
+ libfec \
+ libfec_rs \
+ libbase \
+ libmincrypt \
+ libcrypto_static \
+ libext4_utils_static \
+ libsquashfs_utils
+
include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.cpp fs_mgr_fstab.c
-LOCAL_SRC_FILES += fs_mgr_format.c fs_mgr_slotselect.c
-
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
+LOCAL_SRC_FILES:= \
+ fs_mgr.c \
+ fs_mgr_format.c \
+ fs_mgr_fstab.c \
+ fs_mgr_slotselect.c \
+ fs_mgr_verity.cpp
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include \
system/vold \
system/extras/ext4_utils \
- external/openssl/include
-
+ external/openssl/include \
+ bootable/recovery
LOCAL_MODULE:= libfs_mgr
-LOCAL_STATIC_LIBRARIES := liblogwrap libmincrypt libext4_utils_static libsquashfs_utils libbase
-LOCAL_C_INCLUDES += system/extras/ext4_utils system/extras/squashfs_utils \
- bootable/recovery
+LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_CFLAGS := -Werror
-
ifneq (,$(filter userdebug,$(TARGET_BUILD_VARIANT)))
LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
endif
-
include $(BUILD_STATIC_LIBRARY)
-
-
include $(CLEAR_VARS)
-
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
LOCAL_SRC_FILES:= fs_mgr_main.c
-
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
LOCAL_MODULE:= fs_mgr
-
LOCAL_MODULE_TAGS := optional
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
-
-LOCAL_STATIC_LIBRARIES := libfs_mgr liblogwrap libcutils liblog libc libmincrypt libext4_utils_static libsquashfs_utils libbase
-LOCAL_STATIC_LIBRARIES += libsparse_static libz libselinux
+LOCAL_STATIC_LIBRARIES := libfs_mgr \
+ $(common_static_libraries) \
+ libcutils \
+ liblog \
+ libc \
+ libsparse_static \
+ libz \
+ libselinux
LOCAL_CXX_STL := libc++_static
-
LOCAL_CFLAGS := -Werror
-
include $(BUILD_EXECUTABLE)
-
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 8f7c6a2..c358982 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -38,8 +38,7 @@
#include "mincrypt/sha.h"
#include "mincrypt/sha256.h"
-#include "ext4_sb.h"
-#include "squashfs_utils.h"
+#include "fec/io.h"
#include "fs_mgr.h"
#include "fs_mgr_priv.h"
@@ -47,11 +46,19 @@
#define FSTAB_PREFIX "/fstab."
-#define VERITY_METADATA_SIZE 32768
#define VERITY_TABLE_RSA_KEY "/verity_key"
#define VERITY_TABLE_HASH_IDX 8
#define VERITY_TABLE_SALT_IDX 9
+#define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
+#define VERITY_TABLE_OPT_LOGGING "ignore_corruption"
+#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks"
+
+#define VERITY_TABLE_OPT_FEC_FORMAT \
+ "use_fec_from_device %s fec_start %" PRIu64 " fec_blocks %" PRIu64 \
+ " fec_roots %u " VERITY_TABLE_OPT_IGNZERO
+#define VERITY_TABLE_OPT_FEC_ARGS 9
+
#define METADATA_MAGIC 0x01564c54
#define METADATA_TAG_MAX_LENGTH 63
#define METADATA_EOD "eod"
@@ -92,7 +99,7 @@
}
if (!fread(key, sizeof(*key), 1, f)) {
- ERROR("Could not read key!");
+ ERROR("Could not read key!\n");
fclose(f);
free(key);
return NULL;
@@ -109,7 +116,8 @@
return key;
}
-static int verify_table(char *signature, char *table, int table_length)
+static int verify_table(const uint8_t *signature, const char *table,
+ uint32_t table_length)
{
RSAPublicKey *key;
uint8_t hash_buf[SHA256_DIGEST_SIZE];
@@ -121,17 +129,17 @@
// Now get the public key from the keyfile
key = load_key(VERITY_TABLE_RSA_KEY);
if (!key) {
- ERROR("Couldn't load verity keys");
+ ERROR("Couldn't load verity keys\n");
goto out;
}
// verify the result
if (!RSA_verify(key,
- (uint8_t*) signature,
+ signature,
RSANUMBYTES,
(uint8_t*) hash_buf,
SHA256_DIGEST_SIZE)) {
- ERROR("Couldn't verify table.");
+ ERROR("Couldn't verify table\n");
goto out;
}
@@ -142,11 +150,11 @@
return retval;
}
-static int invalidate_table(char *table, int table_length)
+static int invalidate_table(char *table, size_t table_length)
{
- int n = 0;
- int idx = 0;
- int cleared = 0;
+ size_t n = 0;
+ size_t idx = 0;
+ size_t cleared = 0;
while (n < table_length) {
if (table[n++] == ' ') {
@@ -169,177 +177,6 @@
return -1;
}
-static int squashfs_get_target_device_size(char *blk_device, uint64_t *device_size)
-{
- struct squashfs_info sq_info;
-
- if (squashfs_parse_sb(blk_device, &sq_info) >= 0) {
- *device_size = sq_info.bytes_used_4K_padded;
- return 0;
- } else {
- return -1;
- }
-}
-
-static int ext4_get_target_device_size(char *blk_device, uint64_t *device_size)
-{
- int data_device;
- struct ext4_super_block sb;
- struct fs_info info;
-
- info.len = 0; /* Only len is set to 0 to ask the device for real size. */
-
- data_device = TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC));
- if (data_device == -1) {
- ERROR("Error opening block device (%s)", strerror(errno));
- return -1;
- }
-
- if (TEMP_FAILURE_RETRY(lseek64(data_device, 1024, SEEK_SET)) < 0) {
- ERROR("Error seeking to superblock");
- close(data_device);
- return -1;
- }
-
- if (!android::base::ReadFully(data_device, &sb, sizeof(sb))) {
- ERROR("Error reading superblock");
- close(data_device);
- return -1;
- }
-
- ext4_parse_sb(&sb, &info);
- *device_size = info.len;
-
- close(data_device);
- return 0;
-}
-
-static int get_fs_size(char *fs_type, char *blk_device, uint64_t *device_size) {
- if (!strcmp(fs_type, "ext4")) {
- if (ext4_get_target_device_size(blk_device, device_size) < 0) {
- ERROR("Failed to get ext4 fs size on %s.", blk_device);
- return -1;
- }
- } else if (!strcmp(fs_type, "squashfs")) {
- if (squashfs_get_target_device_size(blk_device, device_size) < 0) {
- ERROR("Failed to get squashfs fs size on %s.", blk_device);
- return -1;
- }
- } else {
- ERROR("%s: Unsupported filesystem for verity.", fs_type);
- return -1;
- }
- return 0;
-}
-
-static int read_verity_metadata(uint64_t device_size, char *block_device, char **signature,
- char **table)
-{
- unsigned magic_number;
- unsigned table_length;
- int protocol_version;
- int device;
- int retval = FS_MGR_SETUP_VERITY_FAIL;
-
- *signature = NULL;
-
- if (table) {
- *table = NULL;
- }
-
- device = TEMP_FAILURE_RETRY(open(block_device, O_RDONLY | O_CLOEXEC));
- if (device == -1) {
- ERROR("Could not open block device %s (%s).\n", block_device, strerror(errno));
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(lseek64(device, device_size, SEEK_SET)) < 0) {
- ERROR("Could not seek to start of verity metadata block.\n");
- goto out;
- }
- // check the magic number
- if (!android::base::ReadFully(device, &magic_number, sizeof(magic_number))) {
- ERROR("Couldn't read magic number!\n");
- goto out;
- }
-
-#ifdef ALLOW_ADBD_DISABLE_VERITY
- if (magic_number == VERITY_METADATA_MAGIC_DISABLE) {
- retval = FS_MGR_SETUP_VERITY_DISABLED;
- INFO("Attempt to cleanly disable verity - only works in USERDEBUG");
- goto out;
- }
-#endif
-
- if (magic_number != VERITY_METADATA_MAGIC_NUMBER) {
- ERROR("Couldn't find verity metadata at offset %" PRIu64 "!\n", device_size);
- goto out;
- }
-
- // check the protocol version
- if (!android::base::ReadFully(device, &protocol_version,
- sizeof(protocol_version))) {
- ERROR("Couldn't read verity metadata protocol version!\n");
- goto out;
- }
- if (protocol_version != 0) {
- ERROR("Got unknown verity metadata protocol version %d!\n", protocol_version);
- goto out;
- }
-
- // get the signature
- *signature = (char*) malloc(RSANUMBYTES);
- if (!*signature) {
- ERROR("Couldn't allocate memory for signature!\n");
- goto out;
- }
- if (!android::base::ReadFully(device, *signature, RSANUMBYTES)) {
- ERROR("Couldn't read signature from verity metadata!\n");
- goto out;
- }
-
- if (!table) {
- retval = FS_MGR_SETUP_VERITY_SUCCESS;
- goto out;
- }
-
- // get the size of the table
- if (!android::base::ReadFully(device, &table_length, sizeof(table_length))) {
- ERROR("Couldn't get the size of the verity table from metadata!\n");
- goto out;
- }
-
- // get the table + null terminator
- *table = static_cast<char*>(malloc(table_length + 1));
- if (!*table) {
- ERROR("Couldn't allocate memory for verity table!\n");
- goto out;
- }
- if (!android::base::ReadFully(device, *table, table_length)) {
- ERROR("Couldn't read the verity table from metadata!\n");
- goto out;
- }
-
- (*table)[table_length] = 0;
- retval = FS_MGR_SETUP_VERITY_SUCCESS;
-
-out:
- if (device != -1)
- close(device);
-
- if (retval != FS_MGR_SETUP_VERITY_SUCCESS) {
- free(*signature);
- *signature = NULL;
-
- if (table) {
- free(*table);
- *table = NULL;
- }
- }
-
- return retval;
-}
-
static void verity_ioctl_init(struct dm_ioctl *io, char *name, unsigned flags)
{
memset(io, 0, DM_BUF_SIZE);
@@ -379,8 +216,76 @@
return 0;
}
-static int load_verity_table(struct dm_ioctl *io, char *name, uint64_t device_size, int fd, char *table,
- int mode)
+struct verity_table_params {
+ const char *table;
+ int mode;
+ struct fec_ecc_metadata ecc;
+ const char *ecc_dev;
+};
+
+typedef bool (*format_verity_table_func)(char *buf, const size_t bufsize,
+ const struct verity_table_params *params);
+
+static bool format_verity_table(char *buf, const size_t bufsize,
+ const struct verity_table_params *params)
+{
+ const char *mode_flag = NULL;
+ int res = -1;
+
+ if (params->mode == VERITY_MODE_RESTART) {
+ mode_flag = VERITY_TABLE_OPT_RESTART;
+ } else if (params->mode == VERITY_MODE_LOGGING) {
+ mode_flag = VERITY_TABLE_OPT_LOGGING;
+ }
+
+ if (params->ecc.valid) {
+ if (mode_flag) {
+ res = snprintf(buf, bufsize,
+ "%s %u %s " VERITY_TABLE_OPT_FEC_FORMAT,
+ params->table, 1 + VERITY_TABLE_OPT_FEC_ARGS, mode_flag, params->ecc_dev,
+ params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots);
+ } else {
+ res = snprintf(buf, bufsize,
+ "%s %u " VERITY_TABLE_OPT_FEC_FORMAT,
+ params->table, VERITY_TABLE_OPT_FEC_ARGS, params->ecc_dev,
+ params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots);
+ }
+ } else if (mode_flag) {
+ res = snprintf(buf, bufsize, "%s 2 " VERITY_TABLE_OPT_IGNZERO " %s", params->table,
+ mode_flag);
+ } else {
+ res = strlcpy(buf, params->table, bufsize);
+ }
+
+ if (res < 0 || (size_t)res >= bufsize) {
+ ERROR("Error building verity table; insufficient buffer size?\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool format_legacy_verity_table(char *buf, const size_t bufsize,
+ const struct verity_table_params *params)
+{
+ int res;
+
+ if (params->mode == VERITY_MODE_EIO) {
+ res = strlcpy(buf, params->table, bufsize);
+ } else {
+ res = snprintf(buf, bufsize, "%s %d", params->table, params->mode);
+ }
+
+ if (res < 0 || (size_t)res >= bufsize) {
+ ERROR("Error building verity table; insufficient buffer size?\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int load_verity_table(struct dm_ioctl *io, char *name, uint64_t device_size, int fd,
+ const struct verity_table_params *params, format_verity_table_func format)
{
char *verity_params;
char *buffer = (char*) io;
@@ -390,35 +295,32 @@
struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
- // set tgt arguments here
+ // set tgt arguments
io->target_count = 1;
- tgt->status=0;
- tgt->sector_start=0;
- tgt->length=device_size/512;
+ tgt->status = 0;
+ tgt->sector_start = 0;
+ tgt->length = device_size / 512;
strcpy(tgt->target_type, "verity");
- // build the verity params here
+ // build the verity params
verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
bufsize = DM_BUF_SIZE - (verity_params - buffer);
- if (mode == VERITY_MODE_EIO) {
- // allow operation with older dm-verity drivers that are unaware
- // of the mode parameter by omitting it; this also means that we
- // cannot use logging mode with these drivers, they always cause
- // an I/O error for corrupted blocks
- strcpy(verity_params, table);
- } else if (snprintf(verity_params, bufsize, "%s %d", table, mode) < 0) {
+ if (!format(verity_params, bufsize, params)) {
+ ERROR("Failed to format verity parameters\n");
return -1;
}
+ INFO("loading verity table: '%s'", verity_params);
+
// set next target boundary
verity_params += strlen(verity_params) + 1;
- verity_params = (char*) (((unsigned long)verity_params + 7) & ~8);
+ verity_params = (char*)(((unsigned long)verity_params + 7) & ~8);
tgt->next = verity_params - buffer;
// send the ioctl to load the verity table
if (ioctl(fd, DM_TABLE_LOAD, io)) {
- ERROR("Error loading verity table (%s)", strerror(errno));
+ ERROR("Error loading verity table (%s)\n", strerror(errno));
return -1;
}
@@ -703,28 +605,31 @@
static int compare_last_signature(struct fstab_rec *fstab, int *match)
{
char tag[METADATA_TAG_MAX_LENGTH + 1];
- char *signature = NULL;
int fd = -1;
int rc = -1;
+ off64_t offset = 0;
+ struct fec_handle *f = NULL;
+ struct fec_verity_metadata verity;
uint8_t curr[SHA256_DIGEST_SIZE];
uint8_t prev[SHA256_DIGEST_SIZE];
- off64_t offset = 0;
- uint64_t device_size;
*match = 1;
- // get verity filesystem size
- if (get_fs_size(fstab->fs_type, fstab->blk_device, &device_size) < 0) {
- ERROR("Failed to get filesystem size\n");
+ if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
+ FEC_DEFAULT_ROOTS) == -1) {
+ ERROR("Failed to open '%s' (%s)\n", fstab->blk_device,
+ strerror(errno));
+ return rc;
+ }
+
+ // read verity metadata
+ if (fec_verity_get_metadata(f, &verity) == -1) {
+ ERROR("Failed to get verity metadata '%s' (%s)\n", fstab->blk_device,
+ strerror(errno));
goto out;
}
- if (read_verity_metadata(device_size, fstab->blk_device, &signature, NULL) < 0) {
- ERROR("Failed to read verity signature from %s\n", fstab->mount_point);
- goto out;
- }
-
- SHA256_hash(signature, RSANUMBYTES, curr);
+ SHA256_hash(verity.signature, RSANUMBYTES, curr);
if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s",
basename(fstab->mount_point)) >= (int)sizeof(tag)) {
@@ -766,12 +671,7 @@
rc = 0;
out:
- free(signature);
-
- if (fd != -1) {
- close(fd);
- }
-
+ fec_close(f);
return rc;
}
@@ -884,6 +784,7 @@
{
alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
bool use_state = true;
+ bool use_state_for_device = true;
char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
char *mount_point;
char propbuf[PROPERTY_VALUE_MAX];
@@ -928,10 +829,12 @@
continue;
}
+ use_state_for_device = use_state;
+
if (use_state) {
if (get_verity_state_offset(&fstab->recs[i], &offset) < 0 ||
read_verity_state(fstab->recs[i].verity_loc, offset, &mode) < 0) {
- continue;
+ use_state_for_device = false;
}
}
@@ -946,7 +849,7 @@
status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
- if (use_state && *status == 'C') {
+ if (use_state_for_device && *status == 'C') {
if (write_verity_state(fstab->recs[i].verity_loc, offset,
VERITY_MODE_LOGGING) < 0) {
continue;
@@ -972,87 +875,105 @@
return rc;
}
-int fs_mgr_setup_verity(struct fstab_rec *fstab) {
-
+int fs_mgr_setup_verity(struct fstab_rec *fstab)
+{
int retval = FS_MGR_SETUP_VERITY_FAIL;
int fd = -1;
- int mode;
-
- char *verity_blk_name = 0;
- char *verity_table = 0;
- char *verity_table_signature = 0;
- int verity_table_length = 0;
- uint64_t device_size = 0;
+ char *invalid_table = NULL;
+ char *verity_blk_name = NULL;
+ struct fec_handle *f = NULL;
+ struct fec_verity_metadata verity;
+ struct verity_table_params params;
alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
struct dm_ioctl *io = (struct dm_ioctl *) buffer;
char *mount_point = basename(fstab->mount_point);
- // get verity filesystem size
- if (get_fs_size(fstab->fs_type, fstab->blk_device, &device_size) < 0) {
+ if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
+ FEC_DEFAULT_ROOTS) < 0) {
+ ERROR("Failed to open '%s' (%s)\n", fstab->blk_device,
+ strerror(errno));
return retval;
}
- // read the verity block at the end of the block device
- // send error code up the chain so we can detect attempts to disable verity
- retval = read_verity_metadata(device_size,
- fstab->blk_device,
- &verity_table_signature,
- &verity_table);
- if (retval < 0) {
+ // read verity metadata
+ if (fec_verity_get_metadata(f, &verity) < 0) {
+ ERROR("Failed to get verity metadata '%s' (%s)\n", fstab->blk_device,
+ strerror(errno));
goto out;
}
- retval = FS_MGR_SETUP_VERITY_FAIL;
- verity_table_length = strlen(verity_table);
+#ifdef ALLOW_ADBD_DISABLE_VERITY
+ if (verity.disabled) {
+ retval = FS_MGR_SETUP_VERITY_DISABLED;
+ INFO("Attempt to cleanly disable verity - only works in USERDEBUG\n");
+ goto out;
+ }
+#endif
+
+ // read ecc metadata
+ if (fec_ecc_get_metadata(f, ¶ms.ecc) < 0) {
+ params.ecc.valid = false;
+ }
+
+ params.ecc_dev = fstab->blk_device;
// get the device mapper fd
if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
- ERROR("Error opening device mapper (%s)", strerror(errno));
+ ERROR("Error opening device mapper (%s)\n", strerror(errno));
goto out;
}
// create the device
if (create_verity_device(io, mount_point, fd) < 0) {
- ERROR("Couldn't create verity device!");
+ ERROR("Couldn't create verity device!\n");
goto out;
}
// get the name of the device file
if (get_verity_device_name(io, mount_point, fd, &verity_blk_name) < 0) {
- ERROR("Couldn't get verity device number!");
+ ERROR("Couldn't get verity device number!\n");
goto out;
}
- if (load_verity_state(fstab, &mode) < 0) {
+ if (load_verity_state(fstab, ¶ms.mode) < 0) {
/* if accessing or updating the state failed, switch to the default
* safe mode. This makes sure the device won't end up in an endless
* restart loop, and no corrupted data will be exposed to userspace
* without a warning. */
- mode = VERITY_MODE_EIO;
+ params.mode = VERITY_MODE_EIO;
}
// verify the signature on the table
- if (verify_table(verity_table_signature,
- verity_table,
- verity_table_length) < 0) {
- if (mode == VERITY_MODE_LOGGING) {
+ if (verify_table(verity.signature, verity.table,
+ verity.table_length) < 0) {
+ if (params.mode == VERITY_MODE_LOGGING) {
// the user has been warned, allow mounting without dm-verity
retval = FS_MGR_SETUP_VERITY_SUCCESS;
goto out;
}
// invalidate root hash and salt to trigger device-specific recovery
- if (invalidate_table(verity_table, verity_table_length) < 0) {
+ invalid_table = strdup(verity.table);
+
+ if (!invalid_table ||
+ invalidate_table(invalid_table, verity.table_length) < 0) {
goto out;
}
+
+ params.table = invalid_table;
+ } else {
+ params.table = verity.table;
}
- INFO("Enabling dm-verity for %s (mode %d)\n", mount_point, mode);
+ INFO("Enabling dm-verity for %s (mode %d)\n", mount_point, params.mode);
// load the verity mapping table
- if (load_verity_table(io, mount_point, device_size, fd, verity_table,
- mode) < 0) {
+ if (load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
+ format_verity_table) < 0 &&
+ // try the legacy format for backwards compatibility
+ load_verity_table(io, mount_point, verity.data_size, fd, ¶ms,
+ format_legacy_verity_table) < 0) {
goto out;
}
@@ -1081,8 +1002,8 @@
close(fd);
}
- free(verity_table);
- free(verity_table_signature);
+ fec_close(f);
+ free(invalid_table);
free(verity_blk_name);
return retval;
diff --git a/include/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/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
new file mode 100644
index 0000000..9ee4abe
--- /dev/null
+++ b/include/ziparchive/zip_writer.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBZIPARCHIVE_ZIPWRITER_H_
+#define LIBZIPARCHIVE_ZIPWRITER_H_
+
+#include "base/macros.h"
+#include <utils/Compat.h>
+
+#include <cstdio>
+#include <string>
+#include <ctime>
+#include <vector>
+
+/**
+ * Writes a Zip file via a stateful interface.
+ *
+ * Example:
+ *
+ * FILE* file = fopen("path/to/zip.zip", "wb");
+ *
+ * ZipWriter writer(file);
+ *
+ * writer.StartEntry("test.txt", ZipWriter::kCompress | ZipWriter::kAlign);
+ * writer.WriteBytes(buffer, bufferLen);
+ * writer.WriteBytes(buffer2, bufferLen2);
+ * writer.FinishEntry();
+ *
+ * writer.StartEntry("empty.txt", 0);
+ * writer.FinishEntry();
+ *
+ * writer.Finish();
+ *
+ * fclose(file);
+ */
+class ZipWriter {
+public:
+ enum {
+ /**
+ * Flag to compress the zip entry using deflate.
+ */
+ kCompress = 0x01,
+
+ /**
+ * Flag to align the zip entry data on a 32bit boundary. Useful for
+ * mmapping the data at runtime.
+ */
+ kAlign32 = 0x02,
+ };
+
+ static const char* ErrorCodeString(int32_t error_code);
+
+ /**
+ * Create a ZipWriter that will write into a FILE stream. The file should be opened with
+ * open mode of "wb" or "w+b". ZipWriter does not take ownership of the file stream. The
+ * caller is responsible for closing the file.
+ */
+ explicit ZipWriter(FILE* f);
+
+ // Move constructor.
+ ZipWriter(ZipWriter&& zipWriter);
+
+ // Move assignment.
+ ZipWriter& operator=(ZipWriter&& zipWriter);
+
+ /**
+ * Starts a new zip entry with the given path and flags.
+ * Flags can be a bitwise OR of ZipWriter::kCompress and ZipWriter::kAlign.
+ * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t StartEntry(const char* path, size_t flags);
+
+ /**
+ * Same as StartEntry(const char*, size_t), but sets a last modified time for the entry.
+ */
+ int32_t StartEntryWithTime(const char* path, size_t flags, time_t time);
+
+ /**
+ * Writes bytes to the zip file for the previously started zip entry.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t WriteBytes(const void* data, size_t len);
+
+ /**
+ * Finish a zip entry started with StartEntry(const char*, size_t) or
+ * StartEntryWithTime(const char*, size_t, time_t). This must be called before
+ * any new zip entries are started, or before Finish() is called.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t FinishEntry();
+
+ /**
+ * Writes the Central Directory Headers and flushes the zip file stream.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t Finish();
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ZipWriter);
+
+ int32_t HandleError(int32_t error_code);
+
+ struct FileInfo {
+ std::string path;
+ uint16_t compression_method;
+ uint32_t crc32;
+ uint32_t compressed_size;
+ uint32_t uncompressed_size;
+ uint16_t last_mod_time;
+ uint16_t last_mod_date;
+ uint32_t local_file_header_offset;
+ };
+
+ enum class State {
+ kWritingZip,
+ kWritingEntry,
+ kDone,
+ kError,
+ };
+
+ FILE* file_;
+ off64_t current_offset_;
+ State state_;
+ std::vector<FileInfo> files_;
+};
+
+#endif /* LIBZIPARCHIVE_ZIPWRITER_H_ */
diff --git a/init/Android.mk b/init/Android.mk
index c1cdc1a..8e45a7a 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -84,6 +84,8 @@
LOCAL_STATIC_LIBRARIES := \
libinit \
libfs_mgr \
+ libfec \
+ libfec_rs \
libsquashfs_utils \
liblogwrap \
libcutils \
@@ -94,6 +96,7 @@
libc \
libselinux \
libmincrypt \
+ libcrypto_static \
libc++_static \
libdl \
libsparse_static \
diff --git a/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/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/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/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/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