adb: add interfaces for Encoder/Decoder.

More groundwork to support more compression algorithms.

Bug: https://issuetracker.google.com/150827486
Test: python3 -m unittest test_device.FileOperationsTest{Uncompressed,Brotli}
Change-Id: I638493083b83e3f6c6854b631471e9d6b50bd79f
This commit is contained in:
Josh Gao 2020-03-26 19:33:25 -07:00
parent 73d44bb6c2
commit 49ba558aa8
10 changed files with 870 additions and 707 deletions

View file

@ -290,7 +290,7 @@ static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy)
}
}
if (do_sync_push(apk_file, apk_dest.c_str(), false, true)) {
if (do_sync_push(apk_file, apk_dest.c_str(), false, CompressionType::Any)) {
result = pm_command(argc, argv);
delete_device_file(apk_dest);
}

View file

@ -282,5 +282,5 @@ int Bugreport::SendShellCommand(const std::string& command, bool disable_shell_p
bool Bugreport::DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
const char* name) {
return do_sync_pull(srcs, dst, copy_attrs, false, name);
return do_sync_pull(srcs, dst, copy_attrs, CompressionType::None, name);
}

View file

@ -129,20 +129,20 @@ static void help() {
" reverse --remove-all remove all reverse socket connections from device\n"
"\n"
"file transfer:\n"
" push [--sync] [-zZ] LOCAL... REMOTE\n"
" push [--sync] [-z ALGORITHM] [-Z] LOCAL... REMOTE\n"
" copy local files/directories to device\n"
" --sync: only push files that are newer on the host than the device\n"
" -z: enable compression\n"
" -z: enable compression with a specified algorithm (any, none, brotli)\n"
" -Z: disable compression\n"
" pull [-azZ] REMOTE... LOCAL\n"
" pull [-a] [-z ALGORITHM] [-Z] REMOTE... LOCAL\n"
" copy files/dirs from device\n"
" -a: preserve file timestamp and mode\n"
" -z: enable compression\n"
" -z: enable compression with a specified algorithm (any, none, brotli)\n"
" -Z: disable compression\n"
" sync [-lzZ] [all|data|odm|oem|product|system|system_ext|vendor]\n"
" sync [-l] [-z ALGORITHM] [-Z] [all|data|odm|oem|product|system|system_ext|vendor]\n"
" sync a local build from $ANDROID_PRODUCT_OUT to the device (default all)\n"
" -l: list files that would be copied, but don't copy them\n"
" -z: enable compression\n"
" -z: enable compression with a specified algorithm (any, none, brotli)\n"
" -Z: disable compression\n"
"\n"
"shell:\n"
@ -1314,12 +1314,34 @@ static int restore(int argc, const char** argv) {
return 0;
}
static CompressionType parse_compression_type(const std::string& str, bool allow_numbers) {
if (allow_numbers) {
if (str == "0") {
return CompressionType::None;
} else if (str == "1") {
return CompressionType::Any;
}
}
if (str == "any") {
return CompressionType::Any;
} else if (str == "none") {
return CompressionType::None;
}
if (str == "brotli") {
return CompressionType::Brotli;
}
error_exit("unexpected compression type %s", str.c_str());
}
static void parse_push_pull_args(const char** arg, int narg, std::vector<const char*>* srcs,
const char** dst, bool* copy_attrs, bool* sync, bool* compressed) {
const char** dst, bool* copy_attrs, bool* sync,
CompressionType* compression) {
*copy_attrs = false;
const char* adb_compression = getenv("ADB_COMPRESSION");
if (adb_compression && strcmp(adb_compression, "0") == 0) {
*compressed = false;
if (const char* adb_compression = getenv("ADB_COMPRESSION")) {
*compression = parse_compression_type(adb_compression, true);
}
srcs->clear();
@ -1333,13 +1355,13 @@ static void parse_push_pull_args(const char** arg, int narg, std::vector<const c
} else if (!strcmp(*arg, "-a")) {
*copy_attrs = true;
} else if (!strcmp(*arg, "-z")) {
if (compressed != nullptr) {
*compressed = true;
if (narg < 2) {
error_exit("-z requires an argument");
}
*compression = parse_compression_type(*++arg, false);
--narg;
} else if (!strcmp(*arg, "-Z")) {
if (compressed != nullptr) {
*compressed = false;
}
*compression = CompressionType::None;
} else if (!strcmp(*arg, "--sync")) {
if (sync != nullptr) {
*sync = true;
@ -1894,22 +1916,22 @@ int adb_commandline(int argc, const char** argv) {
} else if (!strcmp(argv[0], "push")) {
bool copy_attrs = false;
bool sync = false;
bool compressed = true;
CompressionType compression = CompressionType::Any;
std::vector<const char*> srcs;
const char* dst = nullptr;
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, &sync, &compressed);
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, &sync, &compression);
if (srcs.empty() || !dst) error_exit("push requires an argument");
return do_sync_push(srcs, dst, sync, compressed) ? 0 : 1;
return do_sync_push(srcs, dst, sync, compression) ? 0 : 1;
} else if (!strcmp(argv[0], "pull")) {
bool copy_attrs = false;
bool compressed = true;
CompressionType compression = CompressionType::Any;
std::vector<const char*> srcs;
const char* dst = ".";
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, nullptr, &compressed);
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, nullptr, &compression);
if (srcs.empty()) error_exit("pull requires an argument");
return do_sync_pull(srcs, dst, copy_attrs, compressed) ? 0 : 1;
return do_sync_pull(srcs, dst, copy_attrs, compression) ? 0 : 1;
} else if (!strcmp(argv[0], "install")) {
if (argc < 2) error_exit("install requires an argument");
return install_app(argc, argv);
@ -1925,27 +1947,26 @@ int adb_commandline(int argc, const char** argv) {
} else if (!strcmp(argv[0], "sync")) {
std::string src;
bool list_only = false;
bool compressed = true;
CompressionType compression = CompressionType::Any;
const char* adb_compression = getenv("ADB_COMPRESSION");
if (adb_compression && strcmp(adb_compression, "0") == 0) {
compressed = false;
if (const char* adb_compression = getenv("ADB_COMPRESSION"); adb_compression) {
compression = parse_compression_type(adb_compression, true);
}
int opt;
while ((opt = getopt(argc, const_cast<char**>(argv), "lzZ")) != -1) {
while ((opt = getopt(argc, const_cast<char**>(argv), "lz:Z")) != -1) {
switch (opt) {
case 'l':
list_only = true;
break;
case 'z':
compressed = true;
compression = parse_compression_type(optarg, false);
break;
case 'Z':
compressed = false;
compression = CompressionType::None;
break;
default:
error_exit("usage: adb sync [-lzZ] [PARTITION]");
error_exit("usage: adb sync [-l] [-z ALGORITHM] [-Z] [PARTITION]");
}
}
@ -1954,7 +1975,7 @@ int adb_commandline(int argc, const char** argv) {
} else if (optind + 1 == argc) {
src = argv[optind];
} else {
error_exit("usage: adb sync [-lzZ] [PARTITION]");
error_exit("usage: adb sync [-l] [-z ALGORITHM] [-Z] [PARTITION]");
}
std::vector<std::string> partitions{"data", "odm", "oem", "product",
@ -1965,7 +1986,7 @@ int adb_commandline(int argc, const char** argv) {
std::string src_dir{product_file(partition)};
if (!directory_exists(src_dir)) continue;
found = true;
if (!do_sync_sync(src_dir, "/" + partition, list_only, compressed)) return 1;
if (!do_sync_sync(src_dir, "/" + partition, list_only, compression)) return 1;
}
}
if (!found) error_exit("don't know how to sync %s partition", src.c_str());

View file

@ -112,7 +112,7 @@ static void push_to_device(const void* data, size_t byte_count, const char* dst,
// but can't be removed until after the push.
unix_close(tf.release());
if (!do_sync_push(srcs, dst, sync, true)) {
if (!do_sync_push(srcs, dst, sync, CompressionType::Any)) {
error_exit("Failed to push fastdeploy agent to device.");
}
}

View file

@ -34,6 +34,7 @@
#include <memory>
#include <sstream>
#include <string>
#include <variant>
#include <vector>
#include "sysdeps.h"
@ -262,6 +263,18 @@ class SyncConnection {
bool HaveSendRecv2() const { return have_sendrecv_v2_; }
bool HaveSendRecv2Brotli() const { return have_sendrecv_v2_brotli_; }
// Resolve a compression type which might be CompressionType::Any to a specific compression
// algorithm.
CompressionType ResolveCompressionType(CompressionType compression) const {
if (compression == CompressionType::Any) {
if (HaveSendRecv2Brotli()) {
return CompressionType::Brotli;
}
return CompressionType::None;
}
return compression;
}
const FeatureSet& Features() const { return features_; }
bool IsValid() { return fd >= 0; }
@ -323,7 +336,7 @@ class SyncConnection {
return WriteFdExactly(fd, buf.data(), buf.size());
}
bool SendSend2(std::string_view path, mode_t mode, bool compressed) {
bool SendSend2(std::string_view path, mode_t mode, CompressionType compression) {
if (path.length() > 1024) {
Error("SendRequest failed: path too long: %zu", path.length());
errno = ENAMETOOLONG;
@ -339,7 +352,18 @@ class SyncConnection {
syncmsg msg;
msg.send_v2_setup.id = ID_SEND_V2;
msg.send_v2_setup.mode = mode;
msg.send_v2_setup.flags = compressed ? kSyncFlagBrotli : kSyncFlagNone;
msg.send_v2_setup.flags = 0;
switch (compression) {
case CompressionType::None:
break;
case CompressionType::Brotli:
msg.send_v2_setup.flags = kSyncFlagBrotli;
break;
case CompressionType::Any:
LOG(FATAL) << "unexpected CompressionType::Any";
}
buf.resize(sizeof(SyncRequest) + path.length() + sizeof(msg.send_v2_setup));
@ -352,7 +376,7 @@ class SyncConnection {
return WriteFdExactly(fd, buf.data(), buf.size());
}
bool SendRecv2(const std::string& path) {
bool SendRecv2(const std::string& path, CompressionType compression) {
if (path.length() > 1024) {
Error("SendRequest failed: path too long: %zu", path.length());
errno = ENAMETOOLONG;
@ -367,7 +391,18 @@ class SyncConnection {
syncmsg msg;
msg.recv_v2_setup.id = ID_RECV_V2;
msg.recv_v2_setup.flags = kSyncFlagBrotli;
msg.recv_v2_setup.flags = 0;
switch (compression) {
case CompressionType::None:
break;
case CompressionType::Brotli:
msg.recv_v2_setup.flags |= kSyncFlagBrotli;
break;
case CompressionType::Any:
LOG(FATAL) << "unexpected CompressionType::Any";
}
buf.resize(sizeof(SyncRequest) + path.length() + sizeof(msg.recv_v2_setup));
@ -533,9 +568,15 @@ class SyncConnection {
return true;
}
bool SendLargeFileCompressed(const std::string& path, mode_t mode, const std::string& lpath,
const std::string& rpath, unsigned mtime) {
if (!SendSend2(path, mode, true)) {
bool SendLargeFile(const std::string& path, mode_t mode, const std::string& lpath,
const std::string& rpath, unsigned mtime, CompressionType compression) {
if (!HaveSendRecv2()) {
return SendLargeFileLegacy(path, mode, lpath, rpath, mtime);
}
compression = ResolveCompressionType(compression);
if (!SendSend2(path, mode, compression)) {
Error("failed to send ID_SEND_V2 message '%s': %s", path.c_str(), strerror(errno));
return false;
}
@ -558,7 +599,21 @@ class SyncConnection {
syncsendbuf sbuf;
sbuf.id = ID_DATA;
BrotliEncoder<SYNC_DATA_MAX> encoder;
std::variant<std::monostate, NullEncoder, BrotliEncoder> encoder_storage;
Encoder* encoder = nullptr;
switch (compression) {
case CompressionType::None:
encoder = &encoder_storage.emplace<NullEncoder>(SYNC_DATA_MAX);
break;
case CompressionType::Brotli:
encoder = &encoder_storage.emplace<BrotliEncoder>(SYNC_DATA_MAX);
break;
case CompressionType::Any:
LOG(FATAL) << "unexpected CompressionType::Any";
}
bool sending = true;
while (sending) {
Block input(SYNC_DATA_MAX);
@ -569,10 +624,10 @@ class SyncConnection {
}
if (r == 0) {
encoder.Finish();
encoder->Finish();
} else {
input.resize(r);
encoder.Append(std::move(input));
encoder->Append(std::move(input));
RecordBytesTransferred(r);
bytes_copied += r;
ReportProgress(rpath, bytes_copied, total_size);
@ -580,7 +635,7 @@ class SyncConnection {
while (true) {
Block output;
EncodeResult result = encoder.Encode(&output);
EncodeResult result = encoder->Encode(&output);
if (result == EncodeResult::Error) {
Error("compressing '%s' locally failed", lpath.c_str());
return false;
@ -610,12 +665,8 @@ class SyncConnection {
return WriteOrDie(lpath, rpath, &msg.data, sizeof(msg.data));
}
bool SendLargeFile(const std::string& path, mode_t mode, const std::string& lpath,
const std::string& rpath, unsigned mtime, bool compressed) {
if (compressed && HaveSendRecv2Brotli()) {
return SendLargeFileCompressed(path, mode, lpath, rpath, mtime);
}
bool SendLargeFileLegacy(const std::string& path, mode_t mode, const std::string& lpath,
const std::string& rpath, unsigned mtime) {
std::string path_and_mode = android::base::StringPrintf("%s,%d", path.c_str(), mode);
if (!SendRequest(ID_SEND_V1, path_and_mode)) {
Error("failed to send ID_SEND_V1 message '%s': %s", path_and_mode.c_str(),
@ -921,7 +972,7 @@ static bool sync_stat_fallback(SyncConnection& sc, const std::string& path, stru
}
static bool sync_send(SyncConnection& sc, const std::string& lpath, const std::string& rpath,
unsigned mtime, mode_t mode, bool sync, bool compressed) {
unsigned mtime, mode_t mode, bool sync, CompressionType compression) {
if (sync) {
struct stat st;
if (sync_lstat(sc, rpath, &st)) {
@ -964,7 +1015,7 @@ static bool sync_send(SyncConnection& sc, const std::string& lpath, const std::s
return false;
}
} else {
if (!sc.SendLargeFile(rpath, mode, lpath, rpath, mtime, compressed)) {
if (!sc.SendLargeFile(rpath, mode, lpath, rpath, mtime, compression)) {
return false;
}
}
@ -1027,8 +1078,10 @@ static bool sync_recv_v1(SyncConnection& sc, const char* rpath, const char* lpat
}
static bool sync_recv_v2(SyncConnection& sc, const char* rpath, const char* lpath, const char* name,
uint64_t expected_size) {
if (!sc.SendRecv2(rpath)) return false;
uint64_t expected_size, CompressionType compression) {
compression = sc.ResolveCompressionType(compression);
if (!sc.SendRecv2(rpath, compression)) return false;
adb_unlink(lpath);
unique_fd lfd(adb_creat(lpath, 0644));
@ -1040,9 +1093,24 @@ static bool sync_recv_v2(SyncConnection& sc, const char* rpath, const char* lpat
uint64_t bytes_copied = 0;
Block buffer(SYNC_DATA_MAX);
BrotliDecoder decoder(std::span(buffer.data(), buffer.size()));
bool reading = true;
while (reading) {
std::variant<std::monostate, NullDecoder, BrotliDecoder> decoder_storage;
Decoder* decoder = nullptr;
std::span buffer_span(buffer.data(), buffer.size());
switch (compression) {
case CompressionType::None:
decoder = &decoder_storage.emplace<NullDecoder>(buffer_span);
break;
case CompressionType::Brotli:
decoder = &decoder_storage.emplace<BrotliDecoder>(buffer_span);
break;
case CompressionType::Any:
LOG(FATAL) << "unexpected CompressionType::Any";
}
while (true) {
syncmsg msg;
if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
adb_unlink(lpath);
@ -1050,33 +1118,32 @@ static bool sync_recv_v2(SyncConnection& sc, const char* rpath, const char* lpat
}
if (msg.data.id == ID_DONE) {
adb_unlink(lpath);
sc.Error("unexpected ID_DONE");
return false;
}
if (msg.data.id != ID_DATA) {
if (!decoder->Finish()) {
sc.Error("unexpected ID_DONE");
return false;
}
} else if (msg.data.id != ID_DATA) {
adb_unlink(lpath);
sc.ReportCopyFailure(rpath, lpath, msg);
return false;
}
} else {
if (msg.data.size > sc.max) {
sc.Error("msg.data.size too large: %u (max %zu)", msg.data.size, sc.max);
adb_unlink(lpath);
return false;
}
if (msg.data.size > sc.max) {
sc.Error("msg.data.size too large: %u (max %zu)", msg.data.size, sc.max);
adb_unlink(lpath);
return false;
Block block(msg.data.size);
if (!ReadFdExactly(sc.fd, block.data(), msg.data.size)) {
adb_unlink(lpath);
return false;
}
decoder->Append(std::move(block));
}
Block block(msg.data.size);
if (!ReadFdExactly(sc.fd, block.data(), msg.data.size)) {
adb_unlink(lpath);
return false;
}
decoder.Append(std::move(block));
while (true) {
std::span<char> output;
DecodeResult result = decoder.Decode(&output);
DecodeResult result = decoder->Decode(&output);
if (result == DecodeResult::Error) {
sc.Error("decompress failed");
@ -1102,33 +1169,19 @@ static bool sync_recv_v2(SyncConnection& sc, const char* rpath, const char* lpat
} else if (result == DecodeResult::MoreOutput) {
continue;
} else if (result == DecodeResult::Done) {
reading = false;
break;
sc.RecordFilesTransferred(1);
return true;
} else {
LOG(FATAL) << "invalid DecodeResult: " << static_cast<int>(result);
}
}
}
syncmsg msg;
if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
sc.Error("failed to read ID_DONE");
return false;
}
if (msg.data.id != ID_DONE) {
sc.Error("unexpected message after transfer: id = %d (expected ID_DONE)", msg.data.id);
return false;
}
sc.RecordFilesTransferred(1);
return true;
}
static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, const char* name,
uint64_t expected_size, bool compressed) {
if (sc.HaveSendRecv2() && compressed) {
return sync_recv_v2(sc, rpath, lpath, name, expected_size);
uint64_t expected_size, CompressionType compression) {
if (sc.HaveSendRecv2()) {
return sync_recv_v2(sc, rpath, lpath, name, expected_size, compression);
} else {
return sync_recv_v1(sc, rpath, lpath, name, expected_size);
}
@ -1210,7 +1263,8 @@ static bool is_root_dir(std::string_view path) {
}
static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath, std::string rpath,
bool check_timestamps, bool list_only, bool compressed) {
bool check_timestamps, bool list_only,
CompressionType compression) {
sc.NewTransfer();
// Make sure that both directory paths end in a slash.
@ -1292,7 +1346,7 @@ static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath, std::st
if (list_only) {
sc.Println("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
} else {
if (!sync_send(sc, ci.lpath, ci.rpath, ci.time, ci.mode, false, compressed)) {
if (!sync_send(sc, ci.lpath, ci.rpath, ci.time, ci.mode, false, compression)) {
return false;
}
}
@ -1308,7 +1362,7 @@ static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath, std::st
}
bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync,
bool compressed) {
CompressionType compression) {
SyncConnection sc;
if (!sc.IsValid()) return false;
@ -1373,7 +1427,7 @@ bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sy
dst_dir.append(android::base::Basename(src_path));
}
success &= copy_local_dir_remote(sc, src_path, dst_dir, sync, false, compressed);
success &= copy_local_dir_remote(sc, src_path, dst_dir, sync, false, compression);
continue;
} else if (!should_push_file(st.st_mode)) {
sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
@ -1394,7 +1448,7 @@ bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sy
sc.NewTransfer();
sc.SetExpectedTotalBytes(st.st_size);
success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode, sync, compressed);
success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode, sync, compression);
sc.ReportTransferRate(src_path, TransferDirection::push);
}
@ -1480,7 +1534,7 @@ static int set_time_and_mode(const std::string& lpath, time_t time,
}
static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath, std::string lpath,
bool copy_attrs, bool compressed) {
bool copy_attrs, CompressionType compression) {
sc.NewTransfer();
// Make sure that both directory paths end in a slash.
@ -1510,7 +1564,7 @@ static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath, std::st
continue;
}
if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str(), nullptr, ci.size, compressed)) {
if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str(), nullptr, ci.size, compression)) {
return false;
}
@ -1528,7 +1582,7 @@ static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath, std::st
}
bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
bool compressed, const char* name) {
CompressionType compression, const char* name) {
SyncConnection sc;
if (!sc.IsValid()) return false;
@ -1602,7 +1656,7 @@ bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool co
dst_dir.append(android::base::Basename(src_path));
}
success &= copy_remote_dir_local(sc, src_path, dst_dir, copy_attrs, compressed);
success &= copy_remote_dir_local(sc, src_path, dst_dir, copy_attrs, compression);
continue;
} else if (!should_pull_file(src_st.st_mode)) {
sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, src_st.st_mode);
@ -1621,7 +1675,7 @@ bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool co
sc.NewTransfer();
sc.SetExpectedTotalBytes(src_st.st_size);
if (!sync_recv(sc, src_path, dst_path, name, src_st.st_size, compressed)) {
if (!sync_recv(sc, src_path, dst_path, name, src_st.st_size, compression)) {
success = false;
continue;
}
@ -1638,11 +1692,11 @@ bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool co
}
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only,
bool compressed) {
CompressionType compression) {
SyncConnection sc;
if (!sc.IsValid()) return false;
bool success = copy_local_dir_remote(sc, lpath, rpath, true, list_only, compressed);
bool success = copy_local_dir_remote(sc, lpath, rpath, true, list_only, compression);
if (!list_only) {
sc.ReportOverallTransferRate(TransferDirection::push);
}

View file

@ -19,11 +19,13 @@
#include <string>
#include <vector>
#include "file_sync_protocol.h"
bool do_sync_ls(const char* path);
bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync,
bool compressed);
CompressionType compression);
bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
bool compressed, const char* name = nullptr);
CompressionType compression, const char* name = nullptr);
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only,
bool compressed);
CompressionType compression);

View file

@ -16,8 +16,12 @@
#pragma once
#include <algorithm>
#include <memory>
#include <span>
#include <android-base/logging.h>
#include <brotli/decode.h>
#include <brotli/encode.h>
@ -37,15 +41,103 @@ enum class EncodeResult {
MoreOutput,
};
struct BrotliDecoder {
struct Decoder {
void Append(Block&& block) { input_buffer_.append(std::move(block)); }
bool Finish() {
bool old = std::exchange(finished_, true);
if (old) {
LOG(FATAL) << "Decoder::Finish called while already finished?";
return false;
}
return true;
}
virtual DecodeResult Decode(std::span<char>* output) = 0;
protected:
Decoder(std::span<char> output_buffer) : output_buffer_(output_buffer) {}
~Decoder() = default;
bool finished_ = false;
IOVector input_buffer_;
std::span<char> output_buffer_;
};
struct Encoder {
void Append(Block input) { input_buffer_.append(std::move(input)); }
bool Finish() {
bool old = std::exchange(finished_, true);
if (old) {
LOG(FATAL) << "Decoder::Finish called while already finished?";
return false;
}
return true;
}
virtual EncodeResult Encode(Block* output) = 0;
protected:
explicit Encoder(size_t output_block_size) : output_block_size_(output_block_size) {}
~Encoder() = default;
const size_t output_block_size_;
bool finished_ = false;
IOVector input_buffer_;
};
struct NullDecoder final : public Decoder {
explicit NullDecoder(std::span<char> output_buffer) : Decoder(output_buffer) {}
DecodeResult Decode(std::span<char>* output) final {
size_t available_out = output_buffer_.size();
void* p = output_buffer_.data();
while (available_out > 0 && !input_buffer_.empty()) {
size_t len = std::min(available_out, input_buffer_.front_size());
p = mempcpy(p, input_buffer_.front_data(), len);
available_out -= len;
input_buffer_.drop_front(len);
}
*output = std::span(output_buffer_.data(), static_cast<char*>(p));
if (input_buffer_.empty()) {
return finished_ ? DecodeResult::Done : DecodeResult::NeedInput;
}
return DecodeResult::MoreOutput;
}
};
struct NullEncoder final : public Encoder {
explicit NullEncoder(size_t output_block_size) : Encoder(output_block_size) {}
EncodeResult Encode(Block* output) final {
output->clear();
output->resize(output_block_size_);
size_t available_out = output->size();
void* p = output->data();
while (available_out > 0 && !input_buffer_.empty()) {
size_t len = std::min(available_out, input_buffer_.front_size());
p = mempcpy(p, input_buffer_.front_data(), len);
available_out -= len;
input_buffer_.drop_front(len);
}
output->resize(output->size() - available_out);
if (input_buffer_.empty()) {
return finished_ ? EncodeResult::Done : EncodeResult::NeedInput;
}
return EncodeResult::MoreOutput;
}
};
struct BrotliDecoder final : public Decoder {
explicit BrotliDecoder(std::span<char> output_buffer)
: output_buffer_(output_buffer),
: Decoder(output_buffer),
decoder_(BrotliDecoderCreateInstance(nullptr, nullptr, nullptr),
BrotliDecoderDestroyInstance) {}
void Append(Block&& block) { input_buffer_.append(std::move(block)); }
DecodeResult Decode(std::span<char>* output) {
DecodeResult Decode(std::span<char>* output) final {
size_t available_in = input_buffer_.front_size();
const uint8_t* next_in = reinterpret_cast<const uint8_t*>(input_buffer_.front_data());
@ -63,7 +155,8 @@ struct BrotliDecoder {
switch (r) {
case BROTLI_DECODER_RESULT_SUCCESS:
return DecodeResult::Done;
// We need to wait for ID_DONE from the other end.
return finished_ ? DecodeResult::Done : DecodeResult::NeedInput;
case BROTLI_DECODER_RESULT_ERROR:
return DecodeResult::Error;
case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
@ -77,33 +170,29 @@ struct BrotliDecoder {
}
private:
IOVector input_buffer_;
std::span<char> output_buffer_;
std::unique_ptr<BrotliDecoderState, void (*)(BrotliDecoderState*)> decoder_;
};
template <size_t OutputBlockSize>
struct BrotliEncoder {
explicit BrotliEncoder()
: output_block_(OutputBlockSize),
output_bytes_left_(OutputBlockSize),
struct BrotliEncoder final : public Encoder {
explicit BrotliEncoder(size_t output_block_size)
: Encoder(output_block_size),
output_block_(output_block_size_),
output_bytes_left_(output_block_size_),
encoder_(BrotliEncoderCreateInstance(nullptr, nullptr, nullptr),
BrotliEncoderDestroyInstance) {
BrotliEncoderSetParameter(encoder_.get(), BROTLI_PARAM_QUALITY, 1);
}
void Append(Block input) { input_buffer_.append(std::move(input)); }
void Finish() { finished_ = true; }
EncodeResult Encode(Block* output) {
EncodeResult Encode(Block* output) final {
output->clear();
while (true) {
size_t available_in = input_buffer_.front_size();
const uint8_t* next_in = reinterpret_cast<const uint8_t*>(input_buffer_.front_data());
size_t available_out = output_bytes_left_;
uint8_t* next_out = reinterpret_cast<uint8_t*>(output_block_.data() +
(OutputBlockSize - output_bytes_left_));
uint8_t* next_out = reinterpret_cast<uint8_t*>(
output_block_.data() + (output_block_size_ - output_bytes_left_));
BrotliEncoderOperation op = BROTLI_OPERATION_PROCESS;
if (finished_) {
@ -121,13 +210,13 @@ struct BrotliEncoder {
output_bytes_left_ = available_out;
if (BrotliEncoderIsFinished(encoder_.get())) {
output_block_.resize(OutputBlockSize - output_bytes_left_);
output_block_.resize(output_block_size_ - output_bytes_left_);
*output = std::move(output_block_);
return EncodeResult::Done;
} else if (output_bytes_left_ == 0) {
*output = std::move(output_block_);
output_block_.resize(OutputBlockSize);
output_bytes_left_ = OutputBlockSize;
output_block_.resize(output_block_size_);
output_bytes_left_ = output_block_size_;
return EncodeResult::MoreOutput;
} else if (input_buffer_.empty()) {
return EncodeResult::NeedInput;
@ -136,8 +225,6 @@ struct BrotliEncoder {
}
private:
bool finished_ = false;
IOVector input_buffer_;
Block output_block_;
size_t output_bytes_left_;
std::unique_ptr<BrotliEncoderState, void (*)(BrotliEncoderState*)> encoder_;

View file

@ -35,6 +35,7 @@
#include <optional>
#include <span>
#include <string>
#include <variant>
#include <vector>
#include <android-base/file.h>
@ -266,29 +267,45 @@ static bool SendSyncFailErrno(borrowed_fd fd, const std::string& reason) {
return SendSyncFail(fd, StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
}
static bool handle_send_file_compressed(borrowed_fd s, unique_fd fd, uint32_t* timestamp) {
static bool handle_send_file_data(borrowed_fd s, unique_fd fd, uint32_t* timestamp,
CompressionType compression) {
syncmsg msg;
Block decode_buffer(SYNC_DATA_MAX);
BrotliDecoder decoder(std::span(decode_buffer.data(), decode_buffer.size()));
Block buffer(SYNC_DATA_MAX);
std::span<char> buffer_span(buffer.data(), buffer.size());
std::variant<std::monostate, NullDecoder, BrotliDecoder> decoder_storage;
Decoder* decoder = nullptr;
switch (compression) {
case CompressionType::None:
decoder = &decoder_storage.emplace<NullDecoder>(buffer_span);
break;
case CompressionType::Brotli:
decoder = &decoder_storage.emplace<BrotliDecoder>(buffer_span);
break;
case CompressionType::Any:
LOG(FATAL) << "unexpected CompressionType::Any";
}
while (true) {
if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
if (msg.data.id != ID_DATA) {
if (msg.data.id == ID_DONE) {
*timestamp = msg.data.size;
return true;
}
if (msg.data.id == ID_DONE) {
*timestamp = msg.data.size;
decoder->Finish();
} else if (msg.data.id == ID_DATA) {
Block block(msg.data.size);
if (!ReadFdExactly(s, block.data(), msg.data.size)) return false;
decoder->Append(std::move(block));
} else {
SendSyncFail(s, "invalid data message");
return false;
}
Block block(msg.data.size);
if (!ReadFdExactly(s, block.data(), msg.data.size)) return false;
decoder.Append(std::move(block));
while (true) {
std::span<char> output;
DecodeResult result = decoder.Decode(&output);
DecodeResult result = decoder->Decode(&output);
if (result == DecodeResult::Error) {
SendSyncFailErrno(s, "decompress failed");
return false;
@ -304,7 +321,7 @@ static bool handle_send_file_compressed(borrowed_fd s, unique_fd fd, uint32_t* t
} else if (result == DecodeResult::MoreOutput) {
continue;
} else if (result == DecodeResult::Done) {
break;
return true;
} else {
LOG(FATAL) << "invalid DecodeResult: " << static_cast<int>(result);
}
@ -314,37 +331,10 @@ static bool handle_send_file_compressed(borrowed_fd s, unique_fd fd, uint32_t* t
__builtin_unreachable();
}
static bool handle_send_file_uncompressed(borrowed_fd s, unique_fd fd, uint32_t* timestamp,
std::vector<char>& buffer) {
syncmsg msg;
while (true) {
if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
if (msg.data.id != ID_DATA) {
if (msg.data.id == ID_DONE) {
*timestamp = msg.data.size;
return true;
}
SendSyncFail(s, "invalid data message");
return false;
}
if (msg.data.size > buffer.size()) { // TODO: resize buffer?
SendSyncFail(s, "oversize data message");
return false;
}
if (!ReadFdExactly(s, &buffer[0], msg.data.size)) return false;
if (!WriteFdExactly(fd, &buffer[0], msg.data.size)) {
SendSyncFailErrno(s, "write failed");
return false;
}
}
}
static bool handle_send_file(borrowed_fd s, const char* path, uint32_t* timestamp, uid_t uid,
gid_t gid, uint64_t capabilities, mode_t mode, bool compressed,
std::vector<char>& buffer, bool do_unlink) {
gid_t gid, uint64_t capabilities, mode_t mode,
CompressionType compression, std::vector<char>& buffer,
bool do_unlink) {
int rc;
syncmsg msg;
@ -389,14 +379,7 @@ static bool handle_send_file(borrowed_fd s, const char* path, uint32_t* timestam
D("[ Failed to fadvise: %s ]", strerror(rc));
}
bool result;
if (compressed) {
result = handle_send_file_compressed(s, std::move(fd), timestamp);
} else {
result = handle_send_file_uncompressed(s, std::move(fd), timestamp, buffer);
}
if (!result) {
if (!handle_send_file_data(s, std::move(fd), timestamp, compression)) {
goto fail;
}
@ -499,7 +482,7 @@ static bool handle_send_link(int s, const std::string& path, uint32_t* timestamp
}
#endif
static bool send_impl(int s, const std::string& path, mode_t mode, bool compressed,
static bool send_impl(int s, const std::string& path, mode_t mode, CompressionType compression,
std::vector<char>& buffer) {
// Don't delete files before copying if they are not "regular" or symlinks.
struct stat st;
@ -527,7 +510,7 @@ static bool send_impl(int s, const std::string& path, mode_t mode, bool compress
}
result = handle_send_file(s, path.c_str(), &timestamp, uid, gid, capabilities, mode,
compressed, buffer, do_unlink);
compression, buffer, do_unlink);
}
if (!result) {
@ -560,7 +543,7 @@ static bool do_send_v1(int s, const std::string& spec, std::vector<char>& buffer
return false;
}
return send_impl(s, path, mode, false, buffer);
return send_impl(s, path, mode, CompressionType::None, buffer);
}
static bool do_send_v2(int s, const std::string& path, std::vector<char>& buffer) {
@ -574,45 +557,60 @@ static bool do_send_v2(int s, const std::string& path, std::vector<char>& buffer
PLOG(ERROR) << "failed to read send_v2 setup packet";
}
bool compressed = false;
std::optional<CompressionType> compression;
if (msg.send_v2_setup.flags & kSyncFlagBrotli) {
msg.send_v2_setup.flags &= ~kSyncFlagBrotli;
compressed = true;
if (compression) {
SendSyncFail(s, android::base::StringPrintf("multiple compression flags received: %d",
msg.recv_v2_setup.flags));
return false;
}
compression = CompressionType::Brotli;
}
if (msg.send_v2_setup.flags) {
SendSyncFail(s, android::base::StringPrintf("unknown flags: %d", msg.send_v2_setup.flags));
return false;
}
errno = 0;
return send_impl(s, path, msg.send_v2_setup.mode, compressed, buffer);
return send_impl(s, path, msg.send_v2_setup.mode, compression.value_or(CompressionType::None),
buffer);
}
static bool recv_uncompressed(borrowed_fd s, unique_fd fd, std::vector<char>& buffer) {
syncmsg msg;
msg.data.id = ID_DATA;
while (true) {
int r = adb_read(fd.get(), &buffer[0], buffer.size() - sizeof(msg.data));
if (r <= 0) {
if (r == 0) break;
SendSyncFailErrno(s, "read failed");
return false;
}
msg.data.size = r;
static bool recv_impl(borrowed_fd s, const char* path, CompressionType compression,
std::vector<char>& buffer) {
__android_log_security_bswrite(SEC_TAG_ADB_RECV_FILE, path);
if (!WriteFdExactly(s, &msg.data, sizeof(msg.data)) || !WriteFdExactly(s, &buffer[0], r)) {
return false;
}
unique_fd fd(adb_open(path, O_RDONLY | O_CLOEXEC));
if (fd < 0) {
SendSyncFailErrno(s, "open failed");
return false;
}
return true;
}
int rc = posix_fadvise(fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
if (rc != 0) {
D("[ Failed to fadvise: %s ]", strerror(rc));
}
static bool recv_compressed(borrowed_fd s, unique_fd fd) {
syncmsg msg;
msg.data.id = ID_DATA;
BrotliEncoder<SYNC_DATA_MAX> encoder;
std::variant<std::monostate, NullEncoder, BrotliEncoder> encoder_storage;
Encoder* encoder;
switch (compression) {
case CompressionType::None:
encoder = &encoder_storage.emplace<NullEncoder>(SYNC_DATA_MAX);
break;
case CompressionType::Brotli:
encoder = &encoder_storage.emplace<BrotliEncoder>(SYNC_DATA_MAX);
break;
case CompressionType::Any:
LOG(FATAL) << "unexpected CompressionType::Any";
}
bool sending = true;
while (sending) {
@ -624,15 +622,15 @@ static bool recv_compressed(borrowed_fd s, unique_fd fd) {
}
if (r == 0) {
encoder.Finish();
encoder->Finish();
} else {
input.resize(r);
encoder.Append(std::move(input));
encoder->Append(std::move(input));
}
while (true) {
Block output;
EncodeResult result = encoder.Encode(&output);
EncodeResult result = encoder->Encode(&output);
if (result == EncodeResult::Error) {
SendSyncFailErrno(s, "compress failed");
return false;
@ -657,42 +655,13 @@ static bool recv_compressed(borrowed_fd s, unique_fd fd) {
}
}
return true;
}
static bool recv_impl(borrowed_fd s, const char* path, bool compressed, std::vector<char>& buffer) {
__android_log_security_bswrite(SEC_TAG_ADB_RECV_FILE, path);
unique_fd fd(adb_open(path, O_RDONLY | O_CLOEXEC));
if (fd < 0) {
SendSyncFailErrno(s, "open failed");
return false;
}
int rc = posix_fadvise(fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
if (rc != 0) {
D("[ Failed to fadvise: %s ]", strerror(rc));
}
bool result;
if (compressed) {
result = recv_compressed(s, std::move(fd));
} else {
result = recv_uncompressed(s, std::move(fd), buffer);
}
if (!result) {
return false;
}
syncmsg msg;
msg.data.id = ID_DONE;
msg.data.size = 0;
return WriteFdExactly(s, &msg.data, sizeof(msg.data));
}
static bool do_recv_v1(borrowed_fd s, const char* path, std::vector<char>& buffer) {
return recv_impl(s, path, false, buffer);
return recv_impl(s, path, CompressionType::None, buffer);
}
static bool do_recv_v2(borrowed_fd s, const char* path, std::vector<char>& buffer) {
@ -706,17 +675,23 @@ static bool do_recv_v2(borrowed_fd s, const char* path, std::vector<char>& buffe
PLOG(ERROR) << "failed to read recv_v2 setup packet";
}
bool compressed = false;
std::optional<CompressionType> compression;
if (msg.recv_v2_setup.flags & kSyncFlagBrotli) {
msg.recv_v2_setup.flags &= ~kSyncFlagBrotli;
compressed = true;
if (compression) {
SendSyncFail(s, android::base::StringPrintf("multiple compression flags received: %d",
msg.recv_v2_setup.flags));
return false;
}
compression = CompressionType::Brotli;
}
if (msg.recv_v2_setup.flags) {
SendSyncFail(s, android::base::StringPrintf("unknown flags: %d", msg.recv_v2_setup.flags));
return false;
}
return recv_impl(s, path, compressed, buffer);
return recv_impl(s, path, compression.value_or(CompressionType::None), buffer);
}
static const char* sync_id_to_name(uint32_t id) {

View file

@ -94,6 +94,12 @@ enum SyncFlag : uint32_t {
kSyncFlagBrotli = 1,
};
enum class CompressionType {
None,
Any,
Brotli,
};
// send_v1 sent the path in a buffer, followed by a comma and the mode as a string.
// send_v2 sends just the path in the first request, and then sends another syncmsg (with the
// same ID!) with details.

File diff suppressed because it is too large Load diff