adb: add dry-run option to push/sync.
Make it easier to benchmark file sync performance by ignoring the file system. Bug: https://issuetracker.google.com/150827486 Test: test_device.py Change-Id: Icfa4b28eb5206f1914c0c163833d070a3748c3ea
This commit is contained in:
parent
ec44d35fde
commit
5949fccc42
|
@ -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, CompressionType::Any)) {
|
||||
if (do_sync_push(apk_file, apk_dest.c_str(), false, CompressionType::Any, false)) {
|
||||
result = pm_command(argc, argv);
|
||||
delete_device_file(apk_dest);
|
||||
}
|
||||
|
|
|
@ -132,6 +132,7 @@ static void help() {
|
|||
" 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"
|
||||
" -n: dry run: push files to device without storing to the filesystem\n"
|
||||
" -z: enable compression with a specified algorithm (any, none, brotli)\n"
|
||||
" -Z: disable compression\n"
|
||||
" pull [-a] [-z ALGORITHM] [-Z] REMOTE... LOCAL\n"
|
||||
|
@ -141,6 +142,7 @@ static void help() {
|
|||
" -Z: disable compression\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"
|
||||
" -n: dry run: push files to device without storing to the filesystem\n"
|
||||
" -l: list files that would be copied, but don't copy them\n"
|
||||
" -z: enable compression with a specified algorithm (any, none, brotli)\n"
|
||||
" -Z: disable compression\n"
|
||||
|
@ -1340,7 +1342,7 @@ static CompressionType parse_compression_type(const std::string& str, bool allow
|
|||
|
||||
static void parse_push_pull_args(const char** arg, int narg, std::vector<const char*>* srcs,
|
||||
const char** dst, bool* copy_attrs, bool* sync,
|
||||
CompressionType* compression) {
|
||||
CompressionType* compression, bool* dry_run) {
|
||||
*copy_attrs = false;
|
||||
if (const char* adb_compression = getenv("ADB_COMPRESSION")) {
|
||||
*compression = parse_compression_type(adb_compression, true);
|
||||
|
@ -1364,6 +1366,8 @@ static void parse_push_pull_args(const char** arg, int narg, std::vector<const c
|
|||
--narg;
|
||||
} else if (!strcmp(*arg, "-Z")) {
|
||||
*compression = CompressionType::None;
|
||||
} else if (dry_run && !strcmp(*arg, "-n")) {
|
||||
*dry_run = true;
|
||||
} else if (!strcmp(*arg, "--sync")) {
|
||||
if (sync != nullptr) {
|
||||
*sync = true;
|
||||
|
@ -1918,20 +1922,23 @@ int adb_commandline(int argc, const char** argv) {
|
|||
} else if (!strcmp(argv[0], "push")) {
|
||||
bool copy_attrs = false;
|
||||
bool sync = false;
|
||||
bool dry_run = false;
|
||||
CompressionType compression = CompressionType::Any;
|
||||
std::vector<const char*> srcs;
|
||||
const char* dst = nullptr;
|
||||
|
||||
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, &sync, &compression);
|
||||
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, &sync, &compression,
|
||||
&dry_run);
|
||||
if (srcs.empty() || !dst) error_exit("push requires an argument");
|
||||
return do_sync_push(srcs, dst, sync, compression) ? 0 : 1;
|
||||
return do_sync_push(srcs, dst, sync, compression, dry_run) ? 0 : 1;
|
||||
} else if (!strcmp(argv[0], "pull")) {
|
||||
bool copy_attrs = false;
|
||||
CompressionType compression = CompressionType::Any;
|
||||
std::vector<const char*> srcs;
|
||||
const char* dst = ".";
|
||||
|
||||
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, nullptr, &compression);
|
||||
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, nullptr, &compression,
|
||||
nullptr);
|
||||
if (srcs.empty()) error_exit("pull requires an argument");
|
||||
return do_sync_pull(srcs, dst, copy_attrs, compression) ? 0 : 1;
|
||||
} else if (!strcmp(argv[0], "install")) {
|
||||
|
@ -1949,6 +1956,7 @@ int adb_commandline(int argc, const char** argv) {
|
|||
} else if (!strcmp(argv[0], "sync")) {
|
||||
std::string src;
|
||||
bool list_only = false;
|
||||
bool dry_run = false;
|
||||
CompressionType compression = CompressionType::Any;
|
||||
|
||||
if (const char* adb_compression = getenv("ADB_COMPRESSION"); adb_compression) {
|
||||
|
@ -1956,11 +1964,14 @@ int adb_commandline(int argc, const char** argv) {
|
|||
}
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, const_cast<char**>(argv), "lz:Z")) != -1) {
|
||||
while ((opt = getopt(argc, const_cast<char**>(argv), "lnz:Z")) != -1) {
|
||||
switch (opt) {
|
||||
case 'l':
|
||||
list_only = true;
|
||||
break;
|
||||
case 'n':
|
||||
dry_run = true;
|
||||
break;
|
||||
case 'z':
|
||||
compression = parse_compression_type(optarg, false);
|
||||
break;
|
||||
|
@ -1968,7 +1979,7 @@ int adb_commandline(int argc, const char** argv) {
|
|||
compression = CompressionType::None;
|
||||
break;
|
||||
default:
|
||||
error_exit("usage: adb sync [-l] [-z ALGORITHM] [-Z] [PARTITION]");
|
||||
error_exit("usage: adb sync [-l] [-n] [-z ALGORITHM] [-Z] [PARTITION]");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1977,7 +1988,7 @@ int adb_commandline(int argc, const char** argv) {
|
|||
} else if (optind + 1 == argc) {
|
||||
src = argv[optind];
|
||||
} else {
|
||||
error_exit("usage: adb sync [-l] [-z ALGORITHM] [-Z] [PARTITION]");
|
||||
error_exit("usage: adb sync [-l] [-n] [-z ALGORITHM] [-Z] [PARTITION]");
|
||||
}
|
||||
|
||||
std::vector<std::string> partitions{"data", "odm", "oem", "product",
|
||||
|
@ -1988,7 +1999,9 @@ 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, compression)) return 1;
|
||||
if (!do_sync_sync(src_dir, "/" + partition, list_only, compression, dry_run)) {
|
||||
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, CompressionType::Any)) {
|
||||
if (!do_sync_push(srcs, dst, sync, CompressionType::Any, false)) {
|
||||
error_exit("Failed to push fastdeploy agent to device.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -238,6 +238,7 @@ class SyncConnection {
|
|||
have_sendrecv_v2_ = CanUseFeature(features_, kFeatureSendRecv2);
|
||||
have_sendrecv_v2_brotli_ = CanUseFeature(features_, kFeatureSendRecv2Brotli);
|
||||
have_sendrecv_v2_lz4_ = CanUseFeature(features_, kFeatureSendRecv2LZ4);
|
||||
have_sendrecv_v2_dry_run_send_ = CanUseFeature(features_, kFeatureSendRecv2DryRunSend);
|
||||
fd.reset(adb_connect("sync:", &error));
|
||||
if (fd < 0) {
|
||||
Error("connect failed: %s", error.c_str());
|
||||
|
@ -264,6 +265,7 @@ class SyncConnection {
|
|||
bool HaveSendRecv2() const { return have_sendrecv_v2_; }
|
||||
bool HaveSendRecv2Brotli() const { return have_sendrecv_v2_brotli_; }
|
||||
bool HaveSendRecv2LZ4() const { return have_sendrecv_v2_lz4_; }
|
||||
bool HaveSendRecv2DryRunSend() const { return have_sendrecv_v2_dry_run_send_; }
|
||||
|
||||
// Resolve a compression type which might be CompressionType::Any to a specific compression
|
||||
// algorithm.
|
||||
|
@ -340,7 +342,7 @@ class SyncConnection {
|
|||
return WriteFdExactly(fd, buf.data(), buf.size());
|
||||
}
|
||||
|
||||
bool SendSend2(std::string_view path, mode_t mode, CompressionType compression) {
|
||||
bool SendSend2(std::string_view path, mode_t mode, CompressionType compression, bool dry_run) {
|
||||
if (path.length() > 1024) {
|
||||
Error("SendRequest failed: path too long: %zu", path.length());
|
||||
errno = ENAMETOOLONG;
|
||||
|
@ -373,6 +375,10 @@ class SyncConnection {
|
|||
LOG(FATAL) << "unexpected CompressionType::Any";
|
||||
}
|
||||
|
||||
if (dry_run) {
|
||||
msg.send_v2_setup.flags |= kSyncFlagDryRun;
|
||||
}
|
||||
|
||||
buf.resize(sizeof(SyncRequest) + path.length() + sizeof(msg.send_v2_setup));
|
||||
|
||||
void* p = buf.data();
|
||||
|
@ -541,7 +547,12 @@ class SyncConnection {
|
|||
// difference to "adb sync" performance.
|
||||
bool SendSmallFile(const std::string& path, mode_t mode, const std::string& lpath,
|
||||
const std::string& rpath, unsigned mtime, const char* data,
|
||||
size_t data_length) {
|
||||
size_t data_length, bool dry_run) {
|
||||
if (dry_run) {
|
||||
// We need to use send v2 for dry run.
|
||||
return SendLargeFile(path, mode, lpath, rpath, mtime, CompressionType::None, dry_run);
|
||||
}
|
||||
|
||||
std::string path_and_mode = android::base::StringPrintf("%s,%d", path.c_str(), mode);
|
||||
if (path_and_mode.length() > 1024) {
|
||||
Error("SendSmallFile failed: path too long: %zu", path_and_mode.length());
|
||||
|
@ -581,14 +592,20 @@ class SyncConnection {
|
|||
}
|
||||
|
||||
bool SendLargeFile(const std::string& path, mode_t mode, const std::string& lpath,
|
||||
const std::string& rpath, unsigned mtime, CompressionType compression) {
|
||||
const std::string& rpath, unsigned mtime, CompressionType compression,
|
||||
bool dry_run) {
|
||||
if (dry_run && !HaveSendRecv2DryRunSend()) {
|
||||
Error("dry-run not supported by the device");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!HaveSendRecv2()) {
|
||||
return SendLargeFileLegacy(path, mode, lpath, rpath, mtime);
|
||||
}
|
||||
|
||||
compression = ResolveCompressionType(compression);
|
||||
|
||||
if (!SendSend2(path, mode, compression)) {
|
||||
if (!SendSend2(path, mode, compression, dry_run)) {
|
||||
Error("failed to send ID_SEND_V2 message '%s': %s", path.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
@ -908,6 +925,7 @@ class SyncConnection {
|
|||
bool have_sendrecv_v2_;
|
||||
bool have_sendrecv_v2_brotli_;
|
||||
bool have_sendrecv_v2_lz4_;
|
||||
bool have_sendrecv_v2_dry_run_send_;
|
||||
|
||||
TransferLedger global_ledger_;
|
||||
TransferLedger current_ledger_;
|
||||
|
@ -989,7 +1007,8 @@ 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, CompressionType compression) {
|
||||
unsigned mtime, mode_t mode, bool sync, CompressionType compression,
|
||||
bool dry_run) {
|
||||
if (sync) {
|
||||
struct stat st;
|
||||
if (sync_lstat(sc, rpath, &st)) {
|
||||
|
@ -1010,7 +1029,7 @@ static bool sync_send(SyncConnection& sc, const std::string& lpath, const std::s
|
|||
}
|
||||
buf[data_length++] = '\0';
|
||||
|
||||
if (!sc.SendSmallFile(rpath, mode, lpath, rpath, mtime, buf, data_length)) {
|
||||
if (!sc.SendSmallFile(rpath, mode, lpath, rpath, mtime, buf, data_length, dry_run)) {
|
||||
return false;
|
||||
}
|
||||
return sc.ReadAcknowledgements();
|
||||
|
@ -1028,11 +1047,12 @@ static bool sync_send(SyncConnection& sc, const std::string& lpath, const std::s
|
|||
sc.Error("failed to read all of '%s': %s", lpath.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if (!sc.SendSmallFile(rpath, mode, lpath, rpath, mtime, data.data(), data.size())) {
|
||||
if (!sc.SendSmallFile(rpath, mode, lpath, rpath, mtime, data.data(), data.size(),
|
||||
dry_run)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!sc.SendLargeFile(rpath, mode, lpath, rpath, mtime, compression)) {
|
||||
if (!sc.SendLargeFile(rpath, mode, lpath, rpath, mtime, compression, dry_run)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1284,7 +1304,7 @@ 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,
|
||||
CompressionType compression) {
|
||||
CompressionType compression, bool dry_run) {
|
||||
sc.NewTransfer();
|
||||
|
||||
// Make sure that both directory paths end in a slash.
|
||||
|
@ -1366,7 +1386,8 @@ 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, compression)) {
|
||||
if (!sync_send(sc, ci.lpath, ci.rpath, ci.time, ci.mode, false, compression,
|
||||
dry_run)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1382,7 +1403,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,
|
||||
CompressionType compression) {
|
||||
CompressionType compression, bool dry_run) {
|
||||
SyncConnection sc;
|
||||
if (!sc.IsValid()) return false;
|
||||
|
||||
|
@ -1447,7 +1468,8 @@ 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, compression);
|
||||
success &=
|
||||
copy_local_dir_remote(sc, src_path, dst_dir, sync, false, compression, dry_run);
|
||||
continue;
|
||||
} else if (!should_push_file(st.st_mode)) {
|
||||
sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
|
||||
|
@ -1468,7 +1490,8 @@ 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, compression);
|
||||
success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode, sync, compression,
|
||||
dry_run);
|
||||
sc.ReportTransferRate(src_path, TransferDirection::push);
|
||||
}
|
||||
|
||||
|
@ -1712,11 +1735,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,
|
||||
CompressionType compression) {
|
||||
CompressionType compression, bool dry_run) {
|
||||
SyncConnection sc;
|
||||
if (!sc.IsValid()) return false;
|
||||
|
||||
bool success = copy_local_dir_remote(sc, lpath, rpath, true, list_only, compression);
|
||||
bool success = copy_local_dir_remote(sc, lpath, rpath, true, list_only, compression, dry_run);
|
||||
if (!list_only) {
|
||||
sc.ReportOverallTransferRate(TransferDirection::push);
|
||||
}
|
||||
|
|
|
@ -23,9 +23,9 @@
|
|||
|
||||
bool do_sync_ls(const char* path);
|
||||
bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync,
|
||||
CompressionType compression);
|
||||
CompressionType compression, bool dry_run);
|
||||
bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
|
||||
CompressionType compression, const char* name = nullptr);
|
||||
|
||||
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only,
|
||||
CompressionType compression);
|
||||
CompressionType compression, bool dry_run);
|
||||
|
|
|
@ -315,9 +315,12 @@ static bool handle_send_file_data(borrowed_fd s, unique_fd fd, uint32_t* timesta
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!WriteFdExactly(fd, output.data(), output.size())) {
|
||||
SendSyncFailErrno(s, "write failed");
|
||||
return false;
|
||||
// fd is -1 if the client is pushing with --dry-run.
|
||||
if (fd != -1) {
|
||||
if (!WriteFdExactly(fd, output.data(), output.size())) {
|
||||
SendSyncFailErrno(s, "write failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == DecodeResult::NeedInput) {
|
||||
|
@ -337,66 +340,65 @@ static bool handle_send_file_data(borrowed_fd s, unique_fd fd, uint32_t* timesta
|
|||
|
||||
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,
|
||||
CompressionType compression, std::vector<char>& buffer,
|
||||
CompressionType compression, bool dry_run, std::vector<char>& buffer,
|
||||
bool do_unlink) {
|
||||
int rc;
|
||||
syncmsg msg;
|
||||
unique_fd fd;
|
||||
|
||||
__android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
|
||||
|
||||
unique_fd fd(adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode));
|
||||
|
||||
if (fd < 0 && errno == ENOENT) {
|
||||
if (!secure_mkdirs(Dirname(path))) {
|
||||
SendSyncFailErrno(s, "secure_mkdirs failed");
|
||||
goto fail;
|
||||
}
|
||||
if (!dry_run) {
|
||||
__android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
|
||||
fd.reset(adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode));
|
||||
}
|
||||
if (fd < 0 && errno == EEXIST) {
|
||||
fd.reset(adb_open_mode(path, O_WRONLY | O_CLOEXEC, mode));
|
||||
}
|
||||
if (fd < 0) {
|
||||
SendSyncFailErrno(s, "couldn't create file");
|
||||
goto fail;
|
||||
} else {
|
||||
if (fchown(fd.get(), uid, gid) == -1) {
|
||||
SendSyncFailErrno(s, "fchown failed");
|
||||
goto fail;
|
||||
|
||||
if (fd < 0 && errno == ENOENT) {
|
||||
if (!secure_mkdirs(Dirname(path))) {
|
||||
SendSyncFailErrno(s, "secure_mkdirs failed");
|
||||
goto fail;
|
||||
}
|
||||
fd.reset(adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode));
|
||||
}
|
||||
if (fd < 0 && errno == EEXIST) {
|
||||
fd.reset(adb_open_mode(path, O_WRONLY | O_CLOEXEC, mode));
|
||||
}
|
||||
if (fd < 0) {
|
||||
SendSyncFailErrno(s, "couldn't create file");
|
||||
goto fail;
|
||||
} else {
|
||||
if (fchown(fd.get(), uid, gid) == -1) {
|
||||
SendSyncFailErrno(s, "fchown failed");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
// Not all filesystems support setting SELinux labels. http://b/23530370.
|
||||
selinux_android_restorecon(path, 0);
|
||||
// Not all filesystems support setting SELinux labels. http://b/23530370.
|
||||
selinux_android_restorecon(path, 0);
|
||||
#endif
|
||||
|
||||
// fchown clears the setuid bit - restore it if present.
|
||||
// Ignore the result of calling fchmod. It's not supported
|
||||
// by all filesystems, so we don't check for success. b/12441485
|
||||
fchmod(fd.get(), mode);
|
||||
}
|
||||
// fchown clears the setuid bit - restore it if present.
|
||||
// Ignore the result of calling fchmod. It's not supported
|
||||
// by all filesystems, so we don't check for success. b/12441485
|
||||
fchmod(fd.get(), mode);
|
||||
}
|
||||
|
||||
{
|
||||
rc = posix_fadvise(fd.get(), 0, 0,
|
||||
POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED);
|
||||
int 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 (!handle_send_file_data(s, std::move(fd), timestamp, compression)) {
|
||||
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));
|
||||
}
|
||||
|
||||
if (!handle_send_file_data(s, std::move(fd), timestamp, compression)) {
|
||||
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));
|
||||
|
||||
fail:
|
||||
// If there's a problem on the device, we'll send an ID_FAIL message and
|
||||
// close the socket. Unfortunately the kernel will sometimes throw that
|
||||
|
@ -435,7 +437,7 @@ extern bool handle_send_link(int s, const std::string& path,
|
|||
uint32_t* timestamp, std::vector<char>& buffer)
|
||||
__attribute__((error("no symlinks on Windows")));
|
||||
#else
|
||||
static bool handle_send_link(int s, const std::string& path, uint32_t* timestamp,
|
||||
static bool handle_send_link(int s, const std::string& path, uint32_t* timestamp, bool dry_run,
|
||||
std::vector<char>& buffer) {
|
||||
syncmsg msg;
|
||||
|
||||
|
@ -454,19 +456,21 @@ static bool handle_send_link(int s, const std::string& path, uint32_t* timestamp
|
|||
if (!ReadFdExactly(s, &buffer[0], len)) return false;
|
||||
|
||||
std::string buf_link;
|
||||
if (!android::base::Readlink(path, &buf_link) || (buf_link != &buffer[0])) {
|
||||
adb_unlink(path.c_str());
|
||||
auto ret = symlink(&buffer[0], path.c_str());
|
||||
if (ret && errno == ENOENT) {
|
||||
if (!secure_mkdirs(Dirname(path))) {
|
||||
SendSyncFailErrno(s, "secure_mkdirs failed");
|
||||
if (!dry_run) {
|
||||
if (!android::base::Readlink(path, &buf_link) || (buf_link != &buffer[0])) {
|
||||
adb_unlink(path.c_str());
|
||||
auto ret = symlink(&buffer[0], path.c_str());
|
||||
if (ret && errno == ENOENT) {
|
||||
if (!secure_mkdirs(Dirname(path))) {
|
||||
SendSyncFailErrno(s, "secure_mkdirs failed");
|
||||
return false;
|
||||
}
|
||||
ret = symlink(&buffer[0], path.c_str());
|
||||
}
|
||||
if (ret) {
|
||||
SendSyncFailErrno(s, "symlink failed");
|
||||
return false;
|
||||
}
|
||||
ret = symlink(&buffer[0], path.c_str());
|
||||
}
|
||||
if (ret) {
|
||||
SendSyncFailErrno(s, "symlink failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -487,11 +491,14 @@ 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, CompressionType compression,
|
||||
std::vector<char>& buffer) {
|
||||
bool dry_run, 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) ||
|
||||
(S_ISLNK(st.st_mode) && !S_ISLNK(mode));
|
||||
bool do_unlink = false;
|
||||
if (!dry_run) {
|
||||
do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) ||
|
||||
(S_ISLNK(st.st_mode) && !S_ISLNK(mode));
|
||||
}
|
||||
if (do_unlink) {
|
||||
adb_unlink(path.c_str());
|
||||
}
|
||||
|
@ -499,7 +506,7 @@ static bool send_impl(int s, const std::string& path, mode_t mode, CompressionTy
|
|||
bool result;
|
||||
uint32_t timestamp;
|
||||
if (S_ISLNK(mode)) {
|
||||
result = handle_send_link(s, path, ×tamp, buffer);
|
||||
result = handle_send_link(s, path, ×tamp, dry_run, buffer);
|
||||
} else {
|
||||
// Copy user permission bits to "group" and "other" permissions.
|
||||
mode &= 0777;
|
||||
|
@ -509,12 +516,12 @@ static bool send_impl(int s, const std::string& path, mode_t mode, CompressionTy
|
|||
uid_t uid = -1;
|
||||
gid_t gid = -1;
|
||||
uint64_t capabilities = 0;
|
||||
if (should_use_fs_config(path)) {
|
||||
if (should_use_fs_config(path) && !dry_run) {
|
||||
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,
|
||||
compression, buffer, do_unlink);
|
||||
compression, dry_run, buffer, do_unlink);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
|
@ -547,7 +554,7 @@ static bool do_send_v1(int s, const std::string& spec, std::vector<char>& buffer
|
|||
return false;
|
||||
}
|
||||
|
||||
return send_impl(s, path, mode, CompressionType::None, buffer);
|
||||
return send_impl(s, path, mode, CompressionType::None, false, buffer);
|
||||
}
|
||||
|
||||
static bool do_send_v2(int s, const std::string& path, std::vector<char>& buffer) {
|
||||
|
@ -561,6 +568,7 @@ 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 dry_run = false;
|
||||
std::optional<CompressionType> compression;
|
||||
|
||||
uint32_t orig_flags = msg.send_v2_setup.flags;
|
||||
|
@ -582,6 +590,10 @@ static bool do_send_v2(int s, const std::string& path, std::vector<char>& buffer
|
|||
}
|
||||
compression = CompressionType::LZ4;
|
||||
}
|
||||
if (msg.send_v2_setup.flags & kSyncFlagDryRun) {
|
||||
msg.send_v2_setup.flags &= ~kSyncFlagDryRun;
|
||||
dry_run = true;
|
||||
}
|
||||
|
||||
if (msg.send_v2_setup.flags) {
|
||||
SendSyncFail(s, android::base::StringPrintf("unknown flags: %d", msg.send_v2_setup.flags));
|
||||
|
@ -590,7 +602,7 @@ static bool do_send_v2(int s, const std::string& path, std::vector<char>& buffer
|
|||
|
||||
errno = 0;
|
||||
return send_impl(s, path, msg.send_v2_setup.mode, compression.value_or(CompressionType::None),
|
||||
buffer);
|
||||
dry_run, buffer);
|
||||
}
|
||||
|
||||
static bool recv_impl(borrowed_fd s, const char* path, CompressionType compression,
|
||||
|
|
|
@ -93,6 +93,7 @@ enum SyncFlag : uint32_t {
|
|||
kSyncFlagNone = 0,
|
||||
kSyncFlagBrotli = 1,
|
||||
kSyncFlagLZ4 = 2,
|
||||
kSyncFlagDryRun = 0x8000'0000U,
|
||||
};
|
||||
|
||||
enum class CompressionType {
|
||||
|
|
|
@ -1268,6 +1268,59 @@ class FileOperationsTest:
|
|||
if temp_dir is not None:
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
def test_push_dry_run_nonexistent_file(self):
|
||||
"""Push with dry run."""
|
||||
|
||||
for file_size in [8, 1024 * 1024]:
|
||||
try:
|
||||
device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'push_dry_run')
|
||||
device_file = posixpath.join(device_dir, 'file')
|
||||
|
||||
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
|
||||
self.device.shell(['mkdir', '-p', device_dir])
|
||||
|
||||
host_dir = tempfile.mkdtemp()
|
||||
host_file = posixpath.join(host_dir, 'file')
|
||||
|
||||
with open(host_file, "w") as f:
|
||||
f.write('x' * file_size)
|
||||
|
||||
self.device._simple_call(['push', '-n', host_file, device_file])
|
||||
rc, _, _ = self.device.shell_nocheck(['[', '-e', device_file, ']'])
|
||||
self.assertNotEqual(0, rc)
|
||||
|
||||
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
|
||||
finally:
|
||||
if host_dir is not None:
|
||||
shutil.rmtree(host_dir)
|
||||
|
||||
def test_push_dry_run_existent_file(self):
|
||||
"""Push with dry run."""
|
||||
|
||||
for file_size in [8, 1024 * 1024]:
|
||||
try:
|
||||
device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'push_dry_run')
|
||||
device_file = posixpath.join(device_dir, 'file')
|
||||
|
||||
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
|
||||
self.device.shell(['mkdir', '-p', device_dir])
|
||||
self.device.shell(['echo', 'foo', '>', device_file])
|
||||
|
||||
host_dir = tempfile.mkdtemp()
|
||||
host_file = posixpath.join(host_dir, 'file')
|
||||
|
||||
with open(host_file, "w") as f:
|
||||
f.write('x' * file_size)
|
||||
|
||||
self.device._simple_call(['push', '-n', host_file, device_file])
|
||||
stdout, stderr = self.device.shell(['cat', device_file])
|
||||
self.assertEqual(stdout.strip(), "foo")
|
||||
|
||||
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
|
||||
finally:
|
||||
if host_dir is not None:
|
||||
shutil.rmtree(host_dir)
|
||||
|
||||
def test_unicode_paths(self):
|
||||
"""Ensure that we can support non-ASCII paths, even on Windows."""
|
||||
name = u'로보카 폴리'
|
||||
|
|
|
@ -85,6 +85,7 @@ const char* const kFeatureTrackApp = "track_app";
|
|||
const char* const kFeatureSendRecv2 = "sendrecv_v2";
|
||||
const char* const kFeatureSendRecv2Brotli = "sendrecv_v2_brotli";
|
||||
const char* const kFeatureSendRecv2LZ4 = "sendrecv_v2_lz4";
|
||||
const char* const kFeatureSendRecv2DryRunSend = "sendrecv_v2_dry_run_send";
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -1185,6 +1186,7 @@ const FeatureSet& supported_features() {
|
|||
kFeatureSendRecv2,
|
||||
kFeatureSendRecv2Brotli,
|
||||
kFeatureSendRecv2LZ4,
|
||||
kFeatureSendRecv2DryRunSend,
|
||||
// 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.
|
||||
|
|
|
@ -90,6 +90,8 @@ extern const char* const kFeatureSendRecv2;
|
|||
extern const char* const kFeatureSendRecv2Brotli;
|
||||
// adbd supports LZ4 for send/recv v2.
|
||||
extern const char* const kFeatureSendRecv2LZ4;
|
||||
// adbd supports dry-run send for send/recv v2.
|
||||
extern const char* const kFeatureSendRecv2DryRunSend;
|
||||
|
||||
TransportId NextTransportId();
|
||||
|
||||
|
|
Loading…
Reference in New Issue