adb: implement compression for file sync.
This improves performance when syncing by up to 2x (remote cuttlefish goes from 11.9 MB/s to 21.3 MB/s, blueline over USB 2.0 from 36 MB/s to 70 MB/s). This results in a slight drop in push speeds over USB 3.0 (125 -> 115 MB/s on blueline), presumably because we're compressing and extracting on only a single thread, but the gains over lower bandwidth transports make this worth it to submit this now and parallelize later. Bug: https://issuetracker.google.com/150827486 Test: ADB_COMPRESSION={0, 1} test_device.py (with new/old adbd) Change-Id: Ic2a0c974f1b6efecda115f87d336e3caac810035
This commit is contained in:
parent
5e4b94d44d
commit
939fc19aee
|
@ -317,6 +317,7 @@ cc_binary_host {
|
|||
"libandroidfw",
|
||||
"libapp_processes_protos_full",
|
||||
"libbase",
|
||||
"libbrotli",
|
||||
"libcutils",
|
||||
"libcrypto_utils",
|
||||
"libcrypto",
|
||||
|
@ -469,6 +470,7 @@ cc_library {
|
|||
static_libs: [
|
||||
"libadbconnection_server",
|
||||
"libadbd_core",
|
||||
"libbrotli",
|
||||
"libdiagnose_usb",
|
||||
],
|
||||
|
||||
|
@ -567,6 +569,7 @@ cc_library {
|
|||
},
|
||||
|
||||
static_libs: [
|
||||
"libbrotli",
|
||||
"libcutils_sockets",
|
||||
"libdiagnose_usb",
|
||||
"libmdnssd",
|
||||
|
@ -606,6 +609,7 @@ cc_binary {
|
|||
"libapp_processes_protos_lite",
|
||||
"libasyncio",
|
||||
"libbase",
|
||||
"libbrotli",
|
||||
"libcap",
|
||||
"libcrypto_utils",
|
||||
"libcutils_sockets",
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
|
||||
#include <brotli/decode.h>
|
||||
#include <brotli/encode.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
enum class BrotliDecodeResult {
|
||||
Error,
|
||||
Done,
|
||||
NeedInput,
|
||||
MoreOutput,
|
||||
};
|
||||
|
||||
struct BrotliDecoder {
|
||||
explicit BrotliDecoder(std::span<char> output_buffer)
|
||||
: output_buffer_(output_buffer),
|
||||
decoder_(BrotliDecoderCreateInstance(nullptr, nullptr, nullptr),
|
||||
BrotliDecoderDestroyInstance) {}
|
||||
|
||||
void Append(Block&& block) { input_buffer_.append(std::move(block)); }
|
||||
|
||||
BrotliDecodeResult Decode(std::span<char>* output) {
|
||||
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_buffer_.size();
|
||||
uint8_t* next_out = reinterpret_cast<uint8_t*>(output_buffer_.data());
|
||||
|
||||
BrotliDecoderResult r = BrotliDecoderDecompressStream(
|
||||
decoder_.get(), &available_in, &next_in, &available_out, &next_out, nullptr);
|
||||
|
||||
size_t bytes_consumed = input_buffer_.front_size() - available_in;
|
||||
input_buffer_.drop_front(bytes_consumed);
|
||||
|
||||
size_t bytes_emitted = output_buffer_.size() - available_out;
|
||||
*output = std::span<char>(output_buffer_.data(), bytes_emitted);
|
||||
|
||||
switch (r) {
|
||||
case BROTLI_DECODER_RESULT_SUCCESS:
|
||||
return BrotliDecodeResult::Done;
|
||||
case BROTLI_DECODER_RESULT_ERROR:
|
||||
return BrotliDecodeResult::Error;
|
||||
case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
|
||||
// Brotli guarantees as one of its invariants that if it returns NEEDS_MORE_INPUT,
|
||||
// it will consume the entire input buffer passed in, so we don't have to worry
|
||||
// about bytes left over in the front block with more input remaining.
|
||||
return BrotliDecodeResult::NeedInput;
|
||||
case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
|
||||
return BrotliDecodeResult::MoreOutput;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
IOVector input_buffer_;
|
||||
std::span<char> output_buffer_;
|
||||
std::unique_ptr<BrotliDecoderState, void (*)(BrotliDecoderState*)> decoder_;
|
||||
};
|
||||
|
||||
enum class BrotliEncodeResult {
|
||||
Error,
|
||||
Done,
|
||||
NeedInput,
|
||||
MoreOutput,
|
||||
};
|
||||
|
||||
template <size_t OutputBlockSize>
|
||||
struct BrotliEncoder {
|
||||
explicit BrotliEncoder()
|
||||
: output_block_(OutputBlockSize),
|
||||
output_bytes_left_(OutputBlockSize),
|
||||
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; }
|
||||
|
||||
BrotliEncodeResult Encode(Block* output) {
|
||||
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_));
|
||||
|
||||
BrotliEncoderOperation op = BROTLI_OPERATION_PROCESS;
|
||||
if (finished_) {
|
||||
op = BROTLI_OPERATION_FINISH;
|
||||
}
|
||||
|
||||
if (!BrotliEncoderCompressStream(encoder_.get(), op, &available_in, &next_in,
|
||||
&available_out, &next_out, nullptr)) {
|
||||
return BrotliEncodeResult::Error;
|
||||
}
|
||||
|
||||
size_t bytes_consumed = input_buffer_.front_size() - available_in;
|
||||
input_buffer_.drop_front(bytes_consumed);
|
||||
|
||||
output_bytes_left_ = available_out;
|
||||
|
||||
if (BrotliEncoderIsFinished(encoder_.get())) {
|
||||
output_block_.resize(OutputBlockSize - output_bytes_left_);
|
||||
*output = std::move(output_block_);
|
||||
return BrotliEncodeResult::Done;
|
||||
} else if (output_bytes_left_ == 0) {
|
||||
*output = std::move(output_block_);
|
||||
output_block_.resize(OutputBlockSize);
|
||||
output_bytes_left_ = OutputBlockSize;
|
||||
return BrotliEncodeResult::MoreOutput;
|
||||
} else if (input_buffer_.empty()) {
|
||||
return BrotliEncodeResult::NeedInput;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool finished_ = false;
|
||||
IOVector input_buffer_;
|
||||
Block output_block_;
|
||||
size_t output_bytes_left_;
|
||||
std::unique_ptr<BrotliEncoderState, void (*)(BrotliEncoderState*)> encoder_;
|
||||
};
|
|
@ -286,7 +286,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)) {
|
||||
if (do_sync_push(apk_file, apk_dest.c_str(), false, true)) {
|
||||
result = pm_command(argc, argv);
|
||||
delete_device_file(apk_dest);
|
||||
}
|
||||
|
|
|
@ -129,15 +129,21 @@ static void help() {
|
|||
" reverse --remove-all remove all reverse socket connections from device\n"
|
||||
"\n"
|
||||
"file transfer:\n"
|
||||
" push [--sync] LOCAL... REMOTE\n"
|
||||
" push [--sync] [-zZ] LOCAL... REMOTE\n"
|
||||
" copy local files/directories to device\n"
|
||||
" --sync: only push files that are newer on the host than the device\n"
|
||||
" pull [-a] REMOTE... LOCAL\n"
|
||||
" -z: enable compression\n"
|
||||
" -Z: disable compression\n"
|
||||
" pull [-azZ] REMOTE... LOCAL\n"
|
||||
" copy files/dirs from device\n"
|
||||
" -a: preserve file timestamp and mode\n"
|
||||
" sync [all|data|odm|oem|product|system|system_ext|vendor]\n"
|
||||
" -z: enable compression\n"
|
||||
" -Z: disable compression\n"
|
||||
" sync [-lzZ] [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: disable compression\n"
|
||||
"\n"
|
||||
"shell:\n"
|
||||
" shell [-e ESCAPE] [-n] [-Tt] [-x] [COMMAND...]\n"
|
||||
|
@ -1309,8 +1315,12 @@ static int restore(int argc, const char** argv) {
|
|||
}
|
||||
|
||||
static void parse_push_pull_args(const char** arg, int narg, std::vector<const char*>* srcs,
|
||||
const char** dst, bool* copy_attrs, bool* sync) {
|
||||
const char** dst, bool* copy_attrs, bool* sync, bool* compressed) {
|
||||
*copy_attrs = false;
|
||||
const char* adb_compression = getenv("ADB_COMPRESSION");
|
||||
if (adb_compression && strcmp(adb_compression, "0") == 0) {
|
||||
*compressed = false;
|
||||
}
|
||||
|
||||
srcs->clear();
|
||||
bool ignore_flags = false;
|
||||
|
@ -1322,6 +1332,14 @@ static void parse_push_pull_args(const char** arg, int narg, std::vector<const c
|
|||
// Silently ignore for backwards compatibility.
|
||||
} else if (!strcmp(*arg, "-a")) {
|
||||
*copy_attrs = true;
|
||||
} else if (!strcmp(*arg, "-z")) {
|
||||
if (compressed != nullptr) {
|
||||
*compressed = true;
|
||||
}
|
||||
} else if (!strcmp(*arg, "-Z")) {
|
||||
if (compressed != nullptr) {
|
||||
*compressed = false;
|
||||
}
|
||||
} else if (!strcmp(*arg, "--sync")) {
|
||||
if (sync != nullptr) {
|
||||
*sync = true;
|
||||
|
@ -1876,20 +1894,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;
|
||||
std::vector<const char*> srcs;
|
||||
const char* dst = nullptr;
|
||||
|
||||
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, &sync);
|
||||
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, &sync, &compressed);
|
||||
if (srcs.empty() || !dst) error_exit("push requires an argument");
|
||||
return do_sync_push(srcs, dst, sync) ? 0 : 1;
|
||||
return do_sync_push(srcs, dst, sync, compressed) ? 0 : 1;
|
||||
} else if (!strcmp(argv[0], "pull")) {
|
||||
bool copy_attrs = false;
|
||||
bool compressed = true;
|
||||
std::vector<const char*> srcs;
|
||||
const char* dst = ".";
|
||||
|
||||
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, nullptr);
|
||||
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, nullptr, &compressed);
|
||||
if (srcs.empty()) error_exit("pull requires an argument");
|
||||
return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
|
||||
return do_sync_pull(srcs, dst, copy_attrs, compressed) ? 0 : 1;
|
||||
} else if (!strcmp(argv[0], "install")) {
|
||||
if (argc < 2) error_exit("install requires an argument");
|
||||
return install_app(argc, argv);
|
||||
|
@ -1905,18 +1925,38 @@ int adb_commandline(int argc, const char** argv) {
|
|||
} else if (!strcmp(argv[0], "sync")) {
|
||||
std::string src;
|
||||
bool list_only = false;
|
||||
if (argc < 2) {
|
||||
// No partition specified: sync all of them.
|
||||
} else if (argc >= 2 && strcmp(argv[1], "-l") == 0) {
|
||||
list_only = true;
|
||||
if (argc == 3) src = argv[2];
|
||||
} else if (argc == 2) {
|
||||
src = argv[1];
|
||||
} else {
|
||||
error_exit("usage: adb sync [-l] [PARTITION]");
|
||||
bool compressed = true;
|
||||
|
||||
const char* adb_compression = getenv("ADB_COMPRESSION");
|
||||
if (adb_compression && strcmp(adb_compression, "0") == 0) {
|
||||
compressed = false;
|
||||
}
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, const_cast<char**>(argv), "lzZ")) != -1) {
|
||||
switch (opt) {
|
||||
case 'l':
|
||||
list_only = true;
|
||||
break;
|
||||
case 'z':
|
||||
compressed = true;
|
||||
break;
|
||||
case 'Z':
|
||||
compressed = false;
|
||||
break;
|
||||
default:
|
||||
error_exit("usage: adb sync [-lzZ] [PARTITION]");
|
||||
}
|
||||
}
|
||||
|
||||
if (optind == argc) {
|
||||
src = "all";
|
||||
} else if (optind + 1 == argc) {
|
||||
src = argv[optind];
|
||||
} else {
|
||||
error_exit("usage: adb sync [-lzZ] [PARTITION]");
|
||||
}
|
||||
|
||||
if (src.empty()) src = "all";
|
||||
std::vector<std::string> partitions{"data", "odm", "oem", "product",
|
||||
"system", "system_ext", "vendor"};
|
||||
bool found = false;
|
||||
|
@ -1925,7 +1965,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)) return 1;
|
||||
if (!do_sync_sync(src_dir, "/" + partition, list_only, compressed)) return 1;
|
||||
}
|
||||
}
|
||||
if (!found) error_exit("don't know how to sync %s partition", src.c_str());
|
||||
|
|
|
@ -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)) {
|
||||
if (!do_sync_push(srcs, dst, sync, true)) {
|
||||
error_exit("Failed to push fastdeploy agent to device.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "adb_client.h"
|
||||
#include "adb_io.h"
|
||||
#include "adb_utils.h"
|
||||
#include "brotli_utils.h"
|
||||
#include "file_sync_protocol.h"
|
||||
#include "line_printer.h"
|
||||
#include "sysdeps/errno.h"
|
||||
|
@ -233,6 +234,8 @@ class SyncConnection {
|
|||
} else {
|
||||
have_stat_v2_ = CanUseFeature(features_, kFeatureStat2);
|
||||
have_ls_v2_ = CanUseFeature(features_, kFeatureLs2);
|
||||
have_sendrecv_v2_ = CanUseFeature(features_, kFeatureSendRecv2);
|
||||
have_sendrecv_v2_brotli_ = CanUseFeature(features_, kFeatureSendRecv2Brotli);
|
||||
fd.reset(adb_connect("sync:", &error));
|
||||
if (fd < 0) {
|
||||
Error("connect failed: %s", error.c_str());
|
||||
|
@ -256,6 +259,9 @@ class SyncConnection {
|
|||
line_printer_.KeepInfoLine();
|
||||
}
|
||||
|
||||
bool HaveSendRecv2() const { return have_sendrecv_v2_; }
|
||||
bool HaveSendRecv2Brotli() const { return have_sendrecv_v2_brotli_; }
|
||||
|
||||
const FeatureSet& Features() const { return features_; }
|
||||
|
||||
bool IsValid() { return fd >= 0; }
|
||||
|
@ -314,6 +320,62 @@ class SyncConnection {
|
|||
req->path_length = path.length();
|
||||
char* data = reinterpret_cast<char*>(req + 1);
|
||||
memcpy(data, path.data(), path.length());
|
||||
return WriteFdExactly(fd, buf.data(), buf.size());
|
||||
}
|
||||
|
||||
bool SendSend2(std::string_view path, mode_t mode, bool compressed) {
|
||||
if (path.length() > 1024) {
|
||||
Error("SendRequest failed: path too long: %zu", path.length());
|
||||
errno = ENAMETOOLONG;
|
||||
return false;
|
||||
}
|
||||
|
||||
Block buf;
|
||||
|
||||
SyncRequest req;
|
||||
req.id = ID_SEND_V2;
|
||||
req.path_length = path.length();
|
||||
|
||||
syncmsg msg;
|
||||
msg.send_v2_setup.id = ID_SEND_V2;
|
||||
msg.send_v2_setup.mode = mode;
|
||||
msg.send_v2_setup.flags = compressed ? kSyncFlagBrotli : kSyncFlagNone;
|
||||
|
||||
buf.resize(sizeof(SyncRequest) + path.length() + sizeof(msg.send_v2_setup));
|
||||
|
||||
void* p = buf.data();
|
||||
|
||||
p = mempcpy(p, &req, sizeof(SyncRequest));
|
||||
p = mempcpy(p, path.data(), path.length());
|
||||
p = mempcpy(p, &msg.send_v2_setup, sizeof(msg.send_v2_setup));
|
||||
|
||||
return WriteFdExactly(fd, buf.data(), buf.size());
|
||||
}
|
||||
|
||||
bool SendRecv2(const std::string& path) {
|
||||
if (path.length() > 1024) {
|
||||
Error("SendRequest failed: path too long: %zu", path.length());
|
||||
errno = ENAMETOOLONG;
|
||||
return false;
|
||||
}
|
||||
|
||||
Block buf;
|
||||
|
||||
SyncRequest req;
|
||||
req.id = ID_RECV_V2;
|
||||
req.path_length = path.length();
|
||||
|
||||
syncmsg msg;
|
||||
msg.recv_v2_setup.id = ID_RECV_V2;
|
||||
msg.recv_v2_setup.flags = kSyncFlagBrotli;
|
||||
|
||||
buf.resize(sizeof(SyncRequest) + path.length() + sizeof(msg.recv_v2_setup));
|
||||
|
||||
void* p = buf.data();
|
||||
|
||||
p = mempcpy(p, &req, sizeof(SyncRequest));
|
||||
p = mempcpy(p, path.data(), path.length());
|
||||
p = mempcpy(p, &msg.recv_v2_setup, sizeof(msg.recv_v2_setup));
|
||||
|
||||
return WriteFdExactly(fd, buf.data(), buf.size());
|
||||
}
|
||||
|
@ -370,8 +432,8 @@ class SyncConnection {
|
|||
}
|
||||
|
||||
if (msg.stat_v1.id != ID_LSTAT_V1) {
|
||||
PLOG(FATAL) << "protocol fault: stat response has wrong message id: "
|
||||
<< msg.stat_v1.id;
|
||||
LOG(FATAL) << "protocol fault: stat response has wrong message id: "
|
||||
<< msg.stat_v1.id;
|
||||
}
|
||||
|
||||
if (msg.stat_v1.mode == 0 && msg.stat_v1.size == 0 && msg.stat_v1.mtime == 0) {
|
||||
|
@ -445,7 +507,7 @@ class SyncConnection {
|
|||
char* p = &buf[0];
|
||||
|
||||
SyncRequest* req_send = reinterpret_cast<SyncRequest*>(p);
|
||||
req_send->id = ID_SEND;
|
||||
req_send->id = ID_SEND_V1;
|
||||
req_send->path_length = path_and_mode.length();
|
||||
p += sizeof(SyncRequest);
|
||||
memcpy(p, path_and_mode.data(), path_and_mode.size());
|
||||
|
@ -471,11 +533,92 @@ 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)) {
|
||||
Error("failed to send ID_SEND_V2 message '%s': %s", path.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(lpath.c_str(), &st) == -1) {
|
||||
Error("cannot stat '%s': %s", lpath.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t total_size = st.st_size;
|
||||
uint64_t bytes_copied = 0;
|
||||
|
||||
unique_fd lfd(adb_open(lpath.c_str(), O_RDONLY | O_CLOEXEC));
|
||||
if (lfd < 0) {
|
||||
Error("opening '%s' locally failed: %s", lpath.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
syncsendbuf sbuf;
|
||||
sbuf.id = ID_DATA;
|
||||
|
||||
BrotliEncoder<SYNC_DATA_MAX> encoder;
|
||||
bool sending = true;
|
||||
while (sending) {
|
||||
Block input(SYNC_DATA_MAX);
|
||||
int r = adb_read(lfd.get(), input.data(), input.size());
|
||||
if (r < 0) {
|
||||
Error("reading '%s' locally failed: %s", lpath.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (r == 0) {
|
||||
encoder.Finish();
|
||||
} else {
|
||||
input.resize(r);
|
||||
encoder.Append(std::move(input));
|
||||
RecordBytesTransferred(r);
|
||||
bytes_copied += r;
|
||||
ReportProgress(rpath, bytes_copied, total_size);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
Block output;
|
||||
BrotliEncodeResult result = encoder.Encode(&output);
|
||||
if (result == BrotliEncodeResult::Error) {
|
||||
Error("compressing '%s' locally failed", lpath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!output.empty()) {
|
||||
sbuf.size = output.size();
|
||||
memcpy(sbuf.data, output.data(), output.size());
|
||||
WriteOrDie(lpath, rpath, &sbuf, sizeof(SyncRequest) + output.size());
|
||||
}
|
||||
|
||||
if (result == BrotliEncodeResult::Done) {
|
||||
sending = false;
|
||||
break;
|
||||
} else if (result == BrotliEncodeResult::NeedInput) {
|
||||
break;
|
||||
} else if (result == BrotliEncodeResult::MoreOutput) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
syncmsg msg;
|
||||
msg.data.id = ID_DONE;
|
||||
msg.data.size = mtime;
|
||||
RecordFileSent(lpath, rpath);
|
||||
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) {
|
||||
const std::string& rpath, unsigned mtime, bool compressed) {
|
||||
if (compressed && HaveSendRecv2Brotli()) {
|
||||
return SendLargeFileCompressed(path, mode, lpath, rpath, mtime);
|
||||
}
|
||||
|
||||
std::string path_and_mode = android::base::StringPrintf("%s,%d", path.c_str(), mode);
|
||||
if (!SendRequest(ID_SEND, path_and_mode)) {
|
||||
Error("failed to send ID_SEND message '%s': %s", path_and_mode.c_str(),
|
||||
if (!SendRequest(ID_SEND_V1, path_and_mode.c_str())) {
|
||||
Error("failed to send ID_SEND_V1 message '%s': %s", path_and_mode.c_str(),
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
@ -489,7 +632,7 @@ class SyncConnection {
|
|||
uint64_t total_size = st.st_size;
|
||||
uint64_t bytes_copied = 0;
|
||||
|
||||
unique_fd lfd(adb_open(lpath.c_str(), O_RDONLY));
|
||||
unique_fd lfd(adb_open(lpath.c_str(), O_RDONLY | O_CLOEXEC));
|
||||
if (lfd < 0) {
|
||||
Error("opening '%s' locally failed: %s", lpath.c_str(), strerror(errno));
|
||||
return false;
|
||||
|
@ -497,8 +640,9 @@ class SyncConnection {
|
|||
|
||||
syncsendbuf sbuf;
|
||||
sbuf.id = ID_DATA;
|
||||
|
||||
while (true) {
|
||||
int bytes_read = adb_read(lfd, sbuf.data, max - sizeof(SyncRequest));
|
||||
int bytes_read = adb_read(lfd, sbuf.data, max);
|
||||
if (bytes_read == -1) {
|
||||
Error("reading '%s' locally failed: %s", lpath.c_str(), strerror(errno));
|
||||
return false;
|
||||
|
@ -511,7 +655,6 @@ class SyncConnection {
|
|||
|
||||
RecordBytesTransferred(bytes_read);
|
||||
bytes_copied += bytes_read;
|
||||
|
||||
ReportProgress(rpath, bytes_copied, total_size);
|
||||
}
|
||||
|
||||
|
@ -695,6 +838,8 @@ class SyncConnection {
|
|||
FeatureSet features_;
|
||||
bool have_stat_v2_;
|
||||
bool have_ls_v2_;
|
||||
bool have_sendrecv_v2_;
|
||||
bool have_sendrecv_v2_brotli_;
|
||||
|
||||
TransferLedger global_ledger_;
|
||||
TransferLedger current_ledger_;
|
||||
|
@ -776,7 +921,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) {
|
||||
unsigned mtime, mode_t mode, bool sync, bool compressed) {
|
||||
if (sync) {
|
||||
struct stat st;
|
||||
if (sync_lstat(sc, rpath, &st)) {
|
||||
|
@ -819,16 +964,16 @@ static bool sync_send(SyncConnection& sc, const std::string& lpath, const std::s
|
|||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!sc.SendLargeFile(rpath, mode, lpath, rpath, mtime)) {
|
||||
if (!sc.SendLargeFile(rpath, mode, lpath, rpath, mtime, compressed)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return sc.ReadAcknowledgements();
|
||||
}
|
||||
|
||||
static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath,
|
||||
const char* name, uint64_t expected_size) {
|
||||
if (!sc.SendRequest(ID_RECV, rpath)) return false;
|
||||
static bool sync_recv_v1(SyncConnection& sc, const char* rpath, const char* lpath, const char* name,
|
||||
uint64_t expected_size) {
|
||||
if (!sc.SendRequest(ID_RECV_V1, rpath)) return false;
|
||||
|
||||
adb_unlink(lpath);
|
||||
unique_fd lfd(adb_creat(lpath, 0644));
|
||||
|
@ -881,6 +1026,114 @@ static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath,
|
|||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
adb_unlink(lpath);
|
||||
unique_fd lfd(adb_creat(lpath, 0644));
|
||||
if (lfd < 0) {
|
||||
sc.Error("cannot create '%s': %s", lpath, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t bytes_copied = 0;
|
||||
|
||||
Block buffer(SYNC_DATA_MAX);
|
||||
BrotliDecoder decoder(std::span(buffer.data(), buffer.size()));
|
||||
bool reading = true;
|
||||
while (reading) {
|
||||
syncmsg msg;
|
||||
if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
|
||||
adb_unlink(lpath);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (msg.data.id == ID_DONE) {
|
||||
adb_unlink(lpath);
|
||||
sc.Error("unexpected ID_DONE");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (msg.data.id != ID_DATA) {
|
||||
adb_unlink(lpath);
|
||||
sc.ReportCopyFailure(rpath, lpath, msg);
|
||||
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));
|
||||
|
||||
while (true) {
|
||||
std::span<char> output;
|
||||
BrotliDecodeResult result = decoder.Decode(&output);
|
||||
|
||||
if (result == BrotliDecodeResult::Error) {
|
||||
sc.Error("decompress failed");
|
||||
adb_unlink(lpath);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!output.empty()) {
|
||||
if (!WriteFdExactly(lfd, output.data(), output.size())) {
|
||||
sc.Error("cannot write '%s': %s", lpath, strerror(errno));
|
||||
adb_unlink(lpath);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bytes_copied += output.size();
|
||||
|
||||
sc.RecordBytesTransferred(msg.data.size);
|
||||
sc.ReportProgress(name != nullptr ? name : rpath, bytes_copied, expected_size);
|
||||
|
||||
if (result == BrotliDecodeResult::NeedInput) {
|
||||
break;
|
||||
} else if (result == BrotliDecodeResult::MoreOutput) {
|
||||
continue;
|
||||
} else if (result == BrotliDecodeResult::Done) {
|
||||
reading = false;
|
||||
break;
|
||||
} else {
|
||||
LOG(FATAL) << "invalid BrotliDecodeResult: " << 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);
|
||||
} else {
|
||||
return sync_recv_v1(sc, rpath, lpath, name, expected_size);
|
||||
}
|
||||
}
|
||||
|
||||
bool do_sync_ls(const char* path) {
|
||||
SyncConnection sc;
|
||||
if (!sc.IsValid()) return false;
|
||||
|
@ -956,9 +1209,8 @@ static bool is_root_dir(std::string_view path) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
|
||||
std::string rpath, bool check_timestamps,
|
||||
bool list_only) {
|
||||
static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath, std::string rpath,
|
||||
bool check_timestamps, bool list_only, bool compressed) {
|
||||
sc.NewTransfer();
|
||||
|
||||
// Make sure that both directory paths end in a slash.
|
||||
|
@ -1040,7 +1292,7 @@ static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
|
|||
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)) {
|
||||
if (!sync_send(sc, ci.lpath, ci.rpath, ci.time, ci.mode, false, compressed)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1055,7 +1307,8 @@ static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
|
|||
return success;
|
||||
}
|
||||
|
||||
bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync) {
|
||||
bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync,
|
||||
bool compressed) {
|
||||
SyncConnection sc;
|
||||
if (!sc.IsValid()) return false;
|
||||
|
||||
|
@ -1120,7 +1373,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);
|
||||
success &= copy_local_dir_remote(sc, src_path, dst_dir, sync, false, compressed);
|
||||
continue;
|
||||
} else if (!should_push_file(st.st_mode)) {
|
||||
sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
|
||||
|
@ -1141,7 +1394,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);
|
||||
success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode, sync, compressed);
|
||||
sc.ReportTransferRate(src_path, TransferDirection::push);
|
||||
}
|
||||
|
||||
|
@ -1226,8 +1479,8 @@ static int set_time_and_mode(const std::string& lpath, time_t time,
|
|||
return r1 ? r1 : r2;
|
||||
}
|
||||
|
||||
static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath,
|
||||
std::string lpath, bool copy_attrs) {
|
||||
static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath, std::string lpath,
|
||||
bool copy_attrs, bool compressed) {
|
||||
sc.NewTransfer();
|
||||
|
||||
// Make sure that both directory paths end in a slash.
|
||||
|
@ -1257,7 +1510,7 @@ static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str(), nullptr, ci.size)) {
|
||||
if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str(), nullptr, ci.size, compressed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1274,8 +1527,8 @@ static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
|
||||
bool copy_attrs, const char* name) {
|
||||
bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
|
||||
bool compressed, const char* name) {
|
||||
SyncConnection sc;
|
||||
if (!sc.IsValid()) return false;
|
||||
|
||||
|
@ -1349,7 +1602,7 @@ bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
|
|||
dst_dir.append(android::base::Basename(src_path));
|
||||
}
|
||||
|
||||
success &= copy_remote_dir_local(sc, src_path, dst_dir, copy_attrs);
|
||||
success &= copy_remote_dir_local(sc, src_path, dst_dir, copy_attrs, compressed);
|
||||
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);
|
||||
|
@ -1368,7 +1621,7 @@ bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
|
|||
|
||||
sc.NewTransfer();
|
||||
sc.SetExpectedTotalBytes(src_st.st_size);
|
||||
if (!sync_recv(sc, src_path, dst_path, name, src_st.st_size)) {
|
||||
if (!sync_recv(sc, src_path, dst_path, name, src_st.st_size, compressed)) {
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
|
@ -1384,11 +1637,12 @@ bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
|
|||
return success;
|
||||
}
|
||||
|
||||
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {
|
||||
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only,
|
||||
bool compressed) {
|
||||
SyncConnection sc;
|
||||
if (!sc.IsValid()) return false;
|
||||
|
||||
bool success = copy_local_dir_remote(sc, lpath, rpath, true, list_only);
|
||||
bool success = copy_local_dir_remote(sc, lpath, rpath, true, list_only, compressed);
|
||||
if (!list_only) {
|
||||
sc.ReportOverallTransferRate(TransferDirection::push);
|
||||
}
|
||||
|
|
|
@ -20,8 +20,10 @@
|
|||
#include <vector>
|
||||
|
||||
bool do_sync_ls(const char* path);
|
||||
bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync);
|
||||
bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync,
|
||||
bool compressed);
|
||||
bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
|
||||
const char* name = nullptr);
|
||||
bool compressed, const char* name = nullptr);
|
||||
|
||||
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
|
||||
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only,
|
||||
bool compressed);
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include <utime.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -55,10 +57,12 @@
|
|||
#include "adb_io.h"
|
||||
#include "adb_trace.h"
|
||||
#include "adb_utils.h"
|
||||
#include "brotli_utils.h"
|
||||
#include "file_sync_protocol.h"
|
||||
#include "security_log_tags.h"
|
||||
#include "sysdeps/errno.h"
|
||||
|
||||
using android::base::borrowed_fd;
|
||||
using android::base::Dirname;
|
||||
using android::base::StringPrintf;
|
||||
|
||||
|
@ -249,7 +253,7 @@ static bool do_list_v2(int s, const char* path) {
|
|||
// Make sure that SendFail from adb_io.cpp isn't accidentally used in this file.
|
||||
#pragma GCC poison SendFail
|
||||
|
||||
static bool SendSyncFail(int fd, const std::string& reason) {
|
||||
static bool SendSyncFail(borrowed_fd fd, const std::string& reason) {
|
||||
D("sync: failure: %s", reason.c_str());
|
||||
|
||||
syncmsg msg;
|
||||
|
@ -258,13 +262,89 @@ static bool SendSyncFail(int fd, const std::string& reason) {
|
|||
return WriteFdExactly(fd, &msg.data, sizeof(msg.data)) && WriteFdExactly(fd, reason);
|
||||
}
|
||||
|
||||
static bool SendSyncFailErrno(int fd, const std::string& reason) {
|
||||
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(int s, const char* path, uint32_t* timestamp, uid_t uid, gid_t gid,
|
||||
uint64_t capabilities, mode_t mode, std::vector<char>& buffer,
|
||||
bool do_unlink) {
|
||||
static bool handle_send_file_compressed(borrowed_fd s, unique_fd fd, uint32_t* timestamp) {
|
||||
syncmsg msg;
|
||||
Block decode_buffer(SYNC_DATA_MAX);
|
||||
BrotliDecoder decoder(std::span(decode_buffer.data(), decode_buffer.size()));
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
BrotliDecodeResult result = decoder.Decode(&output);
|
||||
if (result == BrotliDecodeResult::Error) {
|
||||
SendSyncFailErrno(s, "decompress failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WriteFdExactly(fd, output.data(), output.size())) {
|
||||
SendSyncFailErrno(s, "write failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result == BrotliDecodeResult::NeedInput) {
|
||||
break;
|
||||
} else if (result == BrotliDecodeResult::MoreOutput) {
|
||||
continue;
|
||||
} else if (result == BrotliDecodeResult::Done) {
|
||||
break;
|
||||
} else {
|
||||
LOG(FATAL) << "invalid BrotliDecodeResult: " << static_cast<int>(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__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) {
|
||||
int rc;
|
||||
syncmsg msg;
|
||||
|
||||
|
@ -302,45 +382,33 @@ static bool handle_send_file(int s, const char* path, uint32_t* timestamp, uid_t
|
|||
fchmod(fd.get(), mode);
|
||||
}
|
||||
|
||||
rc = posix_fadvise(fd.get(), 0, 0,
|
||||
POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED);
|
||||
if (rc != 0) {
|
||||
D("[ Failed to fadvise: %s ]", strerror(rc));
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
|
||||
|
||||
if (msg.data.id != ID_DATA) {
|
||||
if (msg.data.id == ID_DONE) {
|
||||
*timestamp = msg.data.size;
|
||||
break;
|
||||
}
|
||||
SendSyncFail(s, "invalid data message");
|
||||
goto abort;
|
||||
{
|
||||
rc = posix_fadvise(fd.get(), 0, 0,
|
||||
POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED);
|
||||
if (rc != 0) {
|
||||
D("[ Failed to fadvise: %s ]", strerror(rc));
|
||||
}
|
||||
|
||||
if (msg.data.size > buffer.size()) { // TODO: resize buffer?
|
||||
SendSyncFail(s, "oversize data message");
|
||||
goto abort;
|
||||
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 (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
|
||||
|
||||
if (!WriteFdExactly(fd.get(), &buffer[0], msg.data.size)) {
|
||||
SendSyncFailErrno(s, "write failed");
|
||||
if (!result) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (!update_capabilities(path, capabilities)) {
|
||||
SendSyncFailErrno(s, "update_capabilities failed");
|
||||
goto fail;
|
||||
}
|
||||
if (!update_capabilities(path, capabilities)) {
|
||||
SendSyncFailErrno(s, "update_capabilities failed");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
msg.status.id = ID_OKAY;
|
||||
msg.status.msglen = 0;
|
||||
return WriteFdExactly(s, &msg.status, sizeof(msg.status));
|
||||
msg.status.id = ID_OKAY;
|
||||
msg.status.msglen = 0;
|
||||
return WriteFdExactly(s, &msg.status, sizeof(msg.status));
|
||||
}
|
||||
|
||||
fail:
|
||||
// If there's a problem on the device, we'll send an ID_FAIL message and
|
||||
|
@ -371,7 +439,6 @@ fail:
|
|||
if (!ReadFdExactly(s, &buffer[0], msg.data.size)) break;
|
||||
}
|
||||
|
||||
abort:
|
||||
if (do_unlink) adb_unlink(path);
|
||||
return false;
|
||||
}
|
||||
|
@ -432,23 +499,8 @@ static bool handle_send_link(int s, const std::string& path, uint32_t* timestamp
|
|||
}
|
||||
#endif
|
||||
|
||||
static bool do_send(int s, const std::string& spec, std::vector<char>& buffer) {
|
||||
// 'spec' is of the form "/some/path,0755". Break it up.
|
||||
size_t comma = spec.find_last_of(',');
|
||||
if (comma == std::string::npos) {
|
||||
SendSyncFail(s, "missing , in ID_SEND");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path = spec.substr(0, comma);
|
||||
|
||||
errno = 0;
|
||||
mode_t mode = strtoul(spec.substr(comma + 1).c_str(), nullptr, 0);
|
||||
if (errno != 0) {
|
||||
SendSyncFail(s, "bad mode");
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool send_impl(int s, const std::string& path, mode_t mode, bool compressed,
|
||||
std::vector<char>& buffer) {
|
||||
// Don't delete files before copying if they are not "regular" or symlinks.
|
||||
struct stat st;
|
||||
bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) ||
|
||||
|
@ -474,8 +526,8 @@ static bool do_send(int s, const std::string& spec, std::vector<char>& buffer) {
|
|||
adbd_fs_config(path.c_str(), 0, nullptr, &uid, &gid, &mode, &capabilities);
|
||||
}
|
||||
|
||||
result = handle_send_file(s, path.c_str(), ×tamp, uid, gid, capabilities, mode, buffer,
|
||||
do_unlink);
|
||||
result = handle_send_file(s, path.c_str(), ×tamp, uid, gid, capabilities, mode,
|
||||
compressed, buffer, do_unlink);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
|
@ -491,7 +543,125 @@ static bool do_send(int s, const std::string& spec, std::vector<char>& buffer) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool do_recv(int s, const char* path, std::vector<char>& buffer) {
|
||||
static bool do_send_v1(int s, const std::string& spec, std::vector<char>& buffer) {
|
||||
// 'spec' is of the form "/some/path,0755". Break it up.
|
||||
size_t comma = spec.find_last_of(',');
|
||||
if (comma == std::string::npos) {
|
||||
SendSyncFail(s, "missing , in ID_SEND_V1");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path = spec.substr(0, comma);
|
||||
|
||||
errno = 0;
|
||||
mode_t mode = strtoul(spec.substr(comma + 1).c_str(), nullptr, 0);
|
||||
if (errno != 0) {
|
||||
SendSyncFail(s, "bad mode");
|
||||
return false;
|
||||
}
|
||||
|
||||
return send_impl(s, path, mode, false, buffer);
|
||||
}
|
||||
|
||||
static bool do_send_v2(int s, const std::string& path, std::vector<char>& buffer) {
|
||||
// Read the setup packet.
|
||||
syncmsg msg;
|
||||
int rc = ReadFdExactly(s, &msg.send_v2_setup, sizeof(msg.send_v2_setup));
|
||||
if (rc == 0) {
|
||||
LOG(ERROR) << "failed to read send_v2 setup packet: EOF";
|
||||
return false;
|
||||
} else if (rc < 0) {
|
||||
PLOG(ERROR) << "failed to read send_v2 setup packet";
|
||||
}
|
||||
|
||||
bool compressed = false;
|
||||
if (msg.send_v2_setup.flags & kSyncFlagBrotli) {
|
||||
msg.send_v2_setup.flags &= ~kSyncFlagBrotli;
|
||||
compressed = true;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
static bool recv_uncompressed(borrowed_fd s, unique_fd fd, std::vector<char>& buffer) {
|
||||
syncmsg msg;
|
||||
msg.data.id = ID_DATA;
|
||||
std::optional<BrotliEncoder<SYNC_DATA_MAX>> encoder;
|
||||
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;
|
||||
|
||||
if (!WriteFdExactly(s, &msg.data, sizeof(msg.data)) || !WriteFdExactly(s, &buffer[0], r)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool recv_compressed(borrowed_fd s, unique_fd fd) {
|
||||
syncmsg msg;
|
||||
msg.data.id = ID_DATA;
|
||||
|
||||
BrotliEncoder<SYNC_DATA_MAX> encoder;
|
||||
|
||||
bool sending = true;
|
||||
while (sending) {
|
||||
Block input(SYNC_DATA_MAX);
|
||||
int r = adb_read(fd.get(), input.data(), input.size());
|
||||
if (r < 0) {
|
||||
SendSyncFailErrno(s, "read failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (r == 0) {
|
||||
encoder.Finish();
|
||||
} else {
|
||||
input.resize(r);
|
||||
encoder.Append(std::move(input));
|
||||
}
|
||||
|
||||
while (true) {
|
||||
Block output;
|
||||
BrotliEncodeResult result = encoder.Encode(&output);
|
||||
if (result == BrotliEncodeResult::Error) {
|
||||
SendSyncFailErrno(s, "compress failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!output.empty()) {
|
||||
msg.data.size = output.size();
|
||||
if (!WriteFdExactly(s, &msg.data, sizeof(msg.data)) ||
|
||||
!WriteFdExactly(s, output.data(), output.size())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == BrotliEncodeResult::Done) {
|
||||
sending = false;
|
||||
break;
|
||||
} else if (result == BrotliEncodeResult::NeedInput) {
|
||||
break;
|
||||
} else if (result == BrotliEncodeResult::MoreOutput) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
@ -505,26 +675,51 @@ static bool do_recv(int s, const char* path, std::vector<char>& buffer) {
|
|||
D("[ Failed to fadvise: %s ]", strerror(rc));
|
||||
}
|
||||
|
||||
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;
|
||||
if (!WriteFdExactly(s, &msg.data, sizeof(msg.data)) || !WriteFdExactly(s, &buffer[0], r)) {
|
||||
return false;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
static bool do_recv_v2(borrowed_fd s, const char* path, std::vector<char>& buffer) {
|
||||
syncmsg msg;
|
||||
// Read the setup packet.
|
||||
int rc = ReadFdExactly(s, &msg.recv_v2_setup, sizeof(msg.recv_v2_setup));
|
||||
if (rc == 0) {
|
||||
LOG(ERROR) << "failed to read recv_v2 setup packet: EOF";
|
||||
return false;
|
||||
} else if (rc < 0) {
|
||||
PLOG(ERROR) << "failed to read recv_v2 setup packet";
|
||||
}
|
||||
|
||||
bool compressed = false;
|
||||
if (msg.recv_v2_setup.flags & kSyncFlagBrotli) {
|
||||
msg.recv_v2_setup.flags &= ~kSyncFlagBrotli;
|
||||
compressed = true;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
static const char* sync_id_to_name(uint32_t id) {
|
||||
switch (id) {
|
||||
case ID_LSTAT_V1:
|
||||
|
@ -537,10 +732,14 @@ static const char* sync_id_to_name(uint32_t id) {
|
|||
return "list_v1";
|
||||
case ID_LIST_V2:
|
||||
return "list_v2";
|
||||
case ID_SEND:
|
||||
return "send";
|
||||
case ID_RECV:
|
||||
return "recv";
|
||||
case ID_SEND_V1:
|
||||
return "send_v1";
|
||||
case ID_SEND_V2:
|
||||
return "send_v2";
|
||||
case ID_RECV_V1:
|
||||
return "recv_v1";
|
||||
case ID_RECV_V2:
|
||||
return "recv_v2";
|
||||
case ID_QUIT:
|
||||
return "quit";
|
||||
default:
|
||||
|
@ -585,11 +784,17 @@ static bool handle_sync_command(int fd, std::vector<char>& buffer) {
|
|||
case ID_LIST_V2:
|
||||
if (!do_list_v2(fd, name)) return false;
|
||||
break;
|
||||
case ID_SEND:
|
||||
if (!do_send(fd, name, buffer)) return false;
|
||||
case ID_SEND_V1:
|
||||
if (!do_send_v1(fd, name, buffer)) return false;
|
||||
break;
|
||||
case ID_RECV:
|
||||
if (!do_recv(fd, name, buffer)) return false;
|
||||
case ID_SEND_V2:
|
||||
if (!do_send_v2(fd, name, buffer)) return false;
|
||||
break;
|
||||
case ID_RECV_V1:
|
||||
if (!do_recv_v1(fd, name, buffer)) return false;
|
||||
break;
|
||||
case ID_RECV_V2:
|
||||
if (!do_recv_v2(fd, name, buffer)) return false;
|
||||
break;
|
||||
case ID_QUIT:
|
||||
return false;
|
||||
|
|
|
@ -27,8 +27,10 @@
|
|||
#define ID_DENT_V1 MKID('D', 'E', 'N', 'T')
|
||||
#define ID_DENT_V2 MKID('D', 'N', 'T', '2')
|
||||
|
||||
#define ID_SEND MKID('S', 'E', 'N', 'D')
|
||||
#define ID_RECV MKID('R', 'E', 'C', 'V')
|
||||
#define ID_SEND_V1 MKID('S', 'E', 'N', 'D')
|
||||
#define ID_SEND_V2 MKID('S', 'N', 'D', '2')
|
||||
#define ID_RECV_V1 MKID('R', 'E', 'C', 'V')
|
||||
#define ID_RECV_V2 MKID('R', 'C', 'V', '2')
|
||||
#define ID_DONE MKID('D', 'O', 'N', 'E')
|
||||
#define ID_DATA MKID('D', 'A', 'T', 'A')
|
||||
#define ID_OKAY MKID('O', 'K', 'A', 'Y')
|
||||
|
@ -87,6 +89,26 @@ struct __attribute__((packed)) sync_dent_v2 {
|
|||
uint32_t namelen;
|
||||
}; // followed by `namelen` bytes of the name.
|
||||
|
||||
enum SyncFlag : uint32_t {
|
||||
kSyncFlagNone = 0,
|
||||
kSyncFlagBrotli = 1,
|
||||
};
|
||||
|
||||
// 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.
|
||||
struct __attribute__((packed)) sync_send_v2 {
|
||||
uint32_t id;
|
||||
uint32_t mode;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
// Likewise, recv_v1 just sent the path without any accompanying data.
|
||||
struct __attribute__((packed)) sync_recv_v2 {
|
||||
uint32_t id;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) sync_data {
|
||||
uint32_t id;
|
||||
uint32_t size;
|
||||
|
@ -104,6 +126,8 @@ union syncmsg {
|
|||
sync_dent_v2 dent_v2;
|
||||
sync_data data;
|
||||
sync_status status;
|
||||
sync_send_v2 send_v2_setup;
|
||||
sync_recv_v2 recv_v2_setup;
|
||||
};
|
||||
|
||||
#define SYNC_DATA_MAX (64 * 1024)
|
||||
|
|
|
@ -82,6 +82,8 @@ const char* const kFeatureFixedPushSymlinkTimestamp = "fixed_push_symlink_timest
|
|||
const char* const kFeatureAbbExec = "abb_exec";
|
||||
const char* const kFeatureRemountShell = "remount_shell";
|
||||
const char* const kFeatureTrackApp = "track_app";
|
||||
const char* const kFeatureSendRecv2 = "sendrecv_v2";
|
||||
const char* const kFeatureSendRecv2Brotli = "sendrecv_v2_brotli";
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -1177,6 +1179,8 @@ const FeatureSet& supported_features() {
|
|||
kFeatureAbbExec,
|
||||
kFeatureRemountShell,
|
||||
kFeatureTrackApp,
|
||||
kFeatureSendRecv2,
|
||||
kFeatureSendRecv2Brotli,
|
||||
// Increment ADB_SERVER_VERSION when adding a feature that adbd needs
|
||||
// to know about. Otherwise, the client can be stuck running an old
|
||||
// version of the server even after upgrading their copy of adb.
|
||||
|
|
|
@ -81,9 +81,14 @@ extern const char* const kFeatureAbb;
|
|||
extern const char* const kFeatureAbbExec;
|
||||
// adbd properly updates symlink timestamps on push.
|
||||
extern const char* const kFeatureFixedPushSymlinkTimestamp;
|
||||
// Implement `adb remount` via shelling out to /system/bin/remount.
|
||||
extern const char* const kFeatureRemountShell;
|
||||
// adbd supports `track-app` service reporting debuggable/profileable apps.
|
||||
extern const char* const kFeatureTrackApp;
|
||||
// adbd supports version 2 of send/recv.
|
||||
extern const char* const kFeatureSendRecv2;
|
||||
// adbd supports brotli for send/recv v2.
|
||||
extern const char* const kFeatureSendRecv2Brotli;
|
||||
|
||||
TransportId NextTransportId();
|
||||
|
||||
|
|
16
adb/types.h
16
adb/types.h
|
@ -150,6 +150,22 @@ struct IOVector {
|
|||
IOVector& operator=(const IOVector& copy) = delete;
|
||||
IOVector& operator=(IOVector&& move) noexcept;
|
||||
|
||||
const value_type* front_data() const {
|
||||
if (chain_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return chain_.front().data() + begin_offset_;
|
||||
}
|
||||
|
||||
size_type front_size() const {
|
||||
if (chain_.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return chain_.front().size() - begin_offset_;
|
||||
}
|
||||
|
||||
size_type size() const { return chain_length_ - begin_offset_; }
|
||||
bool empty() const { return size() == 0; }
|
||||
|
||||
|
|
Loading…
Reference in New Issue