Merge "init_kill_services_test: reboot device first."
diff --git a/adb/Android.bp b/adb/Android.bp
index 12d9a14..6386a78 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -470,6 +470,7 @@
"libadbd_core",
"libbrotli",
"libdiagnose_usb",
+ "liblz4",
],
shared_libs: [
@@ -571,6 +572,7 @@
"libbrotli",
"libcutils_sockets",
"libdiagnose_usb",
+ "liblz4",
"libmdnssd",
],
@@ -605,6 +607,7 @@
"libadbd_services",
"libasyncio",
"libcap",
+ "liblz4",
"libminijail",
"libssl",
],
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 6c03f74..0518e9d 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1071,19 +1071,25 @@
return 0;
}
+static bool g_reject_kill_server = false;
+void adb_set_reject_kill_server(bool value) {
+ g_reject_kill_server = value;
+}
+
HostRequestResult handle_host_request(std::string_view service, TransportType type,
const char* serial, TransportId transport_id, int reply_fd,
asocket* s) {
if (service == "kill") {
- fprintf(stderr, "adb server killed by remote request\n");
- fflush(stdout);
+ if (g_reject_kill_server) {
+ LOG(WARNING) << "adb server ignoring kill-server";
+ SendFail(reply_fd, "kill-server rejected by remote server");
+ } else {
+ fprintf(stderr, "adb server killed by remote request\n");
+ SendOkay(reply_fd);
- // Send a reply even though we don't read it anymore, so that old versions
- // of adb that do read it don't spew error messages.
- SendOkay(reply_fd);
-
- // Rely on process exit to close the socket for us.
- exit(0);
+ // Rely on process exit to close the socket for us.
+ exit(0);
+ }
}
LOG(DEBUG) << "handle_host_request(" << service << ")";
diff --git a/adb/adb.h b/adb/adb.h
index 7bc60fc..476ed9b 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef __ADB_H
-#define __ADB_H
+#pragma once
#include <limits.h>
#include <stdint.h>
@@ -237,6 +236,7 @@
void parse_banner(const std::string&, atransport* t);
+#if ADB_HOST
// On startup, the adb server needs to wait until all of the connected devices are ready.
// To do this, we need to know when the scan has identified all of the potential new transports, and
// when each transport becomes ready.
@@ -250,6 +250,12 @@
// Wait until device scan has completed and every transport is ready, or a timeout elapses.
void adb_wait_for_device_initialization();
+#endif // ADB_HOST
+
+#if ADB_HOST
+// When ssh-forwarding to a remote adb server, kill-server is almost never what you actually want,
+// and unfortunately, many other tools issue it. This adds a knob to reject kill-servers.
+void adb_set_reject_kill_server(bool reject);
+#endif
void usb_init();
-#endif
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index f724cb5..c859d75 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -204,9 +204,25 @@
return false;
}
- // The server might send OKAY, so consume that.
char buf[4];
- ReadFdExactly(fd.get(), buf, 4);
+ if (!ReadFdExactly(fd.get(), buf, 4)) {
+ fprintf(stderr, "error: failed to read response from server\n");
+ return false;
+ }
+
+ if (memcmp(buf, "OKAY", 4) == 0) {
+ // Nothing to do.
+ } else if (memcmp(buf, "FAIL", 4) == 0) {
+ std::string output, error;
+ if (!ReadProtocolString(fd.get(), &output, &error)) {
+ fprintf(stderr, "error: %s\n", error.c_str());
+ return false;
+ }
+
+ fprintf(stderr, "error: %s\n", output.c_str());
+ return false;
+ }
+
// Now that no more data is expected, wait for socket orderly shutdown or error, indicating
// server death.
ReadOrderlyShutdown(fd.get());
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 092a866..da3154e 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -290,7 +290,7 @@
}
}
- 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, false)) {
result = pm_command(argc, argv);
delete_device_file(apk_dest);
}
diff --git a/adb/client/bugreport.cpp b/adb/client/bugreport.cpp
index ab93f7d..e162aaa 100644
--- a/adb/client/bugreport.cpp
+++ b/adb/client/bugreport.cpp
@@ -282,5 +282,5 @@
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);
}
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 04b250d..ceb21d5 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -129,20 +129,22 @@
" 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"
+ " -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 [-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"
+ " -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\n"
+ " -z: enable compression with a specified algorithm (any, none, brotli)\n"
" -Z: disable compression\n"
"\n"
"shell:\n"
@@ -1314,12 +1316,36 @@
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;
+ } else if (str == "lz4") {
+ return CompressionType::LZ4;
+ }
+
+ 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, bool* dry_run) {
*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 +1359,15 @@
} 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 (dry_run && !strcmp(*arg, "-n")) {
+ *dry_run = true;
} else if (!strcmp(*arg, "--sync")) {
if (sync != nullptr) {
*sync = true;
@@ -1894,22 +1922,25 @@
} else if (!strcmp(argv[0], "push")) {
bool copy_attrs = false;
bool sync = false;
- bool compressed = true;
+ 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, &compressed);
+ 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, compressed) ? 0 : 1;
+ return do_sync_push(srcs, dst, sync, compression, dry_run) ? 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, ©_attrs, nullptr, &compressed);
+ 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, 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 +1956,30 @@
} else if (!strcmp(argv[0], "sync")) {
std::string src;
bool list_only = false;
- bool compressed = true;
+ bool dry_run = false;
+ 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), "lnz:Z")) != -1) {
switch (opt) {
case 'l':
list_only = true;
break;
+ case 'n':
+ dry_run = 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] [-n] [-z ALGORITHM] [-Z] [PARTITION]");
}
}
@@ -1954,7 +1988,7 @@
} else if (optind + 1 == argc) {
src = argv[optind];
} else {
- error_exit("usage: adb sync [-lzZ] [PARTITION]");
+ error_exit("usage: adb sync [-l] [-n] [-z ALGORITHM] [-Z] [PARTITION]");
}
std::vector<std::string> partitions{"data", "odm", "oem", "product",
@@ -1965,7 +1999,9 @@
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, dry_run)) {
+ return 1;
+ }
}
}
if (!found) error_exit("don't know how to sync %s partition", src.c_str());
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index de82e14..bc4b91b 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -112,7 +112,7 @@
// 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, false)) {
error_exit("Failed to push fastdeploy agent to device.");
}
}
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index 2ed58b2..6816734 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -34,6 +34,7 @@
#include <memory>
#include <sstream>
#include <string>
+#include <variant>
#include <vector>
#include "sysdeps.h"
@@ -236,6 +237,8 @@
have_ls_v2_ = CanUseFeature(features_, kFeatureLs2);
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());
@@ -261,6 +264,22 @@
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.
+ CompressionType ResolveCompressionType(CompressionType compression) const {
+ if (compression == CompressionType::Any) {
+ if (HaveSendRecv2LZ4()) {
+ return CompressionType::LZ4;
+ } else if (HaveSendRecv2Brotli()) {
+ return CompressionType::Brotli;
+ }
+ return CompressionType::None;
+ }
+ return compression;
+ }
const FeatureSet& Features() const { return features_; }
@@ -323,7 +342,7 @@
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, bool dry_run) {
if (path.length() > 1024) {
Error("SendRequest failed: path too long: %zu", path.length());
errno = ENAMETOOLONG;
@@ -339,7 +358,26 @@
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::LZ4:
+ msg.send_v2_setup.flags = kSyncFlagLZ4;
+ break;
+
+ case CompressionType::Any:
+ 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));
@@ -352,7 +390,7 @@
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 +405,22 @@
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::LZ4:
+ msg.recv_v2_setup.flags |= kSyncFlagLZ4;
+ break;
+
+ case CompressionType::Any:
+ LOG(FATAL) << "unexpected CompressionType::Any";
+ }
buf.resize(sizeof(SyncRequest) + path.length() + sizeof(msg.recv_v2_setup));
@@ -494,7 +547,12 @@
// 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());
@@ -533,9 +591,21 @@
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,
+ 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, dry_run)) {
Error("failed to send ID_SEND_V2 message '%s': %s", path.c_str(), strerror(errno));
return false;
}
@@ -558,7 +628,25 @@
syncsendbuf sbuf;
sbuf.id = ID_DATA;
- BrotliEncoder<SYNC_DATA_MAX> encoder;
+ std::variant<std::monostate, NullEncoder, BrotliEncoder, LZ4Encoder> 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::LZ4:
+ encoder = &encoder_storage.emplace<LZ4Encoder>(SYNC_DATA_MAX);
+ break;
+
+ case CompressionType::Any:
+ LOG(FATAL) << "unexpected CompressionType::Any";
+ }
+
bool sending = true;
while (sending) {
Block input(SYNC_DATA_MAX);
@@ -569,10 +657,10 @@
}
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 +668,7 @@
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 +698,8 @@
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(),
@@ -840,6 +924,8 @@
bool have_ls_v2_;
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_;
@@ -921,7 +1007,8 @@
}
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,
+ bool dry_run) {
if (sync) {
struct stat st;
if (sync_lstat(sc, rpath, &st)) {
@@ -942,7 +1029,7 @@
}
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();
@@ -960,11 +1047,12 @@
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, compressed)) {
+ if (!sc.SendLargeFile(rpath, mode, lpath, rpath, mtime, compression, dry_run)) {
return false;
}
}
@@ -1027,8 +1115,10 @@
}
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 +1130,28 @@
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, LZ4Decoder> 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::LZ4:
+ decoder = &decoder_storage.emplace<LZ4Decoder>(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 +1159,32 @@
}
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");
@@ -1093,8 +1201,7 @@
}
bytes_copied += output.size();
-
- sc.RecordBytesTransferred(msg.data.size);
+ sc.RecordBytesTransferred(output.size());
sc.ReportProgress(name != nullptr ? name : rpath, bytes_copied, expected_size);
if (result == DecodeResult::NeedInput) {
@@ -1102,33 +1209,19 @@
} 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 +1303,8 @@
}
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, bool dry_run) {
sc.NewTransfer();
// Make sure that both directory paths end in a slash.
@@ -1292,7 +1386,8 @@
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,
+ dry_run)) {
return false;
}
}
@@ -1308,7 +1403,7 @@
}
bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync,
- bool compressed) {
+ CompressionType compression, bool dry_run) {
SyncConnection sc;
if (!sc.IsValid()) return false;
@@ -1373,7 +1468,8 @@
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, 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);
@@ -1394,7 +1490,8 @@
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,
+ dry_run);
sc.ReportTransferRate(src_path, TransferDirection::push);
}
@@ -1480,7 +1577,7 @@
}
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 +1607,7 @@
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 +1625,7 @@
}
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 +1699,7 @@
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 +1718,7 @@
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 +1735,11 @@
}
bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only,
- bool compressed) {
+ CompressionType compression, bool dry_run) {
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, dry_run);
if (!list_only) {
sc.ReportOverallTransferRate(TransferDirection::push);
}
diff --git a/adb/client/file_sync_client.h b/adb/client/file_sync_client.h
index de3f192..cb8ca93 100644
--- a/adb/client/file_sync_client.h
+++ b/adb/client/file_sync_client.h
@@ -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 dry_run);
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, bool dry_run);
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 4a9eadc..05e210f 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -105,7 +105,12 @@
fdevent_run_on_main_thread([]() { exit(0); });
});
- char* leak = getenv("ADB_LEAK");
+ const char* reject_kill_server = getenv("ADB_REJECT_KILL_SERVER");
+ if (reject_kill_server && strcmp(reject_kill_server, "1") == 0) {
+ adb_set_reject_kill_server(true);
+ }
+
+ const char* leak = getenv("ADB_LEAK");
if (leak && strcmp(leak, "1") == 0) {
intentionally_leak();
}
diff --git a/adb/compression_utils.h b/adb/compression_utils.h
index c445095..a0c48a2 100644
--- a/adb/compression_utils.h
+++ b/adb/compression_utils.h
@@ -16,10 +16,15 @@
#pragma once
+#include <algorithm>
+#include <memory>
#include <span>
+#include <android-base/logging.h>
+
#include <brotli/decode.h>
#include <brotli/encode.h>
+#include <lz4frame.h>
#include "types.h"
@@ -37,15 +42,103 @@
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 +156,8 @@
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 +171,29 @@
}
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 +211,13 @@
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,9 +226,158 @@
}
private:
- bool finished_ = false;
- IOVector input_buffer_;
Block output_block_;
size_t output_bytes_left_;
std::unique_ptr<BrotliEncoderState, void (*)(BrotliEncoderState*)> encoder_;
};
+
+struct LZ4Decoder final : public Decoder {
+ explicit LZ4Decoder(std::span<char> output_buffer)
+ : Decoder(output_buffer), decoder_(nullptr, nullptr) {
+ LZ4F_dctx* dctx;
+ if (LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION) != 0) {
+ LOG(FATAL) << "failed to initialize LZ4 decompression context";
+ }
+ decoder_ = std::unique_ptr<LZ4F_dctx, decltype(&LZ4F_freeDecompressionContext)>(
+ dctx, LZ4F_freeDecompressionContext);
+ }
+
+ DecodeResult Decode(std::span<char>* output) final {
+ size_t available_in = input_buffer_.front_size();
+ const char* next_in = input_buffer_.front_data();
+
+ size_t available_out = output_buffer_.size();
+ char* next_out = output_buffer_.data();
+
+ size_t rc = LZ4F_decompress(decoder_.get(), next_out, &available_out, next_in,
+ &available_in, nullptr);
+ if (LZ4F_isError(rc)) {
+ LOG(ERROR) << "LZ4F_decompress failed: " << LZ4F_getErrorName(rc);
+ return DecodeResult::Error;
+ }
+
+ input_buffer_.drop_front(available_in);
+
+ if (rc == 0) {
+ if (!input_buffer_.empty()) {
+ LOG(ERROR) << "LZ4 stream hit end before reading all data";
+ return DecodeResult::Error;
+ }
+ lz4_done_ = true;
+ }
+
+ *output = std::span<char>(output_buffer_.data(), available_out);
+
+ if (finished_) {
+ return input_buffer_.empty() && lz4_done_ ? DecodeResult::Done
+ : DecodeResult::MoreOutput;
+ }
+
+ return DecodeResult::NeedInput;
+ }
+
+ private:
+ bool lz4_done_ = false;
+ std::unique_ptr<LZ4F_dctx, LZ4F_errorCode_t (*)(LZ4F_dctx*)> decoder_;
+};
+
+struct LZ4Encoder final : public Encoder {
+ explicit LZ4Encoder(size_t output_block_size)
+ : Encoder(output_block_size), encoder_(nullptr, nullptr) {
+ LZ4F_cctx* cctx;
+ if (LZ4F_createCompressionContext(&cctx, LZ4F_VERSION) != 0) {
+ LOG(FATAL) << "failed to initialize LZ4 compression context";
+ }
+ encoder_ = std::unique_ptr<LZ4F_cctx, decltype(&LZ4F_freeCompressionContext)>(
+ cctx, LZ4F_freeCompressionContext);
+ Block header(LZ4F_HEADER_SIZE_MAX);
+ size_t rc = LZ4F_compressBegin(encoder_.get(), header.data(), header.size(), nullptr);
+ if (LZ4F_isError(rc)) {
+ LOG(FATAL) << "LZ4F_compressBegin failed: %s", LZ4F_getErrorName(rc);
+ }
+ header.resize(rc);
+ output_buffer_.append(std::move(header));
+ }
+
+ // As an optimization, only emit a block if we have an entire output block ready, or we're done.
+ bool OutputReady() const {
+ return output_buffer_.size() >= output_block_size_ || lz4_finalized_;
+ }
+
+ // TODO: Switch the output type to IOVector to remove a copy?
+ EncodeResult Encode(Block* output) final {
+ size_t available_in = input_buffer_.front_size();
+ const char* next_in = input_buffer_.front_data();
+
+ // LZ4 makes no guarantees about being able to recover from trying to compress with an
+ // insufficiently large output buffer. LZ4F_compressBound tells us how much buffer we
+ // need to compress a given number of bytes, but the smallest value seems to be bigger
+ // than SYNC_DATA_MAX, so we need to buffer ourselves.
+
+ // Input size chosen to be a local maximum for LZ4F_compressBound (i.e. the block size).
+ constexpr size_t max_input_size = 65536;
+ const size_t encode_block_size = LZ4F_compressBound(max_input_size, nullptr);
+
+ if (available_in != 0) {
+ if (lz4_finalized_) {
+ LOG(ERROR) << "LZ4Encoder received data after Finish?";
+ return EncodeResult::Error;
+ }
+
+ available_in = std::min(available_in, max_input_size);
+
+ Block encode_block(encode_block_size);
+ size_t available_out = encode_block.capacity();
+ char* next_out = encode_block.data();
+
+ size_t rc = LZ4F_compressUpdate(encoder_.get(), next_out, available_out, next_in,
+ available_in, nullptr);
+ if (LZ4F_isError(rc)) {
+ LOG(ERROR) << "LZ4F_compressUpdate failed: " << LZ4F_getErrorName(rc);
+ return EncodeResult::Error;
+ }
+
+ input_buffer_.drop_front(available_in);
+
+ available_out -= rc;
+ next_out += rc;
+
+ encode_block.resize(encode_block_size - available_out);
+ output_buffer_.append(std::move(encode_block));
+ }
+
+ if (finished_ && !lz4_finalized_) {
+ lz4_finalized_ = true;
+
+ Block final_block(encode_block_size + 4);
+ size_t rc = LZ4F_compressEnd(encoder_.get(), final_block.data(), final_block.size(),
+ nullptr);
+ if (LZ4F_isError(rc)) {
+ LOG(ERROR) << "LZ4F_compressEnd failed: " << LZ4F_getErrorName(rc);
+ return EncodeResult::Error;
+ }
+
+ final_block.resize(rc);
+ output_buffer_.append(std::move(final_block));
+ }
+
+ if (OutputReady()) {
+ size_t len = std::min(output_block_size_, output_buffer_.size());
+ *output = output_buffer_.take_front(len).coalesce();
+ } else {
+ output->clear();
+ }
+
+ if (lz4_finalized_ && output_buffer_.empty()) {
+ return EncodeResult::Done;
+ } else if (OutputReady()) {
+ return EncodeResult::MoreOutput;
+ }
+ return EncodeResult::NeedInput;
+ }
+
+ private:
+ bool lz4_finalized_ = false;
+ std::unique_ptr<LZ4F_cctx, LZ4F_errorCode_t (*)(LZ4F_cctx*)> encoder_;
+ IOVector output_buffer_;
+};
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index 5ccddea..d58131e 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -35,6 +35,7 @@
#include <optional>
#include <span>
#include <string>
+#include <variant>
#include <vector>
#include <android-base/file.h>
@@ -266,37 +267,60 @@
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, LZ4Decoder> 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::LZ4:
+ decoder = &decoder_storage.emplace<LZ4Decoder>(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;
}
- 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) {
@@ -304,7 +328,7 @@
} else if (result == DecodeResult::MoreOutput) {
continue;
} else if (result == DecodeResult::Done) {
- break;
+ return true;
} else {
LOG(FATAL) << "invalid DecodeResult: " << static_cast<int>(result);
}
@@ -314,102 +338,67 @@
__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;
+ gid_t gid, uint64_t capabilities, mode_t mode,
+ CompressionType compression, bool dry_run, std::vector<char>& buffer,
+ bool do_unlink) {
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));
}
-
- 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) {
- 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
@@ -448,7 +437,7 @@
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;
@@ -467,19 +456,21 @@
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;
}
}
@@ -499,12 +490,15 @@
}
#endif
-static bool send_impl(int s, const std::string& path, mode_t mode, bool compressed,
- std::vector<char>& buffer) {
+static bool send_impl(int s, const std::string& path, mode_t mode, CompressionType compression,
+ 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());
}
@@ -512,7 +506,7 @@
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;
@@ -522,12 +516,12 @@
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,
- compressed, buffer, do_unlink);
+ compression, dry_run, buffer, do_unlink);
}
if (!result) {
@@ -560,7 +554,7 @@
return false;
}
- return send_impl(s, path, mode, false, 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) {
@@ -574,45 +568,80 @@
PLOG(ERROR) << "failed to read send_v2 setup packet";
}
- bool compressed = false;
+ bool dry_run = false;
+ std::optional<CompressionType> compression;
+
+ uint32_t orig_flags = msg.send_v2_setup.flags;
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",
+ orig_flags));
+ return false;
+ }
+ compression = CompressionType::Brotli;
}
+ if (msg.send_v2_setup.flags & kSyncFlagLZ4) {
+ msg.send_v2_setup.flags &= ~kSyncFlagLZ4;
+ if (compression) {
+ SendSyncFail(s, android::base::StringPrintf("multiple compression flags received: %d",
+ orig_flags));
+ return false;
+ }
+ 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));
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),
+ dry_run, 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, LZ4Encoder> 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::LZ4:
+ encoder = &encoder_storage.emplace<LZ4Encoder>(SYNC_DATA_MAX);
+ break;
+
+ case CompressionType::Any:
+ LOG(FATAL) << "unexpected CompressionType::Any";
+ }
bool sending = true;
while (sending) {
@@ -624,15 +653,15 @@
}
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 +686,13 @@
}
}
- 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 +706,33 @@
PLOG(ERROR) << "failed to read recv_v2 setup packet";
}
- bool compressed = false;
+ std::optional<CompressionType> compression;
+ uint32_t orig_flags = msg.recv_v2_setup.flags;
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",
+ orig_flags));
+ return false;
+ }
+ compression = CompressionType::Brotli;
}
+ if (msg.recv_v2_setup.flags & kSyncFlagLZ4) {
+ msg.recv_v2_setup.flags &= ~kSyncFlagLZ4;
+ if (compression) {
+ SendSyncFail(s, android::base::StringPrintf("multiple compression flags received: %d",
+ orig_flags));
+ return false;
+ }
+ compression = CompressionType::LZ4;
+ }
+
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) {
diff --git a/adb/daemon/usb_ffs.cpp b/adb/daemon/usb_ffs.cpp
index 7bd611b..e538ca8 100644
--- a/adb/daemon/usb_ffs.cpp
+++ b/adb/daemon/usb_ffs.cpp
@@ -300,7 +300,6 @@
}
// Signal only when writing the descriptors to ffs
android::base::SetProperty("sys.usb.ffs.ready", "1");
- *out_control = std::move(control);
}
bulk_out.reset(adb_open(USB_FFS_ADB_OUT, O_RDONLY));
@@ -315,6 +314,7 @@
return false;
}
+ *out_control = std::move(control);
*out_bulk_in = std::move(bulk_in);
*out_bulk_out = std::move(bulk_out);
return true;
diff --git a/adb/file_sync_protocol.h b/adb/file_sync_protocol.h
index fd9a516..8f8f85f 100644
--- a/adb/file_sync_protocol.h
+++ b/adb/file_sync_protocol.h
@@ -92,6 +92,15 @@
enum SyncFlag : uint32_t {
kSyncFlagNone = 0,
kSyncFlagBrotli = 1,
+ kSyncFlagLZ4 = 2,
+ kSyncFlagDryRun = 0x8000'0000U,
+};
+
+enum class CompressionType {
+ None,
+ Any,
+ Brotli,
+ LZ4,
};
// send_v1 sent the path in a buffer, followed by a comma and the mode as a string.
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 9a879b5..7326ab1 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -14,11 +14,11 @@
* limitations under the License.
*/
+#pragma once
+
/* this file contains system-dependent definitions used by ADB
* they're related to threads, sockets and file descriptors
*/
-#ifndef _ADB_SYSDEPS_H
-#define _ADB_SYSDEPS_H
#ifdef __CYGWIN__
# undef _WIN32
@@ -42,6 +42,12 @@
#include "sysdeps/network.h"
#include "sysdeps/stat.h"
+#if defined(__APPLE__)
+static inline void* mempcpy(void* dst, const void* src, size_t n) {
+ return static_cast<char*>(memcpy(dst, src, n)) + n;
+}
+#endif
+
#ifdef _WIN32
// Clang-only nullability specifiers
@@ -70,13 +76,13 @@
#define OS_PATH_SEPARATOR_STR "\\"
#define ENV_PATH_SEPARATOR_STR ";"
-static __inline__ bool adb_is_separator(char c) {
+static inline bool adb_is_separator(char c) {
return c == '\\' || c == '/';
}
extern int adb_thread_setname(const std::string& name);
-static __inline__ void close_on_exec(borrowed_fd fd) {
+static inline void close_on_exec(borrowed_fd fd) {
/* nothing really */
}
@@ -107,7 +113,7 @@
extern int adb_getlogin_r(char* buf, size_t bufsize);
// See the comments for the !defined(_WIN32) version of unix_close().
-static __inline__ int unix_close(int fd) {
+static inline int unix_close(int fd) {
return close(fd);
}
#undef close
@@ -117,7 +123,7 @@
extern int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len);
// See the comments for the !defined(_WIN32) version of unix_read().
-static __inline__ int unix_read(borrowed_fd fd, void* buf, size_t len) {
+static inline int unix_read(borrowed_fd fd, void* buf, size_t len) {
return TEMP_FAILURE_RETRY(unix_read_interruptible(fd, buf, len));
}
@@ -128,7 +134,7 @@
#define pread ___xxx_pread
// See the comments for the !defined(_WIN32) version of unix_write().
-static __inline__ int unix_write(borrowed_fd fd, const void* buf, size_t len) {
+static inline int unix_write(borrowed_fd fd, const void* buf, size_t len) {
return write(fd.get(), buf, len);
}
#undef write
@@ -138,14 +144,14 @@
#define pwrite ___xxx_pwrite
// See the comments for the !defined(_WIN32) version of unix_lseek().
-static __inline__ int unix_lseek(borrowed_fd fd, int pos, int where) {
+static inline int unix_lseek(borrowed_fd fd, int pos, int where) {
return lseek(fd.get(), pos, where);
}
#undef lseek
#define lseek ___xxx_lseek
// See the comments for the !defined(_WIN32) version of adb_open_mode().
-static __inline__ int adb_open_mode(const char* path, int options, int mode) {
+static inline int adb_open_mode(const char* path, int options, int mode) {
return adb_open(path, options);
}
@@ -202,7 +208,7 @@
extern int adb_poll(adb_pollfd* fds, size_t nfds, int timeout);
#define poll ___xxx_poll
-static __inline__ int adb_is_absolute_host_path(const char* path) {
+static inline int adb_is_absolute_host_path(const char* path) {
return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
}
@@ -386,15 +392,15 @@
#define OS_PATH_SEPARATOR_STR "/"
#define ENV_PATH_SEPARATOR_STR ":"
-static __inline__ bool adb_is_separator(char c) {
+static inline bool adb_is_separator(char c) {
return c == '/';
}
-static __inline__ int get_fd_flags(borrowed_fd fd) {
+static inline int get_fd_flags(borrowed_fd fd) {
return fcntl(fd.get(), F_GETFD);
}
-static __inline__ void close_on_exec(borrowed_fd fd) {
+static inline void close_on_exec(borrowed_fd fd) {
int flags = get_fd_flags(fd);
if (flags >= 0 && (flags & FD_CLOEXEC) == 0) {
fcntl(fd.get(), F_SETFD, flags | FD_CLOEXEC);
@@ -410,7 +416,7 @@
// by unix_read(), unix_write(), unix_close()). Also, the C Runtime has
// configurable CR/LF translation which defaults to text mode, but is settable
// with _setmode().
-static __inline__ int unix_open(std::string_view path, int options, ...) {
+static inline int unix_open(std::string_view path, int options, ...) {
std::string zero_terminated(path.begin(), path.end());
if ((options & O_CREAT) == 0) {
return TEMP_FAILURE_RETRY(open(zero_terminated.c_str(), options));
@@ -426,7 +432,7 @@
// Similar to the two-argument adb_open(), but takes a mode parameter for file
// creation. See adb_open() for more info.
-static __inline__ int adb_open_mode(const char* pathname, int options, int mode) {
+static inline int adb_open_mode(const char* pathname, int options, int mode) {
return TEMP_FAILURE_RETRY(open(pathname, options, mode));
}
@@ -437,7 +443,7 @@
// sysdeps_win32.cpp) uses Windows native file I/O and bypasses the C Runtime
// and its CR/LF translation. The returned file descriptor should be used with
// adb_read(), adb_write(), adb_close(), etc.
-static __inline__ int adb_open(const char* pathname, int options) {
+static inline int adb_open(const char* pathname, int options) {
int fd = TEMP_FAILURE_RETRY(open(pathname, options));
if (fd < 0) return -1;
close_on_exec(fd);
@@ -446,7 +452,7 @@
#undef open
#define open ___xxx_open
-static __inline__ int adb_shutdown(borrowed_fd fd, int direction = SHUT_RDWR) {
+static inline int adb_shutdown(borrowed_fd fd, int direction = SHUT_RDWR) {
return shutdown(fd.get(), direction);
}
@@ -456,7 +462,7 @@
// Closes a file descriptor that came from adb_open() or adb_open_mode(), but
// not designed to take a file descriptor from unix_open(). See the comments
// for adb_open() for more info.
-__inline__ int adb_close(int fd) {
+inline int adb_close(int fd) {
return close(fd);
}
#undef close
@@ -465,23 +471,23 @@
// On Windows, ADB has an indirection layer for file descriptors. If we get a
// Win32 SOCKET object from an external library, we have to map it in to that
// indirection layer, which this does.
-__inline__ int adb_register_socket(int s) {
+inline int adb_register_socket(int s) {
return s;
}
-static __inline__ int adb_gethostname(char* name, size_t len) {
+static inline int adb_gethostname(char* name, size_t len) {
return gethostname(name, len);
}
-static __inline__ int adb_getlogin_r(char* buf, size_t bufsize) {
+static inline int adb_getlogin_r(char* buf, size_t bufsize) {
return getlogin_r(buf, bufsize);
}
-static __inline__ int adb_read(borrowed_fd fd, void* buf, size_t len) {
+static inline int adb_read(borrowed_fd fd, void* buf, size_t len) {
return TEMP_FAILURE_RETRY(read(fd.get(), buf, len));
}
-static __inline__ int adb_pread(borrowed_fd fd, void* buf, size_t len, off64_t offset) {
+static inline int adb_pread(borrowed_fd fd, void* buf, size_t len, off64_t offset) {
#if defined(__APPLE__)
return TEMP_FAILURE_RETRY(pread(fd.get(), buf, len, offset));
#else
@@ -490,7 +496,7 @@
}
// Like unix_read(), but does not handle EINTR.
-static __inline__ int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len) {
+static inline int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len) {
return read(fd.get(), buf, len);
}
@@ -499,11 +505,11 @@
#undef pread
#define pread ___xxx_pread
-static __inline__ int adb_write(borrowed_fd fd, const void* buf, size_t len) {
+static inline int adb_write(borrowed_fd fd, const void* buf, size_t len) {
return TEMP_FAILURE_RETRY(write(fd.get(), buf, len));
}
-static __inline__ int adb_pwrite(int fd, const void* buf, size_t len, off64_t offset) {
+static inline int adb_pwrite(int fd, const void* buf, size_t len, off64_t offset) {
#if defined(__APPLE__)
return TEMP_FAILURE_RETRY(pwrite(fd, buf, len, offset));
#else
@@ -516,7 +522,7 @@
#undef pwrite
#define pwrite ___xxx_pwrite
-static __inline__ int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where) {
+static inline int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where) {
#if defined(__APPLE__)
return lseek(fd.get(), pos, where);
#else
@@ -526,13 +532,13 @@
#undef lseek
#define lseek ___xxx_lseek
-static __inline__ int adb_unlink(const char* path) {
+static inline int adb_unlink(const char* path) {
return unlink(path);
}
#undef unlink
#define unlink ___xxx_unlink
-static __inline__ int adb_creat(const char* path, int mode) {
+static inline int adb_creat(const char* path, int mode) {
int fd = TEMP_FAILURE_RETRY(creat(path, mode));
if (fd < 0) return -1;
@@ -543,7 +549,7 @@
#undef creat
#define creat ___xxx_creat
-static __inline__ int unix_isatty(borrowed_fd fd) {
+static inline int unix_isatty(borrowed_fd fd) {
return isatty(fd.get());
}
#define isatty ___xxx_isatty
@@ -570,8 +576,8 @@
int network_connect(const std::string& host, int port, int type, int timeout, std::string* error);
-static __inline__ int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr,
- socklen_t* addrlen) {
+static inline int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr,
+ socklen_t* addrlen) {
int fd;
fd = TEMP_FAILURE_RETRY(accept(serverfd.get(), addr, addrlen));
@@ -600,7 +606,7 @@
#define unix_lseek adb_lseek
#define unix_close adb_close
-static __inline__ int adb_thread_setname(const std::string& name) {
+static inline int adb_thread_setname(const std::string& name) {
#ifdef __APPLE__
return pthread_setname_np(name.c_str());
#else
@@ -613,19 +619,19 @@
#endif
}
-static __inline__ int adb_setsockopt(borrowed_fd fd, int level, int optname, const void* optval,
- socklen_t optlen) {
+static inline int adb_setsockopt(borrowed_fd fd, int level, int optname, const void* optval,
+ socklen_t optlen) {
return setsockopt(fd.get(), level, optname, optval, optlen);
}
#undef setsockopt
#define setsockopt ___xxx_setsockopt
-static __inline__ int unix_socketpair(int d, int type, int protocol, int sv[2]) {
+static inline int unix_socketpair(int d, int type, int protocol, int sv[2]) {
return socketpair(d, type, protocol, sv);
}
-static __inline__ int adb_socketpair(int sv[2]) {
+static inline int adb_socketpair(int sv[2]) {
int rc;
rc = unix_socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
@@ -640,32 +646,32 @@
#define socketpair ___xxx_socketpair
typedef struct pollfd adb_pollfd;
-static __inline__ int adb_poll(adb_pollfd* fds, size_t nfds, int timeout) {
+static inline int adb_poll(adb_pollfd* fds, size_t nfds, int timeout) {
return TEMP_FAILURE_RETRY(poll(fds, nfds, timeout));
}
#define poll ___xxx_poll
-static __inline__ int adb_mkdir(const std::string& path, int mode) {
+static inline int adb_mkdir(const std::string& path, int mode) {
return mkdir(path.c_str(), mode);
}
#undef mkdir
#define mkdir ___xxx_mkdir
-static __inline__ int adb_rename(const char* oldpath, const char* newpath) {
+static inline int adb_rename(const char* oldpath, const char* newpath) {
return rename(oldpath, newpath);
}
-static __inline__ int adb_is_absolute_host_path(const char* path) {
+static inline int adb_is_absolute_host_path(const char* path) {
return path[0] == '/';
}
-static __inline__ int adb_get_os_handle(borrowed_fd fd) {
+static inline int adb_get_os_handle(borrowed_fd fd) {
return fd.get();
}
-static __inline__ int cast_handle_to_int(int fd) {
+static inline int cast_handle_to_int(int fd) {
return fd;
}
@@ -715,5 +721,3 @@
// Win32 defines ERROR, which we don't need, but which conflicts with google3 logging.
#undef ERROR
#endif
-
-#endif /* _ADB_SYSDEPS_H */
diff --git a/adb/test_device.py b/adb/test_device.py
index 6a9ff89..f5e4cbb 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -77,8 +77,7 @@
class DeviceTest(unittest.TestCase):
- def setUp(self):
- self.device = adb.get_device()
+ device = adb.get_device()
class AbbTest(DeviceTest):
@@ -753,535 +752,611 @@
return files
-class FileOperationsTest(DeviceTest):
- SCRATCH_DIR = '/data/local/tmp'
- DEVICE_TEMP_FILE = SCRATCH_DIR + '/adb_test_file'
- DEVICE_TEMP_DIR = SCRATCH_DIR + '/adb_test_dir'
+class FileOperationsTest:
+ class Base(DeviceTest):
+ SCRATCH_DIR = '/data/local/tmp'
+ DEVICE_TEMP_FILE = SCRATCH_DIR + '/adb_test_file'
+ DEVICE_TEMP_DIR = SCRATCH_DIR + '/adb_test_dir'
- def _verify_remote(self, checksum, remote_path):
- dev_md5, _ = self.device.shell([get_md5_prog(self.device),
- remote_path])[0].split()
- self.assertEqual(checksum, dev_md5)
+ def setUp(self):
+ self.previous_env = os.environ.get("ADB_COMPRESSION")
+ os.environ["ADB_COMPRESSION"] = self.compression
- def _verify_local(self, checksum, local_path):
- with open(local_path, 'rb') as host_file:
- host_md5 = compute_md5(host_file.read())
- self.assertEqual(host_md5, checksum)
+ def tearDown(self):
+ if self.previous_env is None:
+ del os.environ["ADB_COMPRESSION"]
+ else:
+ os.environ["ADB_COMPRESSION"] = self.previous_env
- def test_push(self):
- """Push a randomly generated file to specified device."""
- kbytes = 512
- tmp = tempfile.NamedTemporaryFile(mode='wb', delete=False)
- rand_str = os.urandom(1024 * kbytes)
- tmp.write(rand_str)
- tmp.close()
+ def _verify_remote(self, checksum, remote_path):
+ dev_md5, _ = self.device.shell([get_md5_prog(self.device),
+ remote_path])[0].split()
+ self.assertEqual(checksum, dev_md5)
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
- self.device.push(local=tmp.name, remote=self.DEVICE_TEMP_FILE)
+ def _verify_local(self, checksum, local_path):
+ with open(local_path, 'rb') as host_file:
+ host_md5 = compute_md5(host_file.read())
+ self.assertEqual(host_md5, checksum)
- self._verify_remote(compute_md5(rand_str), self.DEVICE_TEMP_FILE)
- self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
+ def test_push(self):
+ """Push a randomly generated file to specified device."""
+ kbytes = 512
+ tmp = tempfile.NamedTemporaryFile(mode='wb', delete=False)
+ rand_str = os.urandom(1024 * kbytes)
+ tmp.write(rand_str)
+ tmp.close()
- os.remove(tmp.name)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
+ self.device.push(local=tmp.name, remote=self.DEVICE_TEMP_FILE)
- def test_push_dir(self):
- """Push a randomly generated directory of files to the device."""
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
+ self._verify_remote(compute_md5(rand_str), self.DEVICE_TEMP_FILE)
+ self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
- try:
- host_dir = tempfile.mkdtemp()
+ os.remove(tmp.name)
- # Make sure the temp directory isn't setuid, or else adb will complain.
- os.chmod(host_dir, 0o700)
-
- # Create 32 random files.
- temp_files = make_random_host_files(in_dir=host_dir, num_files=32)
- self.device.push(host_dir, self.DEVICE_TEMP_DIR)
-
- for temp_file in temp_files:
- remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
- os.path.basename(host_dir),
- temp_file.base_name)
- self._verify_remote(temp_file.checksum, remote_path)
+ def test_push_dir(self):
+ """Push a randomly generated directory of files to the device."""
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
+ self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
- def disabled_test_push_empty(self):
- """Push an empty directory to the device."""
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
+ try:
+ host_dir = tempfile.mkdtemp()
- try:
- host_dir = tempfile.mkdtemp()
+ # Make sure the temp directory isn't setuid, or else adb will complain.
+ os.chmod(host_dir, 0o700)
- # Make sure the temp directory isn't setuid, or else adb will complain.
- os.chmod(host_dir, 0o700)
+ # Create 32 random files.
+ temp_files = make_random_host_files(in_dir=host_dir, num_files=32)
+ self.device.push(host_dir, self.DEVICE_TEMP_DIR)
- # Create an empty directory.
- empty_dir_path = os.path.join(host_dir, 'empty')
- os.mkdir(empty_dir_path);
+ for temp_file in temp_files:
+ remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
+ os.path.basename(host_dir),
+ temp_file.base_name)
+ self._verify_remote(temp_file.checksum, remote_path)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
- self.device.push(empty_dir_path, self.DEVICE_TEMP_DIR)
-
- remote_path = os.path.join(self.DEVICE_TEMP_DIR, "empty")
- test_empty_cmd = ["[", "-d", remote_path, "]"]
- rc, _, _ = self.device.shell_nocheck(test_empty_cmd)
-
- self.assertEqual(rc, 0)
+ def disabled_test_push_empty(self):
+ """Push an empty directory to the device."""
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
+ self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
- @unittest.skipIf(sys.platform == "win32", "symlinks require elevated privileges on windows")
- def test_push_symlink(self):
- """Push a symlink.
+ try:
+ host_dir = tempfile.mkdtemp()
- Bug: http://b/31491920
- """
- try:
- host_dir = tempfile.mkdtemp()
+ # Make sure the temp directory isn't setuid, or else adb will complain.
+ os.chmod(host_dir, 0o700)
- # Make sure the temp directory isn't setuid, or else adb will
- # complain.
- os.chmod(host_dir, 0o700)
+ # Create an empty directory.
+ empty_dir_path = os.path.join(host_dir, 'empty')
+ os.mkdir(empty_dir_path);
- with open(os.path.join(host_dir, 'foo'), 'w') as f:
- f.write('foo')
+ self.device.push(empty_dir_path, self.DEVICE_TEMP_DIR)
- symlink_path = os.path.join(host_dir, 'symlink')
- os.symlink('foo', symlink_path)
+ remote_path = os.path.join(self.DEVICE_TEMP_DIR, "empty")
+ test_empty_cmd = ["[", "-d", remote_path, "]"]
+ rc, _, _ = self.device.shell_nocheck(test_empty_cmd)
+
+ self.assertEqual(rc, 0)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ @unittest.skipIf(sys.platform == "win32", "symlinks require elevated privileges on windows")
+ def test_push_symlink(self):
+ """Push a symlink.
+
+ Bug: http://b/31491920
+ """
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ # Make sure the temp directory isn't setuid, or else adb will
+ # complain.
+ os.chmod(host_dir, 0o700)
+
+ with open(os.path.join(host_dir, 'foo'), 'w') as f:
+ f.write('foo')
+
+ symlink_path = os.path.join(host_dir, 'symlink')
+ os.symlink('foo', symlink_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
+ self.device.push(symlink_path, self.DEVICE_TEMP_DIR)
+ rc, out, _ = self.device.shell_nocheck(
+ ['cat', posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')])
+ self.assertEqual(0, rc)
+ self.assertEqual(out.strip(), 'foo')
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_multiple_push(self):
+ """Push multiple files to the device in one adb push command.
+
+ Bug: http://b/25324823
+ """
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
- self.device.push(symlink_path, self.DEVICE_TEMP_DIR)
- rc, out, _ = self.device.shell_nocheck(
- ['cat', posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')])
- self.assertEqual(0, rc)
- self.assertEqual(out.strip(), 'foo')
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
- def test_multiple_push(self):
- """Push multiple files to the device in one adb push command.
-
- Bug: http://b/25324823
- """
-
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
-
- try:
- host_dir = tempfile.mkdtemp()
-
- # Create some random files and a subdirectory containing more files.
- temp_files = make_random_host_files(in_dir=host_dir, num_files=4)
-
- subdir = os.path.join(host_dir, 'subdir')
- os.mkdir(subdir)
- subdir_temp_files = make_random_host_files(in_dir=subdir,
- num_files=4)
-
- paths = [x.full_path for x in temp_files]
- paths.append(subdir)
- self.device._simple_call(['push'] + paths + [self.DEVICE_TEMP_DIR])
-
- for temp_file in temp_files:
- remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
- temp_file.base_name)
- self._verify_remote(temp_file.checksum, remote_path)
-
- for subdir_temp_file in subdir_temp_files:
- remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
- # BROKEN: http://b/25394682
- # 'subdir';
- temp_file.base_name)
- self._verify_remote(temp_file.checksum, remote_path)
-
-
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
-
- @requires_non_root
- def test_push_error_reporting(self):
- """Make sure that errors that occur while pushing a file get reported
-
- Bug: http://b/26816782
- """
- with tempfile.NamedTemporaryFile() as tmp_file:
- tmp_file.write(b'\0' * 1024 * 1024)
- tmp_file.flush()
try:
- self.device.push(local=tmp_file.name, remote='/system/')
- self.fail('push should not have succeeded')
+ host_dir = tempfile.mkdtemp()
+
+ # Create some random files and a subdirectory containing more files.
+ temp_files = make_random_host_files(in_dir=host_dir, num_files=4)
+
+ subdir = os.path.join(host_dir, 'subdir')
+ os.mkdir(subdir)
+ subdir_temp_files = make_random_host_files(in_dir=subdir,
+ num_files=4)
+
+ paths = [x.full_path for x in temp_files]
+ paths.append(subdir)
+ self.device._simple_call(['push'] + paths + [self.DEVICE_TEMP_DIR])
+
+ for temp_file in temp_files:
+ remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
+ temp_file.base_name)
+ self._verify_remote(temp_file.checksum, remote_path)
+
+ for subdir_temp_file in subdir_temp_files:
+ remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
+ # BROKEN: http://b/25394682
+ # 'subdir';
+ temp_file.base_name)
+ self._verify_remote(temp_file.checksum, remote_path)
+
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ @requires_non_root
+ def test_push_error_reporting(self):
+ """Make sure that errors that occur while pushing a file get reported
+
+ Bug: http://b/26816782
+ """
+ with tempfile.NamedTemporaryFile() as tmp_file:
+ tmp_file.write(b'\0' * 1024 * 1024)
+ tmp_file.flush()
+ try:
+ self.device.push(local=tmp_file.name, remote='/system/')
+ self.fail('push should not have succeeded')
+ except subprocess.CalledProcessError as e:
+ output = e.output
+
+ self.assertTrue(b'Permission denied' in output or
+ b'Read-only file system' in output)
+
+ @requires_non_root
+ def test_push_directory_creation(self):
+ """Regression test for directory creation.
+
+ Bug: http://b/110953234
+ """
+ with tempfile.NamedTemporaryFile() as tmp_file:
+ tmp_file.write(b'\0' * 1024 * 1024)
+ tmp_file.flush()
+ remote_path = self.DEVICE_TEMP_DIR + '/test_push_directory_creation'
+ self.device.shell(['rm', '-rf', remote_path])
+
+ remote_path += '/filename'
+ self.device.push(local=tmp_file.name, remote=remote_path)
+
+ def disabled_test_push_multiple_slash_root(self):
+ """Regression test for pushing to //data/local/tmp.
+
+ Bug: http://b/141311284
+
+ Disabled because this broken on the adbd side as well: b/141943968
+ """
+ with tempfile.NamedTemporaryFile() as tmp_file:
+ tmp_file.write('\0' * 1024 * 1024)
+ tmp_file.flush()
+ remote_path = '/' + self.DEVICE_TEMP_DIR + '/test_push_multiple_slash_root'
+ self.device.shell(['rm', '-rf', remote_path])
+ self.device.push(local=tmp_file.name, remote=remote_path)
+
+ def _test_pull(self, remote_file, checksum):
+ tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
+ tmp_write.close()
+ self.device.pull(remote=remote_file, local=tmp_write.name)
+ with open(tmp_write.name, 'rb') as tmp_read:
+ host_contents = tmp_read.read()
+ host_md5 = compute_md5(host_contents)
+ self.assertEqual(checksum, host_md5)
+ os.remove(tmp_write.name)
+
+ @requires_non_root
+ def test_pull_error_reporting(self):
+ self.device.shell(['touch', self.DEVICE_TEMP_FILE])
+ self.device.shell(['chmod', 'a-rwx', self.DEVICE_TEMP_FILE])
+
+ try:
+ output = self.device.pull(remote=self.DEVICE_TEMP_FILE, local='x')
except subprocess.CalledProcessError as e:
output = e.output
- self.assertTrue(b'Permission denied' in output or
- b'Read-only file system' in output)
+ self.assertIn(b'Permission denied', output)
- @requires_non_root
- def test_push_directory_creation(self):
- """Regression test for directory creation.
+ self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
- Bug: http://b/110953234
- """
- with tempfile.NamedTemporaryFile() as tmp_file:
- tmp_file.write(b'\0' * 1024 * 1024)
- tmp_file.flush()
- remote_path = self.DEVICE_TEMP_DIR + '/test_push_directory_creation'
- self.device.shell(['rm', '-rf', remote_path])
+ def test_pull(self):
+ """Pull a randomly generated file from specified device."""
+ kbytes = 512
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
+ cmd = ['dd', 'if=/dev/urandom',
+ 'of={}'.format(self.DEVICE_TEMP_FILE), 'bs=1024',
+ 'count={}'.format(kbytes)]
+ self.device.shell(cmd)
+ dev_md5, _ = self.device.shell(
+ [get_md5_prog(self.device), self.DEVICE_TEMP_FILE])[0].split()
+ self._test_pull(self.DEVICE_TEMP_FILE, dev_md5)
+ self.device.shell_nocheck(['rm', self.DEVICE_TEMP_FILE])
- remote_path += '/filename'
- self.device.push(local=tmp_file.name, remote=remote_path)
+ def test_pull_dir(self):
+ """Pull a randomly generated directory of files from the device."""
+ try:
+ host_dir = tempfile.mkdtemp()
- def disabled_test_push_multiple_slash_root(self):
- """Regression test for pushing to //data/local/tmp.
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
- Bug: http://b/141311284
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
- Disabled because this broken on the adbd side as well: b/141943968
- """
- with tempfile.NamedTemporaryFile() as tmp_file:
- tmp_file.write('\0' * 1024 * 1024)
- tmp_file.flush()
- remote_path = '/' + self.DEVICE_TEMP_DIR + '/test_push_multiple_slash_root'
- self.device.shell(['rm', '-rf', remote_path])
- self.device.push(local=tmp_file.name, remote=remote_path)
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
- def _test_pull(self, remote_file, checksum):
- tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
- tmp_write.close()
- self.device.pull(remote=remote_file, local=tmp_write.name)
- with open(tmp_write.name, 'rb') as tmp_read:
- host_contents = tmp_read.read()
- host_md5 = compute_md5(host_contents)
- self.assertEqual(checksum, host_md5)
- os.remove(tmp_write.name)
+ for temp_file in temp_files:
+ host_path = os.path.join(
+ host_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
+ temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
- @requires_non_root
- def test_pull_error_reporting(self):
- self.device.shell(['touch', self.DEVICE_TEMP_FILE])
- self.device.shell(['chmod', 'a-rwx', self.DEVICE_TEMP_FILE])
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
- try:
- output = self.device.pull(remote=self.DEVICE_TEMP_FILE, local='x')
- except subprocess.CalledProcessError as e:
- output = e.output
+ def test_pull_dir_symlink(self):
+ """Pull a directory into a symlink to a directory.
- self.assertIn(b'Permission denied', output)
+ Bug: http://b/27362811
+ """
+ if os.name != 'posix':
+ raise unittest.SkipTest('requires POSIX')
- self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
+ try:
+ host_dir = tempfile.mkdtemp()
+ real_dir = os.path.join(host_dir, 'dir')
+ symlink = os.path.join(host_dir, 'symlink')
+ os.mkdir(real_dir)
+ os.symlink(real_dir, symlink)
- def test_pull(self):
- """Pull a randomly generated file from specified device."""
- kbytes = 512
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
- cmd = ['dd', 'if=/dev/urandom',
- 'of={}'.format(self.DEVICE_TEMP_FILE), 'bs=1024',
- 'count={}'.format(kbytes)]
- self.device.shell(cmd)
- dev_md5, _ = self.device.shell(
- [get_md5_prog(self.device), self.DEVICE_TEMP_FILE])[0].split()
- self._test_pull(self.DEVICE_TEMP_FILE, dev_md5)
- self.device.shell_nocheck(['rm', self.DEVICE_TEMP_FILE])
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
- def test_pull_dir(self):
- """Pull a randomly generated directory of files from the device."""
- try:
- host_dir = tempfile.mkdtemp()
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=symlink)
- # Populate device directory with random files.
- temp_files = make_random_device_files(
- self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+ for temp_file in temp_files:
+ host_path = os.path.join(
+ real_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
+ temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
- self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+ def test_pull_dir_symlink_collision(self):
+ """Pull a directory into a colliding symlink to directory."""
+ if os.name != 'posix':
+ raise unittest.SkipTest('requires POSIX')
+
+ try:
+ host_dir = tempfile.mkdtemp()
+ real_dir = os.path.join(host_dir, 'real')
+ tmp_dirname = os.path.basename(self.DEVICE_TEMP_DIR)
+ symlink = os.path.join(host_dir, tmp_dirname)
+ os.mkdir(real_dir)
+ os.symlink(real_dir, symlink)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
+
+ for temp_file in temp_files:
+ host_path = os.path.join(real_dir, temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_pull_dir_nonexistent(self):
+ """Pull a directory of files from the device to a nonexistent path."""
+ try:
+ host_dir = tempfile.mkdtemp()
+ dest_dir = os.path.join(host_dir, 'dest')
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=dest_dir)
+
+ for temp_file in temp_files:
+ host_path = os.path.join(dest_dir, temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ # selinux prevents adbd from accessing symlinks on /data/local/tmp.
+ def disabled_test_pull_symlink_dir(self):
+ """Pull a symlink to a directory of symlinks to files."""
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ remote_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'contents')
+ remote_links = posixpath.join(self.DEVICE_TEMP_DIR, 'links')
+ remote_symlink = posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', remote_dir, remote_links])
+ self.device.shell(['ln', '-s', remote_links, remote_symlink])
+
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=remote_dir, num_files=32)
+
+ for temp_file in temp_files:
+ self.device.shell(
+ ['ln', '-s', '../contents/{}'.format(temp_file.base_name),
+ posixpath.join(remote_links, temp_file.base_name)])
+
+ self.device.pull(remote=remote_symlink, local=host_dir)
+
+ for temp_file in temp_files:
+ host_path = os.path.join(
+ host_dir, 'symlink', temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_pull_empty(self):
+ """Pull a directory containing an empty directory from the device."""
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ remote_empty_path = posixpath.join(self.DEVICE_TEMP_DIR, 'empty')
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', remote_empty_path])
+
+ self.device.pull(remote=remote_empty_path, local=host_dir)
+ self.assertTrue(os.path.isdir(os.path.join(host_dir, 'empty')))
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_multiple_pull(self):
+ """Pull a randomly generated directory of files from the device."""
+
+ try:
+ host_dir = tempfile.mkdtemp()
+
+ subdir = posixpath.join(self.DEVICE_TEMP_DIR, 'subdir')
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', subdir])
+
+ # Create some random files and a subdirectory containing more files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=4)
+
+ subdir_temp_files = make_random_device_files(
+ self.device, in_dir=subdir, num_files=4, prefix='subdir_')
+
+ paths = [x.full_path for x in temp_files]
+ paths.append(subdir)
+ self.device._simple_call(['pull'] + paths + [host_dir])
+
+ for temp_file in temp_files:
+ local_path = os.path.join(host_dir, temp_file.base_name)
+ self._verify_local(temp_file.checksum, local_path)
+
+ for subdir_temp_file in subdir_temp_files:
+ local_path = os.path.join(host_dir,
+ 'subdir',
+ subdir_temp_file.base_name)
+ self._verify_local(subdir_temp_file.checksum, local_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def verify_sync(self, device, temp_files, device_dir):
+ """Verifies that a list of temp files was synced to the device."""
+ # Confirm that every file on the device mirrors that on the host.
for temp_file in temp_files:
- host_path = os.path.join(
- host_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
- temp_file.base_name)
- self._verify_local(temp_file.checksum, host_path)
+ device_full_path = posixpath.join(
+ device_dir, temp_file.base_name)
+ dev_md5, _ = device.shell(
+ [get_md5_prog(self.device), device_full_path])[0].split()
+ self.assertEqual(temp_file.checksum, dev_md5)
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
+ def test_sync(self):
+ """Sync a host directory to the data partition."""
- def test_pull_dir_symlink(self):
- """Pull a directory into a symlink to a directory.
+ try:
+ base_dir = tempfile.mkdtemp()
- Bug: http://b/27362811
- """
- if os.name != 'posix':
- raise unittest.SkipTest('requires POSIX')
+ # Create mirror device directory hierarchy within base_dir.
+ full_dir_path = base_dir + self.DEVICE_TEMP_DIR
+ os.makedirs(full_dir_path)
- try:
- host_dir = tempfile.mkdtemp()
- real_dir = os.path.join(host_dir, 'dir')
- symlink = os.path.join(host_dir, 'symlink')
- os.mkdir(real_dir)
- os.symlink(real_dir, symlink)
+ # Create 32 random files within the host mirror.
+ temp_files = make_random_host_files(
+ in_dir=full_dir_path, num_files=32)
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+ # Clean up any stale files on the device.
+ device = adb.get_device() # pylint: disable=no-member
+ device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- # Populate device directory with random files.
- temp_files = make_random_device_files(
- self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+ old_product_out = os.environ.get('ANDROID_PRODUCT_OUT')
+ os.environ['ANDROID_PRODUCT_OUT'] = base_dir
+ device.sync('data')
+ if old_product_out is None:
+ del os.environ['ANDROID_PRODUCT_OUT']
+ else:
+ os.environ['ANDROID_PRODUCT_OUT'] = old_product_out
- self.device.pull(remote=self.DEVICE_TEMP_DIR, local=symlink)
+ self.verify_sync(device, temp_files, self.DEVICE_TEMP_DIR)
- for temp_file in temp_files:
- host_path = os.path.join(
- real_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
- temp_file.base_name)
- self._verify_local(temp_file.checksum, host_path)
+ #self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if base_dir is not None:
+ shutil.rmtree(base_dir)
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
+ def test_push_sync(self):
+ """Sync a host directory to a specific path."""
- def test_pull_dir_symlink_collision(self):
- """Pull a directory into a colliding symlink to directory."""
- if os.name != 'posix':
- raise unittest.SkipTest('requires POSIX')
+ try:
+ temp_dir = tempfile.mkdtemp()
+ temp_files = make_random_host_files(in_dir=temp_dir, num_files=32)
- try:
- host_dir = tempfile.mkdtemp()
- real_dir = os.path.join(host_dir, 'real')
- tmp_dirname = os.path.basename(self.DEVICE_TEMP_DIR)
- symlink = os.path.join(host_dir, tmp_dirname)
- os.mkdir(real_dir)
- os.symlink(real_dir, symlink)
+ device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'sync_src_dst')
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+ # Clean up any stale files on the device.
+ device = adb.get_device() # pylint: disable=no-member
+ device.shell(['rm', '-rf', device_dir])
- # Populate device directory with random files.
- temp_files = make_random_device_files(
- self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+ device.push(temp_dir, device_dir, sync=True)
- self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
+ self.verify_sync(device, temp_files, device_dir)
- for temp_file in temp_files:
- host_path = os.path.join(real_dir, temp_file.base_name)
- self._verify_local(temp_file.checksum, host_path)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if temp_dir is not None:
+ shutil.rmtree(temp_dir)
- 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_nonexistent_file(self):
+ """Push with dry run."""
- def test_pull_dir_nonexistent(self):
- """Pull a directory of files from the device to a nonexistent path."""
- try:
- host_dir = tempfile.mkdtemp()
- dest_dir = os.path.join(host_dir, 'dest')
+ 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', self.DEVICE_TEMP_DIR])
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', device_dir])
- # Populate device directory with random files.
- temp_files = make_random_device_files(
- self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+ host_dir = tempfile.mkdtemp()
+ host_file = posixpath.join(host_dir, 'file')
- self.device.pull(remote=self.DEVICE_TEMP_DIR, local=dest_dir)
+ with open(host_file, "w") as f:
+ f.write('x' * file_size)
- for temp_file in temp_files:
- host_path = os.path.join(dest_dir, temp_file.base_name)
- self._verify_local(temp_file.checksum, host_path)
+ 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)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
- # selinux prevents adbd from accessing symlinks on /data/local/tmp.
- def disabled_test_pull_symlink_dir(self):
- """Pull a symlink to a directory of symlinks to files."""
- try:
- host_dir = tempfile.mkdtemp()
+ def test_push_dry_run_existent_file(self):
+ """Push with dry run."""
- remote_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'contents')
- remote_links = posixpath.join(self.DEVICE_TEMP_DIR, 'links')
- remote_symlink = posixpath.join(self.DEVICE_TEMP_DIR, 'symlink')
+ 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', remote_dir, remote_links])
- self.device.shell(['ln', '-s', remote_links, remote_symlink])
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', device_dir])
+ self.device.shell(['echo', 'foo', '>', device_file])
- # Populate device directory with random files.
- temp_files = make_random_device_files(
- self.device, in_dir=remote_dir, num_files=32)
+ host_dir = tempfile.mkdtemp()
+ host_file = posixpath.join(host_dir, 'file')
- for temp_file in temp_files:
- self.device.shell(
- ['ln', '-s', '../contents/{}'.format(temp_file.base_name),
- posixpath.join(remote_links, temp_file.base_name)])
+ with open(host_file, "w") as f:
+ f.write('x' * file_size)
- self.device.pull(remote=remote_symlink, local=host_dir)
+ self.device._simple_call(['push', '-n', host_file, device_file])
+ stdout, stderr = self.device.shell(['cat', device_file])
+ self.assertEqual(stdout.strip(), "foo")
- for temp_file in temp_files:
- host_path = os.path.join(
- host_dir, 'symlink', temp_file.base_name)
- self._verify_local(temp_file.checksum, host_path)
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
- 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'로보카 폴리'
- def test_pull_empty(self):
- """Pull a directory containing an empty directory from the device."""
- try:
- host_dir = tempfile.mkdtemp()
+ self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
+ remote_path = u'/data/local/tmp/adb-test-{}'.format(name)
- remote_empty_path = posixpath.join(self.DEVICE_TEMP_DIR, 'empty')
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', '-p', remote_empty_path])
+ ## push.
+ tf = tempfile.NamedTemporaryFile('wb', suffix=name, delete=False)
+ tf.close()
+ self.device.push(tf.name, remote_path)
+ os.remove(tf.name)
+ self.assertFalse(os.path.exists(tf.name))
- self.device.pull(remote=remote_empty_path, local=host_dir)
- self.assertTrue(os.path.isdir(os.path.join(host_dir, 'empty')))
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
+ # Verify that the device ended up with the expected UTF-8 path
+ output = self.device.shell(
+ ['ls', '/data/local/tmp/adb-test-*'])[0].strip()
+ self.assertEqual(remote_path, output)
- def test_multiple_pull(self):
- """Pull a randomly generated directory of files from the device."""
+ # pull.
+ self.device.pull(remote_path, tf.name)
+ self.assertTrue(os.path.exists(tf.name))
+ os.remove(tf.name)
+ self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
- try:
- host_dir = tempfile.mkdtemp()
- subdir = posixpath.join(self.DEVICE_TEMP_DIR, 'subdir')
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- self.device.shell(['mkdir', '-p', subdir])
+class FileOperationsTestUncompressed(FileOperationsTest.Base):
+ compression = "none"
- # Create some random files and a subdirectory containing more files.
- temp_files = make_random_device_files(
- self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=4)
- subdir_temp_files = make_random_device_files(
- self.device, in_dir=subdir, num_files=4, prefix='subdir_')
+class FileOperationsTestBrotli(FileOperationsTest.Base):
+ compression = "brotli"
- paths = [x.full_path for x in temp_files]
- paths.append(subdir)
- self.device._simple_call(['pull'] + paths + [host_dir])
- for temp_file in temp_files:
- local_path = os.path.join(host_dir, temp_file.base_name)
- self._verify_local(temp_file.checksum, local_path)
-
- for subdir_temp_file in subdir_temp_files:
- local_path = os.path.join(host_dir,
- 'subdir',
- subdir_temp_file.base_name)
- self._verify_local(subdir_temp_file.checksum, local_path)
-
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if host_dir is not None:
- shutil.rmtree(host_dir)
-
- def verify_sync(self, device, temp_files, device_dir):
- """Verifies that a list of temp files was synced to the device."""
- # Confirm that every file on the device mirrors that on the host.
- for temp_file in temp_files:
- device_full_path = posixpath.join(
- device_dir, temp_file.base_name)
- dev_md5, _ = device.shell(
- [get_md5_prog(self.device), device_full_path])[0].split()
- self.assertEqual(temp_file.checksum, dev_md5)
-
- def test_sync(self):
- """Sync a host directory to the data partition."""
-
- try:
- base_dir = tempfile.mkdtemp()
-
- # Create mirror device directory hierarchy within base_dir.
- full_dir_path = base_dir + self.DEVICE_TEMP_DIR
- os.makedirs(full_dir_path)
-
- # Create 32 random files within the host mirror.
- temp_files = make_random_host_files(
- in_dir=full_dir_path, num_files=32)
-
- # Clean up any stale files on the device.
- device = adb.get_device() # pylint: disable=no-member
- device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
-
- old_product_out = os.environ.get('ANDROID_PRODUCT_OUT')
- os.environ['ANDROID_PRODUCT_OUT'] = base_dir
- device.sync('data')
- if old_product_out is None:
- del os.environ['ANDROID_PRODUCT_OUT']
- else:
- os.environ['ANDROID_PRODUCT_OUT'] = old_product_out
-
- self.verify_sync(device, temp_files, self.DEVICE_TEMP_DIR)
-
- #self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if base_dir is not None:
- shutil.rmtree(base_dir)
-
- def test_push_sync(self):
- """Sync a host directory to a specific path."""
-
- try:
- temp_dir = tempfile.mkdtemp()
- temp_files = make_random_host_files(in_dir=temp_dir, num_files=32)
-
- device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'sync_src_dst')
-
- # Clean up any stale files on the device.
- device = adb.get_device() # pylint: disable=no-member
- device.shell(['rm', '-rf', device_dir])
-
- device.push(temp_dir, device_dir, sync=True)
-
- self.verify_sync(device, temp_files, device_dir)
-
- self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
- finally:
- if temp_dir is not None:
- shutil.rmtree(temp_dir)
-
- def test_unicode_paths(self):
- """Ensure that we can support non-ASCII paths, even on Windows."""
- name = u'로보카 폴리'
-
- self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
- remote_path = u'/data/local/tmp/adb-test-{}'.format(name)
-
- ## push.
- tf = tempfile.NamedTemporaryFile('wb', suffix=name, delete=False)
- tf.close()
- self.device.push(tf.name, remote_path)
- os.remove(tf.name)
- self.assertFalse(os.path.exists(tf.name))
-
- # Verify that the device ended up with the expected UTF-8 path
- output = self.device.shell(
- ['ls', '/data/local/tmp/adb-test-*'])[0].strip()
- self.assertEqual(remote_path, output)
-
- # pull.
- self.device.pull(remote_path, tf.name)
- self.assertTrue(os.path.exists(tf.name))
- os.remove(tf.name)
- self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
+class FileOperationsTestLZ4(FileOperationsTest.Base):
+ compression = "lz4"
class DeviceOfflineTest(DeviceTest):
diff --git a/adb/transport.cpp b/adb/transport.cpp
index e06dbe3..cc2e0e3 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -84,6 +84,8 @@
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 {
@@ -1183,6 +1185,8 @@
kFeatureTrackApp,
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.
diff --git a/adb/transport.h b/adb/transport.h
index b1984db..4b2e000 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -88,6 +88,10 @@
extern const char* const kFeatureSendRecv2;
// adbd supports brotli for send/recv v2.
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();
diff --git a/adb/types.cpp b/adb/types.cpp
index 26b77ab..9cdf32b 100644
--- a/adb/types.cpp
+++ b/adb/types.cpp
@@ -51,7 +51,7 @@
auto dropped = 0u;
while (dropped < len) {
const auto next = chain_[start_index_].size() - begin_offset_;
- if (dropped + next < len) {
+ if (dropped + next <= len) {
pop_front_block();
dropped += next;
} else {
diff --git a/adb/types.h b/adb/types.h
index deca7ea..620aa8e 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -155,7 +155,7 @@
return nullptr;
}
- return chain_.front().data() + begin_offset_;
+ return chain_[start_index_].data() + begin_offset_;
}
size_type front_size() const {
@@ -163,7 +163,7 @@
return 0;
}
- return chain_.front().size() - begin_offset_;
+ return chain_[start_index_].size() - begin_offset_;
}
size_type size() const { return chain_length_ - begin_offset_; }
diff --git a/adb/types_test.cpp b/adb/types_test.cpp
index 2c99f95..41fa1db 100644
--- a/adb/types_test.cpp
+++ b/adb/types_test.cpp
@@ -117,3 +117,20 @@
ASSERT_EQ(1ULL, bc.size());
ASSERT_EQ(create_block("x"), bc.coalesce());
}
+
+TEST(IOVector, drop_front) {
+ IOVector vec;
+
+ vec.append(create_block('x', 2));
+ vec.append(create_block('y', 1000));
+ ASSERT_EQ(2U, vec.front_size());
+ ASSERT_EQ(1002U, vec.size());
+
+ vec.drop_front(1);
+ ASSERT_EQ(1U, vec.front_size());
+ ASSERT_EQ(1001U, vec.size());
+
+ vec.drop_front(1);
+ ASSERT_EQ(1000U, vec.front_size());
+ ASSERT_EQ(1000U, vec.size());
+}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index a9c1676..5d6cee4 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -1238,16 +1238,26 @@
// Shift last_reboot_reason_property to last_last_reboot_reason_property
std::string last_boot_reason;
if (!android::base::ReadFileToString(last_reboot_reason_file, &last_boot_reason)) {
+ PLOG(ERROR) << "Failed to read " << last_reboot_reason_file;
last_boot_reason = android::base::GetProperty(last_reboot_reason_property, "");
+ LOG(INFO) << "Value of " << last_reboot_reason_property << " : " << last_boot_reason;
+ } else {
+ LOG(INFO) << "Last reboot reason read from " << last_reboot_reason_file << " : "
+ << last_boot_reason << ". Last reboot reason read from "
+ << last_reboot_reason_property << " : "
+ << android::base::GetProperty(last_reboot_reason_property, "");
}
if (last_boot_reason.empty() || isKernelRebootReason(system_boot_reason)) {
last_boot_reason = system_boot_reason;
} else {
transformReason(last_boot_reason);
}
+ LOG(INFO) << "Normalized last reboot reason : " << last_boot_reason;
android::base::SetProperty(last_last_reboot_reason_property, last_boot_reason);
android::base::SetProperty(last_reboot_reason_property, "");
- unlink(last_reboot_reason_file);
+ if (unlink(last_reboot_reason_file) != 0) {
+ PLOG(ERROR) << "Failed to unlink " << last_reboot_reason_file;
+ }
}
// Gets the boot time offset. This is useful when Android is running in a
diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c
index e52762e..58153f3 100644
--- a/cpio/mkbootfs.c
+++ b/cpio/mkbootfs.c
@@ -13,6 +13,7 @@
#include <fcntl.h>
#include <private/android_filesystem_config.h>
+#include <private/fs_config.h>
/* NOTES
**
diff --git a/fastboot/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp
index bb54fd9..aa449b2 100644
--- a/fastboot/fuzzy_fastboot/Android.bp
+++ b/fastboot/fuzzy_fastboot/Android.bp
@@ -49,6 +49,6 @@
auto_gen_config: false,
test_suites: [
"general-tests",
- "vts-core",
+ "vts",
],
}
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 5475cae..1486e87 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -97,7 +97,6 @@
using android::base::Basename;
using android::base::GetBoolProperty;
using android::base::GetUintProperty;
-using android::base::Readlink;
using android::base::Realpath;
using android::base::SetProperty;
using android::base::StartsWith;
@@ -1552,8 +1551,8 @@
return std::chrono::milliseconds(std::move(value));
}
-static bool fs_mgr_unmount_all_data_mounts(const std::string& block_device) {
- LINFO << __FUNCTION__ << "(): about to umount everything on top of " << block_device;
+static bool fs_mgr_unmount_all_data_mounts(const std::string& data_block_device) {
+ LINFO << __FUNCTION__ << "(): about to umount everything on top of " << data_block_device;
Timer t;
auto timeout = GetMillisProperty("init.userspace_reboot.userdata_remount.timeoutmillis", 5s);
while (true) {
@@ -1565,7 +1564,13 @@
}
// Now proceed with other bind mounts on top of /data.
for (const auto& entry : proc_mounts) {
- if (entry.blk_device == block_device) {
+ std::string block_device;
+ if (StartsWith(entry.blk_device, "/dev/block") &&
+ !Realpath(entry.blk_device, &block_device)) {
+ PWARNING << __FUNCTION__ << "(): failed to realpath " << entry.blk_device;
+ block_device = entry.blk_device;
+ }
+ if (data_block_device == block_device) {
if (umount2(entry.mount_point.c_str(), 0) != 0) {
PERROR << __FUNCTION__ << "(): Failed to umount " << entry.mount_point;
umount_done = false;
@@ -1577,7 +1582,8 @@
return true;
}
if (t.duration() > timeout) {
- LERROR << __FUNCTION__ << "(): Timed out unmounting all mounts on " << block_device;
+ LERROR << __FUNCTION__ << "(): Timed out unmounting all mounts on "
+ << data_block_device;
Fstab remaining_mounts;
if (!ReadFstabFromFile("/proc/mounts", &remaining_mounts)) {
LERROR << __FUNCTION__ << "(): Can't read /proc/mounts";
@@ -1616,14 +1622,11 @@
return true;
}
-FstabEntry* fs_mgr_get_mounted_entry_for_userdata(Fstab* fstab, const FstabEntry& mounted_entry) {
- if (mounted_entry.mount_point != "/data") {
- LERROR << mounted_entry.mount_point << " is not /data";
- return nullptr;
- }
+FstabEntry* fs_mgr_get_mounted_entry_for_userdata(Fstab* fstab,
+ const std::string& data_block_device) {
std::vector<std::string> dm_stack;
- if (!UnwindDmDeviceStack(mounted_entry.blk_device, &dm_stack)) {
- LERROR << "Failed to unwind dm-device stack for " << mounted_entry.blk_device;
+ if (!UnwindDmDeviceStack(data_block_device, &dm_stack)) {
+ LERROR << "Failed to unwind dm-device stack for " << data_block_device;
return nullptr;
}
for (auto& entry : *fstab) {
@@ -1637,15 +1640,15 @@
continue;
}
block_device = entry.blk_device;
- } else if (!Readlink(entry.blk_device, &block_device)) {
- PWARNING << "Failed to read link " << entry.blk_device;
+ } else if (!Realpath(entry.blk_device, &block_device)) {
+ PWARNING << "Failed to realpath " << entry.blk_device;
block_device = entry.blk_device;
}
if (std::find(dm_stack.begin(), dm_stack.end(), block_device) != dm_stack.end()) {
return &entry;
}
}
- LERROR << "Didn't find entry that was used to mount /data onto " << mounted_entry.blk_device;
+ LERROR << "Didn't find entry that was used to mount /data onto " << data_block_device;
return nullptr;
}
@@ -1656,14 +1659,17 @@
LERROR << "Can't read /proc/mounts";
return -1;
}
- std::string block_device;
auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data");
if (mounted_entry == nullptr) {
LERROR << "/data is not mounted";
return -1;
}
- block_device = mounted_entry->blk_device;
- auto fstab_entry = fs_mgr_get_mounted_entry_for_userdata(fstab, *mounted_entry);
+ std::string block_device;
+ if (!Realpath(mounted_entry->blk_device, &block_device)) {
+ PERROR << "Failed to realpath " << mounted_entry->blk_device;
+ return -1;
+ }
+ auto fstab_entry = fs_mgr_get_mounted_entry_for_userdata(fstab, block_device);
if (fstab_entry == nullptr) {
LERROR << "Can't find /data in fstab";
return -1;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index a836d3b..b218f21 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -829,6 +829,20 @@
return std::set<std::string>(boot_devices.begin(), boot_devices.end());
}
+ std::string cmdline;
+ if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
+ std::set<std::string> boot_devices;
+ const std::string cmdline_key = "androidboot.boot_device";
+ for (const auto& [key, value] : fs_mgr_parse_boot_config(cmdline)) {
+ if (key == cmdline_key) {
+ boot_devices.emplace(value);
+ }
+ }
+ if (!boot_devices.empty()) {
+ return boot_devices;
+ }
+ }
+
// Fallback to extract boot devices from fstab.
Fstab fstab;
if (!ReadDefaultFstab(&fstab)) {
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 3d556c9..86090c1 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -107,10 +107,9 @@
// it destroys verity devices from device mapper after the device is unmounted.
int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab);
-// Finds a entry in |fstab| that was used to mount a /data |mounted_entry| from
-// /proc/mounts.
+// Finds a entry in |fstab| that was used to mount a /data on |data_block_device|.
android::fs_mgr::FstabEntry* fs_mgr_get_mounted_entry_for_userdata(
- android::fs_mgr::Fstab* fstab, const android::fs_mgr::FstabEntry& mounted_entry);
+ android::fs_mgr::Fstab* fstab, const std::string& data_block_device);
int fs_mgr_remount_userdata_into_checkpointing(android::fs_mgr::Fstab* fstab);
// Finds the dm_bow device on which this block device is stacked, or returns
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 0499e8d..58241b3 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -79,7 +79,7 @@
cc_test {
name: "vts_libdm_test",
defaults: ["libdm_test_defaults"],
- test_suites: ["vts-core"],
+ test_suites: ["vts"],
test_min_api_level: 29,
}
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 29b1032..a594198 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -243,20 +243,8 @@
return android::base::Join(argv, " ");
}
-bool DmTargetDefaultKey::IsLegacy(bool* result) {
- DeviceMapper& dm = DeviceMapper::Instance();
- DmTargetTypeInfo info;
- if (!dm.GetTargetByName(kName, &info)) return false;
- // dm-default-key was modified to be like dm-crypt with version 2
- *result = !info.IsAtLeast(2, 0, 0);
- return true;
-}
-
bool DmTargetDefaultKey::Valid() const {
- bool real_is_legacy;
- if (!DmTargetDefaultKey::IsLegacy(&real_is_legacy)) return false;
- if (real_is_legacy != is_legacy_) return false;
- if (!is_legacy_ && !set_dun_) return false;
+ if (!use_legacy_options_format_ && !set_dun_) return false;
return true;
}
@@ -264,13 +252,13 @@
std::vector<std::string> argv;
argv.emplace_back(cipher_);
argv.emplace_back(key_);
- if (!is_legacy_) {
+ if (!use_legacy_options_format_) {
argv.emplace_back("0"); // iv_offset
}
argv.emplace_back(blockdev_);
argv.push_back(std::to_string(start_sector_));
std::vector<std::string> extra_argv;
- if (is_legacy_) {
+ if (use_legacy_options_format_) {
if (set_dun_) { // v2 always sets the DUN.
extra_argv.emplace_back("set_dun");
}
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 67af59a..41d3145 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -516,32 +516,22 @@
}
TEST(libdm, DefaultKeyArgs) {
- DmTargetTypeInfo info;
-
- DeviceMapper& dm = DeviceMapper::Instance();
- if (!dm.GetTargetByName("default-key", &info)) {
- cout << "default-key module not enabled; skipping test" << std::endl;
- return;
- }
- bool is_legacy;
- ASSERT_TRUE(DmTargetDefaultKey::IsLegacy(&is_legacy));
- // set_dun only in the non-is_legacy case
- DmTargetDefaultKey target(0, 4096, "AES-256-XTS", "abcdef0123456789", "/dev/loop0", 0);
- if (is_legacy) {
- target.SetIsLegacy();
- } else {
- target.SetSetDun();
- }
+ DmTargetDefaultKey target(0, 4096, "aes-xts-plain64", "abcdef0123456789", "/dev/loop0", 0);
+ target.SetSetDun();
ASSERT_EQ(target.name(), "default-key");
ASSERT_TRUE(target.Valid());
- if (is_legacy) {
- ASSERT_EQ(target.GetParameterString(), "AES-256-XTS abcdef0123456789 /dev/loop0 0");
- } else {
- // TODO: Add case for wrapped key enabled
- ASSERT_EQ(target.GetParameterString(),
- "AES-256-XTS abcdef0123456789 0 /dev/loop0 0 3 allow_discards sector_size:4096 "
- "iv_large_sectors");
- }
+ // TODO: Add case for wrapped key enabled
+ ASSERT_EQ(target.GetParameterString(),
+ "aes-xts-plain64 abcdef0123456789 0 /dev/loop0 0 3 allow_discards sector_size:4096 "
+ "iv_large_sectors");
+}
+
+TEST(libdm, DefaultKeyLegacyArgs) {
+ DmTargetDefaultKey target(0, 4096, "AES-256-XTS", "abcdef0123456789", "/dev/loop0", 0);
+ target.SetUseLegacyOptionsFormat();
+ ASSERT_EQ(target.name(), "default-key");
+ ASSERT_TRUE(target.Valid());
+ ASSERT_EQ(target.GetParameterString(), "AES-256-XTS abcdef0123456789 /dev/loop0 0");
}
TEST(libdm, DeleteDeviceWithTimeout) {
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 050d0b6..57096ce 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -290,8 +290,7 @@
std::string name() const override { return kName; }
bool Valid() const override;
std::string GetParameterString() const override;
- static bool IsLegacy(bool* result);
- void SetIsLegacy() { is_legacy_ = true; }
+ void SetUseLegacyOptionsFormat() { use_legacy_options_format_ = true; }
void SetSetDun() { set_dun_ = true; }
void SetWrappedKeyV0() { is_hw_wrapped_ = true; }
@@ -302,7 +301,7 @@
std::string key_;
std::string blockdev_;
uint64_t start_sector_;
- bool is_legacy_ = false;
+ bool use_legacy_options_format_ = false;
bool set_dun_ = false;
bool is_hw_wrapped_ = false;
};
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index ac589c7..9d18a44 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -81,7 +81,7 @@
"fiemap_writer_test.cpp",
],
- test_suites: ["vts-core", "device-tests"],
+ test_suites: ["vts", "device-tests"],
auto_gen_config: true,
test_min_api_level: 29,
require_root: true,
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index ad19f38..a779a78 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -97,7 +97,7 @@
cc_test {
name: "vts_core_liblp_test",
defaults: ["liblp_test_defaults"],
- test_suites: ["vts-core"],
+ test_suites: ["vts"],
auto_gen_config: true,
test_min_api_level: 29,
require_root: true,
diff --git a/fs_mgr/liblp/liblp_test.xml b/fs_mgr/liblp/liblp_test.xml
index d9ee12e..98414b1 100644
--- a/fs_mgr/liblp/liblp_test.xml
+++ b/fs_mgr/liblp/liblp_test.xml
@@ -19,7 +19,6 @@
<option name="cleanup" value="true" />
<option name="push" value="liblp_test->/data/local/tmp/liblp_test" />
</target_preparer>
- <option name="test-suite-tag" value="vts-core" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="liblp_test" />
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index d670ca0..a209ea6 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -182,7 +182,7 @@
"libstorage_literals_headers",
],
test_suites: [
- "vts-core",
+ "vts",
"device-tests"
],
test_min_api_level: 29,
@@ -195,6 +195,12 @@
defaults: ["libsnapshot_test_defaults"],
}
+// For VTS 10
+vts_config {
+ name: "VtsLibsnapshotTest",
+ test_config: "VtsLibsnapshotTest.xml"
+}
+
cc_binary {
name: "snapshotctl",
srcs: [
diff --git a/fs_mgr/libsnapshot/VtsLibsnapshotTest.xml b/fs_mgr/libsnapshot/VtsLibsnapshotTest.xml
new file mode 100644
index 0000000..b53b51e
--- /dev/null
+++ b/fs_mgr/libsnapshot/VtsLibsnapshotTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for VTS VtsLibsnapshotTest">
+ <option name="config-descriptor:metadata" key="plan" value="vts-kernel"/>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+ <option name="abort-on-push-failure" value="false"/>
+ <option name="push-group" value="HostDrivenTest.push"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+ <option name="test-module-name" value="VtsLibsnapshotTest"/>
+ <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_libsnapshot_test/vts_libsnapshot_test"/>
+ <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_libsnapshot_test/vts_libsnapshot_test"/>
+ <option name="binary-test-type" value="gtest"/>
+ <option name="test-timeout" value="5m"/>
+ </test>
+</configuration>
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 28dee88..f68ab87 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -17,7 +17,6 @@
test_suites: [
"cts",
"device-tests",
- "vts",
"vts10",
],
compile_multilib: "both",
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index cf324fe..82c4262 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -732,6 +732,7 @@
grep -v \
-e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\) " \
-e "^\(binfmt_misc\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
+ -e " functionfs " \
-e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
-e "^rootfs / rootfs rw," \
-e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|persist\|metadata\) "
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 3fec608..27c8aae 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -1020,6 +1020,8 @@
ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &proc_mounts)) << "Failed to read /proc/mounts";
auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data");
ASSERT_NE(mounted_entry, nullptr) << "/data is not mounted";
- ASSERT_NE(nullptr, fs_mgr_get_mounted_entry_for_userdata(&fstab, *mounted_entry))
+ std::string block_device;
+ ASSERT_TRUE(android::base::Realpath(mounted_entry->blk_device, &block_device));
+ ASSERT_NE(nullptr, fs_mgr_get_mounted_entry_for_userdata(&fstab, block_device))
<< "/data wasn't mounted from default fstab";
}
diff --git a/init/Android.bp b/init/Android.bp
index d512a4e..1b3aa18 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -258,7 +258,6 @@
test_suites: [
"cts",
"device-tests",
- "vts",
"vts10",
],
}
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index bd71cb5..ef8ffbe 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -234,7 +234,16 @@
old_root_dir.reset();
}
- Modprobe m({"/lib/modules"});
+ std::string module_load_file = "modules.load";
+ if (IsRecoveryMode() && !ForceNormalBoot(cmdline)) {
+ struct stat fileStat;
+ std::string recovery_load_path = "/lib/modules/modules.load.recovery";
+ if (!stat(recovery_load_path.c_str(), &fileStat)) {
+ module_load_file = "modules.load.recovery";
+ }
+ }
+
+ Modprobe m({"/lib/modules"}, module_load_file);
auto want_console = ALLOW_FIRST_STAGE_CONSOLE && FirstStageConsole(cmdline);
if (!m.LoadListedModules(!want_console)) {
if (want_console) {
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index 0bd4df4..ef9a451 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -32,6 +32,7 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
+#include <generated_android_ids.h>
#include <hidl/metadata.h>
#include <property_info_serializer/property_info_serializer.h>
@@ -48,9 +49,6 @@
#include "service_list.h"
#include "service_parser.h"
-#define EXCLUDE_FS_CONFIG_STRUCTURES
-#include "generated_android_ids.h"
-
using namespace std::literals;
using android::base::ParseInt;
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 655b8de..842b2e5 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -47,6 +47,7 @@
#include <thread>
#include <vector>
+#include <InitProperties.sysprop.h>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -85,6 +86,7 @@
using android::properties::ParsePropertyInfoFile;
using android::properties::PropertyInfoAreaFile;
using android::properties::PropertyInfoEntry;
+using android::sysprop::InitProperties::is_userspace_reboot_supported;
namespace android {
namespace init {
@@ -492,6 +494,10 @@
if (!value.empty()) {
DebugRebootLogging();
}
+ if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) {
+ *error = "Userspace reboot is not supported by this device";
+ return PROP_ERROR_INVALID_VALUE;
+ }
}
// If a process other than init is writing a non-empty value, it means that process is
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index 0f4cd0d..c6dcfa2 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -22,8 +22,10 @@
#include <sys/_system_properties.h>
#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
#include <gtest/gtest.h>
+using android::base::GetProperty;
using android::base::SetProperty;
namespace android {
@@ -74,5 +76,19 @@
EXPECT_TRUE(SetProperty("property_service_utf8_test", "\xF0\x90\x80\x80"));
}
+TEST(property_service, userspace_reboot_not_supported) {
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Skipping test, must be run as root.";
+ return;
+ }
+ const std::string original_value = GetProperty("init.userspace_reboot.is_supported", "");
+ auto guard = android::base::make_scope_guard([&original_value]() {
+ SetProperty("init.userspace_reboot.is_supported", original_value);
+ });
+
+ ASSERT_TRUE(SetProperty("init.userspace_reboot.is_supported", "false"));
+ EXPECT_FALSE(SetProperty("sys.powerctl", "reboot,userspace"));
+}
+
} // namespace init
} // namespace android
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index dccf588..175b2b7 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -346,7 +346,7 @@
cc_test {
name: "KernelLibcutilsTest",
- test_suites: ["general-tests", "vts-core"],
+ test_suites: ["general-tests", "vts"],
defaults: ["libcutils_test_static_defaults"],
test_config: "KernelLibcutilsTest.xml",
}
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index b73a29b..e4f45a8 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -34,14 +34,7 @@
* partition, from which the system reads passwd and group files.
*/
-#ifndef _ANDROID_FILESYSTEM_CONFIG_H_
-#define _ANDROID_FILESYSTEM_CONFIG_H_
-
-#include <sys/types.h>
-
-#if !defined(__ANDROID_VNDK__) && !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
-#include <private/fs_config.h>
-#endif
+#pragma once
/* This is the master Users and Groups config for the platform.
* DO NOT EVER RENUMBER
@@ -224,5 +217,3 @@
* documented at the top of this header file.
* Also see build/tools/fs_config for more details.
*/
-
-#endif
diff --git a/libcutils/include/private/canned_fs_config.h b/libcutils/include/private/canned_fs_config.h
index 135b91c..ad4de4c 100644
--- a/libcutils/include/private/canned_fs_config.h
+++ b/libcutils/include/private/canned_fs_config.h
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#ifndef _CANNED_FS_CONFIG_H
-#define _CANNED_FS_CONFIG_H
+#pragma once
#include <inttypes.h>
+#include <sys/cdefs.h>
__BEGIN_DECLS
@@ -26,5 +26,3 @@
unsigned* gid, unsigned* mode, uint64_t* capabilities);
__END_DECLS
-
-#endif
diff --git a/liblog/README.protocol.md b/liblog/README.protocol.md
index fef29c9..f247b28 100644
--- a/liblog/README.protocol.md
+++ b/liblog/README.protocol.md
@@ -17,6 +17,49 @@
};
};
+where the embedded structs are defined as:
+
+ struct android_log_header_t {
+ uint8_t id;
+ uint16_t tid;
+ log_time realtime;
+ };
+
+ struct log_time {
+ uint32_t tv_sec = 0;
+ uint32_t tv_nsec = 0;
+ }
+
+ struct android_event_header_t {
+ int32_t tag;
+ };
+
+ struct android_event_list_t {
+ int8_t type; // EVENT_TYPE_LIST
+ int8_t element_count;
+ };
+
+ struct android_event_float_t {
+ int8_t type; // EVENT_TYPE_FLOAT
+ float data;
+ };
+
+ struct android_event_int_t {
+ int8_t type; // EVENT_TYPE_INT
+ int32_t data;
+ } android_event_int_t;
+
+ struct android_event_long_t {
+ int8_t type; // EVENT_TYPE_LONG
+ int64_t data;
+ };
+
+ struct android_event_string_t {
+ int8_t type; // EVENT_TYPE_STRING;
+ int32_t length;
+ char data[];
+ };
+
The payload, excluding the header, has a max size of LOGGER_ENTRY_MAX_PAYLOAD.
## header
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index fffb809..385b079 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -96,7 +96,6 @@
cflags: ["-DNO_PSTORE"],
test_suites: [
"cts",
- "vts",
"vts10",
],
}
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index ee6ae7a..297036e 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -24,7 +24,7 @@
class Modprobe {
public:
- Modprobe(const std::vector<std::string>&);
+ Modprobe(const std::vector<std::string>&, const std::string load_file = "modules.load");
bool LoadListedModules(bool strict = true);
bool LoadWithAliases(const std::string& module_name, bool strict,
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index f22bbf1..d193796 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -312,7 +312,7 @@
}
}
-Modprobe::Modprobe(const std::vector<std::string>& base_paths) {
+Modprobe::Modprobe(const std::vector<std::string>& base_paths, const std::string load_file) {
using namespace std::placeholders;
for (const auto& base_path : base_paths) {
@@ -326,7 +326,7 @@
ParseCfg(base_path + "/modules.softdep", softdep_callback);
auto load_callback = std::bind(&Modprobe::ParseLoadCallback, this, _1);
- ParseCfg(base_path + "/modules.load", load_callback);
+ ParseCfg(base_path + "/" + load_file, load_callback);
auto options_callback = std::bind(&Modprobe::ParseOptionsCallback, this, _1);
ParseCfg(base_path + "/modules.options", options_callback);
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 2351afa..5efe03f 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -529,6 +529,10 @@
free(buf);
} else if (opthdr->nd_opt_type == ND_OPT_DNSSL) {
// TODO: support DNSSL.
+ } else if (opthdr->nd_opt_type == ND_OPT_CAPTIVE_PORTAL) {
+ // TODO: support CAPTIVE PORTAL.
+ } else if (opthdr->nd_opt_type == ND_OPT_PREF64) {
+ // TODO: support PREF64.
} else {
SLOGD("Unknown ND option type %d\n", opthdr->nd_opt_type);
return false;
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
index bf63abf..8fc3d23 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -50,6 +50,22 @@
std::unique_ptr<DexFile> DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory,
MapInfo* info) {
+ if (UNLIKELY(!HasDexSupport())) {
+ return nullptr;
+ }
+
+ size_t max_size = info->end - dex_file_offset_in_memory;
+ if (memory->IsLocal()) {
+ size_t size = max_size;
+
+ std::string err_msg;
+ std::unique_ptr<art_api::dex::DexFile> art_dex_file = DexFile::OpenFromMemory(
+ reinterpret_cast<void const*>(dex_file_offset_in_memory), &size, info->name, &err_msg);
+ if (art_dex_file != nullptr && size <= max_size) {
+ return std::unique_ptr<DexFile>(new DexFile(art_dex_file));
+ }
+ }
+
if (!info->name.empty()) {
std::unique_ptr<DexFile> dex_file =
DexFileFromFile::Create(dex_file_offset_in_memory - info->start + info->offset, info->name);
@@ -57,7 +73,7 @@
return dex_file;
}
}
- return DexFileFromMemory::Create(dex_file_offset_in_memory, memory, info->name);
+ return DexFileFromMemory::Create(dex_file_offset_in_memory, memory, info->name, max_size);
}
bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
@@ -94,7 +110,8 @@
std::unique_ptr<DexFileFromMemory> DexFileFromMemory::Create(uint64_t dex_file_offset_in_memory,
Memory* memory,
- const std::string& name) {
+ const std::string& name,
+ size_t max_size) {
if (UNLIKELY(!HasDexSupport())) {
return nullptr;
}
@@ -105,6 +122,9 @@
std::string error_msg;
std::unique_ptr<art_api::dex::DexFile> art_dex_file =
OpenFromMemory(backing_memory.data(), &size, name, &error_msg);
+ if (size > max_size) {
+ return nullptr;
+ }
if (art_dex_file != nullptr) {
return std::unique_ptr<DexFileFromMemory>(
diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h
index 4e8369f..fe185da 100644
--- a/libunwindstack/DexFile.h
+++ b/libunwindstack/DexFile.h
@@ -55,7 +55,8 @@
class DexFileFromMemory : public DexFile {
public:
static std::unique_ptr<DexFileFromMemory> Create(uint64_t dex_file_offset_in_memory,
- Memory* memory, const std::string& name);
+ Memory* memory, const std::string& name,
+ size_t max_size);
private:
DexFileFromMemory(std::unique_ptr<art_api::dex::DexFile>& art_dex_file,
diff --git a/libunwindstack/MemoryLocal.h b/libunwindstack/MemoryLocal.h
index 29aaf12..7e027cf 100644
--- a/libunwindstack/MemoryLocal.h
+++ b/libunwindstack/MemoryLocal.h
@@ -28,6 +28,8 @@
MemoryLocal() = default;
virtual ~MemoryLocal() = default;
+ bool IsLocal() const override { return true; }
+
size_t Read(uint64_t addr, void* dst, size_t size) override;
};
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index 3106564..ecd908a 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -41,6 +41,8 @@
virtual void Clear() {}
+ virtual bool IsLocal() const { return false; }
+
virtual size_t Read(uint64_t addr, void* dst, size_t size) = 0;
bool ReadFully(uint64_t addr, void* dst, size_t size);
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index dc935a3..1deba01 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -21,6 +21,7 @@
#include <unordered_map>
+#include <MemoryLocal.h>
#include <android-base/file.h>
#include <gtest/gtest.h>
#include <unwindstack/MapInfo.h>
@@ -109,7 +110,7 @@
memory.SetMemory(0x1000, kDexData, 10);
- EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") == nullptr);
+ EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "", sizeof(kDexData)) == nullptr);
}
TEST(DexFileTest, from_memory_fail_too_small_for_data) {
@@ -117,7 +118,7 @@
memory.SetMemory(0x1000, kDexData, sizeof(kDexData) - 2);
- EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") == nullptr);
+ EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "", sizeof(kDexData)) == nullptr);
}
TEST(DexFileTest, from_memory_open) {
@@ -125,7 +126,7 @@
memory.SetMemory(0x1000, kDexData, sizeof(kDexData));
- EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") != nullptr);
+ EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "", sizeof(kDexData)) != nullptr);
}
TEST(DexFileTest, from_memory_no_leak) {
@@ -136,7 +137,7 @@
size_t first_allocated_bytes = 0;
size_t last_allocated_bytes = 0;
for (size_t i = 0; i < kNumLeakLoops; i++) {
- EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") != nullptr);
+ EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "", sizeof(kDexData)) != nullptr);
ASSERT_NO_FATAL_FAILURE(CheckForLeak(i, &first_allocated_bytes, &last_allocated_bytes));
}
}
@@ -213,6 +214,43 @@
EXPECT_TRUE(dex_file == nullptr);
}
+TEST(DexFileTest, create_using_memory_size_too_small) {
+ MemoryFake memory;
+ memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+ MapInfo info(nullptr, nullptr, 0x100, sizeof(kDexData) - 2, 0x200, 0x5, "/does/not/exist");
+ EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != nullptr);
+}
+
+class MemoryLocalFake : public MemoryLocal {
+ public:
+ MemoryLocalFake(size_t memory_size) : backing_(memory_size) {}
+ virtual ~MemoryLocalFake() = default;
+
+ void* Data() { return backing_.data(); }
+
+ private:
+ std::vector<void*> backing_;
+};
+
+TEST(DexFileTest, create_using_local_memory) {
+ MemoryLocalFake memory(sizeof(kDexData));
+
+ memcpy(memory.Data(), kDexData, sizeof(kDexData));
+ uint64_t start = reinterpret_cast<uint64_t>(memory.Data());
+ MapInfo info(nullptr, nullptr, start, start + 0x1000, 0x200, 0x5, "/does/not/exist");
+ EXPECT_TRUE(DexFile::Create(start, &memory, &info) != nullptr);
+}
+
+TEST(DexFileTest, create_using_local_memory_size_too_small) {
+ MemoryLocalFake memory(sizeof(kDexData));
+
+ memcpy(memory.Data(), kDexData, sizeof(kDexData));
+ uint64_t start = reinterpret_cast<uint64_t>(memory.Data());
+ MapInfo info(nullptr, nullptr, start, start + sizeof(kDexData) - 2, 0x200, 0x5,
+ "/does/not/exist");
+ EXPECT_TRUE(DexFile::Create(start, &memory, &info) == nullptr);
+}
+
TEST(DexFileTest, get_method) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 4697bb7..3d51de9 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -25,6 +25,7 @@
#include <sys/cdefs.h>
#include <sys/types.h>
+#include <functional>
#include <string>
#include <string_view>
@@ -36,10 +37,10 @@
kCompressDeflated = 8, // standard deflate
};
-/*
- * Represents information about a zip entry in a zip file.
- */
-struct ZipEntry {
+// This struct holds the common information of a zip entry other than the
+// the entry size. The compressed and uncompressed length will be handled
+// separately in the derived class.
+struct ZipEntryCommon {
// Compression method. One of kCompressStored or kCompressDeflated.
// See also `gpbf` for deflate subtypes.
uint16_t method;
@@ -67,16 +68,6 @@
// Data descriptor footer at the end of the file entry.
uint32_t crc32;
- // Compressed length of this ZipEntry. Might be present
- // either in the local file header or in the data descriptor
- // footer.
- uint32_t compressed_length;
-
- // Uncompressed length of this ZipEntry. Might be present
- // either in the local file header or in the data descriptor
- // footer.
- uint32_t uncompressed_length;
-
// If the value of uncompressed length and compressed length are stored in
// the zip64 extended info of the extra field.
bool zip64_format_size{false};
@@ -97,6 +88,52 @@
bool is_text;
};
+struct ZipEntry64;
+// Many users of the library assume the entry size is capped at UNIT32_MAX. So we keep
+// the interface for the old ZipEntry here; and we could switch them over to the new
+// ZipEntry64 later.
+struct ZipEntry : public ZipEntryCommon {
+ // Compressed length of this ZipEntry. The maximum value is UNIT32_MAX.
+ // Might be present either in the local file header or in the data
+ // descriptor footer.
+ uint32_t compressed_length{0};
+
+ // Uncompressed length of this ZipEntry. The maximum value is UNIT32_MAX.
+ // Might be present either in the local file header or in the data
+ // descriptor footer.
+ uint32_t uncompressed_length{0};
+
+ // Copies the contents of a ZipEntry64 object to a 32 bits ZipEntry. Returns 0 if the
+ // size of the entry fits into uint32_t, returns a negative error code
+ // (kUnsupportedEntrySize) otherwise.
+ static int32_t CopyFromZipEntry64(ZipEntry* dst, const ZipEntry64* src);
+
+ private:
+ ZipEntry& operator=(const ZipEntryCommon& other) {
+ ZipEntryCommon::operator=(other);
+ return *this;
+ }
+};
+
+// Represents information about a zip entry in a zip file.
+struct ZipEntry64 : public ZipEntryCommon {
+ // Compressed length of this ZipEntry. The maximum value is UNIT64_MAX.
+ // Might be present either in the local file header, the zip64 extended field,
+ // or in the data descriptor footer.
+ uint64_t compressed_length{0};
+
+ // Uncompressed length of this ZipEntry. The maximum value is UNIT64_MAX.
+ // Might be present either in the local file header, the zip64 extended field,
+ // or in the data descriptor footer.
+ uint64_t uncompressed_length{0};
+
+ explicit ZipEntry64() = default;
+ explicit ZipEntry64(const ZipEntry& zip_entry) : ZipEntryCommon(zip_entry) {
+ compressed_length = zip_entry.compressed_length;
+ uncompressed_length = zip_entry.uncompressed_length;
+ }
+};
+
struct ZipArchive;
typedef ZipArchive* ZipArchiveHandle;
@@ -172,7 +209,8 @@
* On non-Windows platforms this method does not modify internal state and
* can be called concurrently.
*/
-int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, ZipEntry* data);
+int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
+ ZipEntry64* data);
/*
* Start iterating over all entries of a zip file. The order of iteration
@@ -206,8 +244,8 @@
* Returns 0 on success, -1 if there are no more elements in this
* archive and lower negative values on failure.
*/
-int32_t Next(void* cookie, ZipEntry* data, std::string* name);
-int32_t Next(void* cookie, ZipEntry* data, std::string_view* name);
+int32_t Next(void* cookie, ZipEntry64* data, std::string_view* name);
+int32_t Next(void* cookie, ZipEntry64* data, std::string* name);
/*
* End iteration over all entries of a zip file and frees the memory allocated
@@ -224,7 +262,7 @@
*
* Returns 0 on success and negative values on failure.
*/
-int32_t ExtractEntryToFile(ZipArchiveHandle archive, ZipEntry* entry, int fd);
+int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry64* entry, int fd);
/**
* Uncompress a given zip entry to the memory region at |begin| and of
@@ -234,7 +272,8 @@
*
* Returns 0 on success and negative values on failure.
*/
-int32_t ExtractToMemory(ZipArchiveHandle archive, ZipEntry* entry, uint8_t* begin, uint32_t size);
+int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry64* entry, uint8_t* begin,
+ size_t size);
int GetFileDescriptor(const ZipArchiveHandle archive);
@@ -246,6 +285,16 @@
const char* ErrorCodeString(int32_t error_code);
+// Many users of libziparchive assume the entry size to be 32 bits long. So we keep these
+// interfaces that use 32 bit ZipEntry to make old code work. TODO(xunchang) Remove the 32 bit
+// wrapper functions once we switch all users to recognize ZipEntry64.
+int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, ZipEntry* data);
+int32_t Next(void* cookie, ZipEntry* data, std::string* name);
+int32_t Next(void* cookie, ZipEntry* data, std::string_view* name);
+int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry* entry, int fd);
+int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry* entry, uint8_t* begin,
+ size_t size);
+
#if !defined(_WIN32)
typedef bool (*ProcessZipEntryFunction)(const uint8_t* buf, size_t buf_size, void* cookie);
@@ -253,7 +302,9 @@
* Stream the uncompressed data through the supplied function,
* passing cookie to it each time it gets called.
*/
-int32_t ProcessZipEntryContents(ZipArchiveHandle archive, ZipEntry* entry,
+int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry* entry,
+ ProcessZipEntryFunction func, void* cookie);
+int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry64* entry,
ProcessZipEntryFunction func, void* cookie);
#endif
@@ -274,7 +325,7 @@
class Reader {
public:
- virtual bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const = 0;
+ virtual bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const = 0;
virtual ~Reader();
protected:
@@ -296,6 +347,6 @@
* If |crc_out| is not nullptr, it is set to the crc32 checksum of the
* uncompressed data.
*/
-int32_t Inflate(const Reader& reader, const uint32_t compressed_length,
- const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out);
+int32_t Inflate(const Reader& reader, const uint64_t compressed_length,
+ const uint64_t uncompressed_length, Writer* writer, uint64_t* crc_out);
} // namespace zip_archive
diff --git a/libziparchive/test_ziparchive_large.py b/libziparchive/test_ziparchive_large.py
index 6b82f63..46d02aa 100644
--- a/libziparchive/test_ziparchive_large.py
+++ b/libziparchive/test_ziparchive_large.py
@@ -83,7 +83,7 @@
self._ExtractEntries(zip_path.name)
- def test_largeCompressedEntries(self):
+ def test_largeCompressedEntriesSmallerThan4G(self):
zip_path = tempfile.NamedTemporaryFile(suffix='.zip')
with zipfile.ZipFile(zip_path, 'w', compression=zipfile.ZIP_DEFLATED,
allowZip64=True) as output_zip:
@@ -99,8 +99,7 @@
def test_forceDataDescriptor(self):
file_path = tempfile.NamedTemporaryFile(suffix='.txt')
- # TODO create the entry > 4GiB.
- self._WriteFile(file_path.name, 1024)
+ self._WriteFile(file_path.name, 5000 * 1024)
zip_path = tempfile.NamedTemporaryFile(suffix='.zip')
with zipfile.ZipFile(zip_path, 'w', allowZip64=True) as output_zip:
@@ -113,6 +112,35 @@
self.assertEquals([file_path.name[1:]], read_names)
self._ExtractEntries(zip_path.name)
+
+ def test_largeUncompressedEntriesLargerThan4G(self):
+ zip_path = tempfile.NamedTemporaryFile(suffix='.zip')
+ with zipfile.ZipFile(zip_path, 'w', compression=zipfile.ZIP_STORED,
+ allowZip64=True) as output_zip:
+ # Add entries close to 4GiB in size. Somehow the python library will put the (un)compressed
+ # sizes in the extra field. Test if our ziptool should be able to parse it.
+ entry_dict = {'g.txt': 5000 * 1024, 'h.txt': 6000 * 1024}
+ self._AddEntriesToZip(output_zip, entry_dict)
+
+ read_names = self._getEntryNames(zip_path.name)
+ self.assertEquals(sorted(entry_dict.keys()), sorted(read_names))
+ self._ExtractEntries(zip_path.name)
+
+
+ def test_largeCompressedEntriesLargerThan4G(self):
+ zip_path = tempfile.NamedTemporaryFile(suffix='.zip')
+ with zipfile.ZipFile(zip_path, 'w', compression=zipfile.ZIP_DEFLATED,
+ allowZip64=True) as output_zip:
+ # Add entries close to 4GiB in size. Somehow the python library will put the (un)compressed
+ # sizes in the extra field. Test if our ziptool should be able to parse it.
+ entry_dict = {'i.txt': 4096 * 1024, 'j.txt': 7000 * 1024}
+ self._AddEntriesToZip(output_zip, entry_dict)
+
+ read_names = self._getEntryNames(zip_path.name)
+ self.assertEquals(sorted(entry_dict.keys()), sorted(read_names))
+ self._ExtractEntries(zip_path.name)
+
+
if __name__ == '__main__':
testsuite = unittest.TestLoader().discover(
os.path.dirname(os.path.realpath(__file__)))
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 031d43a..7658d5c 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -406,15 +406,6 @@
return kInvalidFile;
}
- // TODO(xunchang) Support handling file large than UINT32_MAX. It's theoretically possible
- // for libz to (de)compressing file larger than UINT32_MAX. But we should use our own
- // bytes counter to replace stream.total_out.
- if ((uncompressedFileSize.has_value() && uncompressedFileSize.value() > UINT32_MAX) ||
- (compressedFileSize.has_value() && compressedFileSize.value() > UINT32_MAX)) {
- ALOGW("Zip: File size larger than UINT32_MAX isn't supported yet");
- return kInvalidFile;
- }
-
zip64Info->uncompressed_file_size = uncompressedFileSize;
zip64Info->compressed_file_size = compressedFileSize;
zip64Info->local_header_offset = localHeaderOffset;
@@ -613,7 +604,7 @@
delete archive;
}
-static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, ZipEntry* entry) {
+static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, const ZipEntry64* entry) {
// Maximum possible size for data descriptor: 2 * 4 + 2 * 8 = 24 bytes
uint8_t ddBuf[24];
off64_t offset = entry->offset;
@@ -644,7 +635,7 @@
if (entry->compressed_length != descriptor.compressed_size ||
entry->uncompressed_length != descriptor.uncompressed_size ||
entry->crc32 != descriptor.crc32) {
- ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32
+ ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu64 ", %" PRIu64 ", %" PRIx32
"}, was {%" PRIu64 ", %" PRIu64 ", %" PRIx32 "}",
entry->compressed_length, entry->uncompressed_length, entry->crc32,
descriptor.compressed_size, descriptor.uncompressed_size, descriptor.crc32);
@@ -655,7 +646,7 @@
}
static int32_t FindEntry(const ZipArchive* archive, std::string_view entryName,
- const uint64_t nameOffset, ZipEntry* data) {
+ const uint64_t nameOffset, ZipEntry64* data) {
// Recover the start of the central directory entry from the filename
// pointer. The filename is the first entry past the fixed-size data,
// so we can just subtract back from that.
@@ -704,11 +695,8 @@
return status;
}
- // TODO(xunchang) remove the size limit and support entry length > UINT32_MAX.
- data->uncompressed_length =
- static_cast<uint32_t>(zip64_info.uncompressed_file_size.value_or(cdr->uncompressed_size));
- data->compressed_length =
- static_cast<uint32_t>(zip64_info.compressed_file_size.value_or(cdr->compressed_size));
+ data->uncompressed_length = zip64_info.uncompressed_file_size.value_or(cdr->uncompressed_size);
+ data->compressed_length = zip64_info.compressed_file_size.value_or(cdr->compressed_size);
local_header_offset = zip64_info.local_header_offset.value_or(local_header_offset);
data->zip64_format_size =
cdr->uncompressed_size == UINT32_MAX || cdr->compressed_size == UINT32_MAX;
@@ -822,7 +810,7 @@
data->has_data_descriptor = 0;
if (data->compressed_length != lfh_compressed_size ||
data->uncompressed_length != lfh_uncompressed_size || data->crc32 != lfh->crc32) {
- ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32
+ ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu64 ", %" PRIu64 ", %" PRIx32
"}, was {%" PRIu64 ", %" PRIu64 ", %" PRIx32 "}",
data->compressed_length, data->uncompressed_length, data->crc32, lfh_compressed_size,
lfh_uncompressed_size, lfh->crc32);
@@ -855,16 +843,15 @@
return kInvalidOffset;
}
- if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) {
- ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
+ if (data->compressed_length > cd_offset - data_offset) {
+ ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu64 " > %" PRId64 ")",
static_cast<int64_t>(data_offset), data->compressed_length,
static_cast<int64_t>(cd_offset));
return kInvalidOffset;
}
- if (data->method == kCompressStored &&
- static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
- ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
+ if (data->method == kCompressStored && data->uncompressed_length > cd_offset - data_offset) {
+ ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu64 " > %" PRId64 ")",
static_cast<int64_t>(data_offset), data->uncompressed_length,
static_cast<int64_t>(cd_offset));
return kInvalidOffset;
@@ -918,8 +905,33 @@
delete reinterpret_cast<IterationHandle*>(cookie);
}
+int32_t ZipEntry::CopyFromZipEntry64(ZipEntry* dst, const ZipEntry64* src) {
+ if (src->compressed_length > UINT32_MAX || src->uncompressed_length > UINT32_MAX) {
+ ALOGW(
+ "Zip: the entry size is too large to fit into the 32 bits ZipEntry, uncompressed "
+ "length %" PRIu64 ", compressed length %" PRIu64,
+ src->uncompressed_length, src->compressed_length);
+ return kUnsupportedEntrySize;
+ }
+
+ *dst = *src;
+ dst->uncompressed_length = static_cast<uint32_t>(src->uncompressed_length);
+ dst->compressed_length = static_cast<uint32_t>(src->compressed_length);
+ return kSuccess;
+}
+
int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
ZipEntry* data) {
+ ZipEntry64 entry64;
+ if (auto status = FindEntry(archive, entryName, &entry64); status != kSuccess) {
+ return status;
+ }
+
+ return ZipEntry::CopyFromZipEntry64(data, &entry64);
+}
+
+int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
+ ZipEntry64* data) {
if (entryName.empty() || entryName.size() > static_cast<size_t>(UINT16_MAX)) {
ALOGW("Zip: Invalid filename of length %zu", entryName.size());
return kInvalidEntryName;
@@ -936,6 +948,24 @@
}
int32_t Next(void* cookie, ZipEntry* data, std::string* name) {
+ ZipEntry64 entry64;
+ if (auto status = Next(cookie, &entry64, name); status != kSuccess) {
+ return status;
+ }
+
+ return ZipEntry::CopyFromZipEntry64(data, &entry64);
+}
+
+int32_t Next(void* cookie, ZipEntry* data, std::string_view* name) {
+ ZipEntry64 entry64;
+ if (auto status = Next(cookie, &entry64, name); status != kSuccess) {
+ return status;
+ }
+
+ return ZipEntry::CopyFromZipEntry64(data, &entry64);
+}
+
+int32_t Next(void* cookie, ZipEntry64* data, std::string* name) {
std::string_view sv;
int32_t result = Next(cookie, data, &sv);
if (result == 0 && name) {
@@ -944,7 +974,7 @@
return result;
}
-int32_t Next(void* cookie, ZipEntry* data, std::string_view* name) {
+int32_t Next(void* cookie, ZipEntry64* data, std::string_view* name) {
IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
if (handle == nullptr) {
ALOGW("Zip: Null ZipArchiveHandle");
@@ -979,10 +1009,19 @@
// the data appended to it.
class MemoryWriter : public zip_archive::Writer {
public:
- MemoryWriter(uint8_t* buf, size_t size) : Writer(), buf_(buf), size_(size), bytes_written_(0) {}
+ static std::unique_ptr<MemoryWriter> Create(uint8_t* buf, size_t size, const ZipEntry64* entry) {
+ const uint64_t declared_length = entry->uncompressed_length;
+ if (declared_length > size) {
+ ALOGW("Zip: file size %" PRIu64 " is larger than the buffer size %zu.", declared_length,
+ size);
+ return nullptr;
+ }
+
+ return std::unique_ptr<MemoryWriter>(new MemoryWriter(buf, size));
+ }
virtual bool Append(uint8_t* buf, size_t buf_size) override {
- if (bytes_written_ + buf_size > size_) {
+ if (size_ < buf_size || bytes_written_ > size_ - buf_size) {
ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", size_,
bytes_written_ + buf_size);
return false;
@@ -994,7 +1033,9 @@
}
private:
- uint8_t* const buf_;
+ MemoryWriter(uint8_t* buf, size_t size) : Writer(), buf_(buf), size_(size), bytes_written_(0) {}
+
+ uint8_t* const buf_{nullptr};
const size_t size_;
size_t bytes_written_;
};
@@ -1010,12 +1051,17 @@
// block device).
//
// Returns a valid FileWriter on success, |nullptr| if an error occurred.
- static FileWriter Create(int fd, const ZipEntry* entry) {
- const uint32_t declared_length = entry->uncompressed_length;
+ static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry64* entry) {
+ const uint64_t declared_length = entry->uncompressed_length;
const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
if (current_offset == -1) {
ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
- return FileWriter{};
+ return nullptr;
+ }
+
+ if (declared_length > SIZE_MAX || declared_length > INT64_MAX) {
+ ALOGW("Zip: file size %" PRIu64 " is too large to extract.", declared_length);
+ return nullptr;
}
#if defined(__linux__)
@@ -1031,10 +1077,9 @@
// disk does not have enough space.
long result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
if (result == -1 && errno == ENOSPC) {
- ALOGW("Zip: unable to allocate %" PRId64 " bytes at offset %" PRId64 ": %s",
- static_cast<int64_t>(declared_length), static_cast<int64_t>(current_offset),
- strerror(errno));
- return FileWriter{};
+ ALOGW("Zip: unable to allocate %" PRIu64 " bytes at offset %" PRId64 ": %s",
+ declared_length, static_cast<int64_t>(current_offset), strerror(errno));
+ return nullptr;
}
}
#endif // __linux__
@@ -1042,7 +1087,7 @@
struct stat sb;
if (fstat(fd, &sb) == -1) {
ALOGW("Zip: unable to fstat file: %s", strerror(errno));
- return FileWriter{};
+ return nullptr;
}
// Block device doesn't support ftruncate(2).
@@ -1051,11 +1096,11 @@
if (result == -1) {
ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
static_cast<int64_t>(declared_length + current_offset), strerror(errno));
- return FileWriter{};
+ return nullptr;
}
}
- return FileWriter(fd, declared_length);
+ return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
}
FileWriter(FileWriter&& other) noexcept
@@ -1065,11 +1110,9 @@
other.fd_ = -1;
}
- bool IsValid() const { return fd_ != -1; }
-
virtual bool Append(uint8_t* buf, size_t buf_size) override {
- if (total_bytes_written_ + buf_size > declared_length_) {
- ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", declared_length_,
+ if (declared_length_ < buf_size || total_bytes_written_ > declared_length_ - buf_size) {
+ ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", declared_length_,
total_bytes_written_ + buf_size);
return false;
}
@@ -1085,8 +1128,13 @@
}
private:
- explicit FileWriter(const int fd = -1, const size_t declared_length = 0)
- : Writer(), fd_(fd), declared_length_(declared_length), total_bytes_written_(0) {}
+ explicit FileWriter(const int fd = -1, const uint64_t declared_length = 0)
+ : Writer(),
+ fd_(fd),
+ declared_length_(static_cast<size_t>(declared_length)),
+ total_bytes_written_(0) {
+ CHECK_LE(declared_length, SIZE_MAX);
+ }
int fd_;
const size_t declared_length_;
@@ -1095,10 +1143,10 @@
class EntryReader : public zip_archive::Reader {
public:
- EntryReader(const MappedZipFile& zip_file, const ZipEntry* entry)
+ EntryReader(const MappedZipFile& zip_file, const ZipEntry64* entry)
: Reader(), zip_file_(zip_file), entry_(entry) {}
- virtual bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
+ virtual bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
return zip_file_.ReadAtOffset(buf, len, entry_->offset + offset);
}
@@ -1106,7 +1154,7 @@
private:
const MappedZipFile& zip_file_;
- const ZipEntry* entry_;
+ const ZipEntry64* entry_;
};
// This method is using libz macros with old-style-casts
@@ -1123,8 +1171,8 @@
Reader::~Reader() {}
Writer::~Writer() {}
-int32_t Inflate(const Reader& reader, const uint32_t compressed_length,
- const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out) {
+int32_t Inflate(const Reader& reader, const uint64_t compressed_length,
+ const uint64_t uncompressed_length, Writer* writer, uint64_t* crc_out) {
const size_t kBufSize = 32768;
std::vector<uint8_t> read_buf(kBufSize);
std::vector<uint8_t> write_buf(kBufSize);
@@ -1167,12 +1215,14 @@
const bool compute_crc = (crc_out != nullptr);
uLong crc = 0;
- uint32_t remaining_bytes = compressed_length;
+ uint64_t remaining_bytes = compressed_length;
+ uint64_t total_output = 0;
do {
/* read as much as we can */
if (zstream.avail_in == 0) {
- const uint32_t read_size = (remaining_bytes > kBufSize) ? kBufSize : remaining_bytes;
- const uint32_t offset = (compressed_length - remaining_bytes);
+ const uint32_t read_size =
+ (remaining_bytes > kBufSize) ? kBufSize : static_cast<uint32_t>(remaining_bytes);
+ const off64_t offset = (compressed_length - remaining_bytes);
// Make sure to read at offset to ensure concurrent access to the fd.
if (!reader.ReadAtOffset(read_buf.data(), read_size, offset)) {
ALOGW("Zip: inflate read failed, getSize = %u: %s", read_size, strerror(errno));
@@ -1203,6 +1253,7 @@
crc = crc32(crc, &write_buf[0], static_cast<uint32_t>(write_size));
}
+ total_output += kBufSize - zstream.avail_out;
zstream.next_out = &write_buf[0];
zstream.avail_out = kBufSize;
}
@@ -1219,9 +1270,8 @@
if (compute_crc) {
*crc_out = crc;
}
-
- if (zstream.total_out != uncompressed_length || remaining_bytes != 0) {
- ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")", zstream.total_out,
+ if (total_output != uncompressed_length || remaining_bytes != 0) {
+ ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu64 ")", zstream.total_out,
uncompressed_length);
return kInconsistentInformation;
}
@@ -1230,7 +1280,7 @@
}
} // namespace zip_archive
-static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
+static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry64* entry,
zip_archive::Writer* writer, uint64_t* crc_out) {
const EntryReader reader(mapped_zip, entry);
@@ -1238,20 +1288,21 @@
crc_out);
}
-static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
+static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry64* entry,
zip_archive::Writer* writer, uint64_t* crc_out) {
static const uint32_t kBufSize = 32768;
std::vector<uint8_t> buf(kBufSize);
- const uint32_t length = entry->uncompressed_length;
- uint32_t count = 0;
+ const uint64_t length = entry->uncompressed_length;
+ uint64_t count = 0;
uLong crc = 0;
while (count < length) {
- uint32_t remaining = length - count;
+ uint64_t remaining = length - count;
off64_t offset = entry->offset + count;
// Safe conversion because kBufSize is narrow enough for a 32 bit signed value.
- const uint32_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
+ const uint32_t block_size =
+ (remaining > kBufSize) ? kBufSize : static_cast<uint32_t>(remaining);
// Make sure to read at offset to ensure concurrent access to the fd.
if (!mapped_zip.ReadAtOffset(buf.data(), block_size, offset)) {
@@ -1272,20 +1323,21 @@
return 0;
}
-int32_t ExtractToWriter(ZipArchiveHandle archive, ZipEntry* entry, zip_archive::Writer* writer) {
+int32_t ExtractToWriter(ZipArchiveHandle handle, const ZipEntry64* entry,
+ zip_archive::Writer* writer) {
const uint16_t method = entry->method;
// this should default to kUnknownCompressionMethod.
int32_t return_value = -1;
uint64_t crc = 0;
if (method == kCompressStored) {
- return_value = CopyEntryToWriter(archive->mapped_zip, entry, writer, &crc);
+ return_value = CopyEntryToWriter(handle->mapped_zip, entry, writer, &crc);
} else if (method == kCompressDeflated) {
- return_value = InflateEntryToWriter(archive->mapped_zip, entry, writer, &crc);
+ return_value = InflateEntryToWriter(handle->mapped_zip, entry, writer, &crc);
}
if (!return_value && entry->has_data_descriptor) {
- return_value = ValidateDataDescriptor(archive->mapped_zip, entry);
+ return_value = ValidateDataDescriptor(handle->mapped_zip, entry);
if (return_value) {
return return_value;
}
@@ -1300,18 +1352,34 @@
return return_value;
}
-int32_t ExtractToMemory(ZipArchiveHandle archive, ZipEntry* entry, uint8_t* begin, uint32_t size) {
- MemoryWriter writer(begin, size);
- return ExtractToWriter(archive, entry, &writer);
+int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry* entry, uint8_t* begin,
+ size_t size) {
+ ZipEntry64 entry64(*entry);
+ return ExtractToMemory(archive, &entry64, begin, size);
}
-int32_t ExtractEntryToFile(ZipArchiveHandle archive, ZipEntry* entry, int fd) {
- auto writer = FileWriter::Create(fd, entry);
- if (!writer.IsValid()) {
+int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry64* entry, uint8_t* begin,
+ size_t size) {
+ auto writer = MemoryWriter::Create(begin, size, entry);
+ if (!writer) {
return kIoError;
}
- return ExtractToWriter(archive, entry, &writer);
+ return ExtractToWriter(archive, entry, writer.get());
+}
+
+int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry* entry, int fd) {
+ ZipEntry64 entry64(*entry);
+ return ExtractEntryToFile(archive, &entry64, fd);
+}
+
+int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry64* entry, int fd) {
+ auto writer = FileWriter::Create(fd, entry);
+ if (!writer) {
+ return kIoError;
+ }
+
+ return ExtractToWriter(archive, entry, writer.get());
}
int GetFileDescriptor(const ZipArchiveHandle archive) {
@@ -1337,7 +1405,13 @@
void* cookie_;
};
-int32_t ProcessZipEntryContents(ZipArchiveHandle archive, ZipEntry* entry,
+int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry* entry,
+ ProcessZipEntryFunction func, void* cookie) {
+ ZipEntry64 entry64(*entry);
+ return ProcessZipEntryContents(archive, &entry64, func, cookie);
+}
+
+int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry64* entry,
ProcessZipEntryFunction func, void* cookie) {
ProcessWriter writer(func, cookie);
return ExtractToWriter(archive, entry, &writer);
@@ -1466,7 +1540,7 @@
return true;
}
-tm ZipEntry::GetModificationTime() const {
+tm ZipEntryCommon::GetModificationTime() const {
tm t = {};
t.tm_hour = (mod_time >> 11) & 0x1f;
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 4ed07aa..4b43cba 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -106,7 +106,8 @@
bool InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size);
};
-int32_t ExtractToWriter(ZipArchiveHandle handle, ZipEntry* entry, zip_archive::Writer* writer);
+int32_t ExtractToWriter(ZipArchiveHandle handle, const ZipEntry64* entry,
+ zip_archive::Writer* writer);
// Reads the unaligned data of type |T| and auto increment the offset.
template <typename T>
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 3563340..f5429be 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -217,7 +217,7 @@
void* iteration_cookie;
ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
- ZipEntry data;
+ ZipEntry64 data;
std::vector<std::string_view> names;
std::string_view name;
while (Next(iteration_cookie, &data, &name) == 0) names.push_back(name);
@@ -232,12 +232,12 @@
static void AssertIterationNames(void* iteration_cookie,
const std::vector<std::string>& expected_names_sorted) {
- ZipEntry data;
+ ZipEntry64 data;
std::vector<std::string> names;
- std::string name;
+ std::string_view name;
for (size_t i = 0; i < expected_names_sorted.size(); ++i) {
ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
- names.push_back(name);
+ names.push_back(std::string(name));
}
// End of iteration.
ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
@@ -325,8 +325,8 @@
void* iteration_cookie;
ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, "x", "y"));
- ZipEntry data;
- std::string name;
+ ZipEntry64 data;
+ std::string_view name;
// End of iteration.
ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
@@ -338,14 +338,14 @@
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
- ZipEntry data;
+ ZipEntry64 data;
ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
// Known facts about a.txt, from zipinfo -v.
ASSERT_EQ(63, data.offset);
ASSERT_EQ(kCompressDeflated, data.method);
- ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
- ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
+ ASSERT_EQ(17u, data.uncompressed_length);
+ ASSERT_EQ(13u, data.compressed_length);
ASSERT_EQ(0x950821c5, data.crc32);
ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
@@ -359,7 +359,7 @@
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
- ZipEntry data;
+ ZipEntry64 data;
ASSERT_EQ(kInvalidEntryName, FindEntry(handle, "", &data));
CloseArchive(handle);
@@ -370,7 +370,7 @@
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
std::string very_long_name(65536, 'x');
- ZipEntry data;
+ ZipEntry64 data;
ASSERT_EQ(kInvalidEntryName, FindEntry(handle, very_long_name, &data));
CloseArchive(handle);
@@ -383,8 +383,8 @@
void* iteration_cookie;
ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
- std::string name;
- ZipEntry data;
+ std::string_view name;
+ ZipEntry64 data;
ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
@@ -415,9 +415,9 @@
static_cast<off64_t>(leading_garbage.size())));
// An entry that's deflated.
- ZipEntry data;
+ ZipEntry64 data;
ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
- const uint32_t a_size = data.uncompressed_length;
+ const auto a_size = static_cast<size_t>(data.uncompressed_length);
ASSERT_EQ(a_size, kATxtContents.size());
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[a_size]);
ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), a_size));
@@ -425,7 +425,7 @@
// An entry that's stored.
ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
- const uint32_t b_size = data.uncompressed_length;
+ const auto b_size = static_cast<size_t>(data.uncompressed_length);
ASSERT_EQ(b_size, kBTxtContents.size());
buffer = std::unique_ptr<uint8_t[]>(new uint8_t[b_size]);
ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), b_size));
@@ -439,9 +439,9 @@
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
// An entry that's deflated.
- ZipEntry data;
+ ZipEntry64 data;
ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
- const uint32_t a_size = data.uncompressed_length;
+ const auto a_size = static_cast<size_t>(data.uncompressed_length);
ASSERT_EQ(a_size, kATxtContents.size());
uint8_t* buffer = new uint8_t[a_size];
ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
@@ -450,7 +450,7 @@
// An entry that's stored.
ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
- const uint32_t b_size = data.uncompressed_length;
+ const auto b_size = static_cast<size_t>(data.uncompressed_length);
ASSERT_EQ(b_size, kBTxtContents.size());
buffer = new uint8_t[b_size];
ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
@@ -503,11 +503,14 @@
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
- ZipEntry entry;
+ ZipEntry64 entry;
ASSERT_EQ(0, FindEntry(handle, "empty.txt", &entry));
- ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
+ ASSERT_EQ(0u, entry.uncompressed_length);
+ // Extraction to a 1 byte buffer should succeed.
uint8_t buffer[1];
ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
+ // Extraction to an empty buffer should succeed.
+ ASSERT_EQ(0, ExtractToMemory(handle, &entry, nullptr, 0));
TemporaryFile tmp_output_file;
ASSERT_NE(-1, tmp_output_file.fd);
@@ -526,7 +529,7 @@
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
- ZipEntry entry;
+ ZipEntry64 entry;
ASSERT_EQ(0, FindEntry(handle, kAbTxtName, &entry));
ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
@@ -583,7 +586,7 @@
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
- ZipEntry entry;
+ ZipEntry64 entry;
ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
@@ -594,9 +597,9 @@
ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
// Assert that the remainder of the file contains the incompressed data.
- std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
- ASSERT_TRUE(
- android::base::ReadFully(tmp_file.fd, uncompressed_data.data(), entry.uncompressed_length));
+ std::vector<uint8_t> uncompressed_data(static_cast<size_t>(entry.uncompressed_length));
+ ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(),
+ static_cast<size_t>(entry.uncompressed_length)));
ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), kATxtContents.size()));
// Assert that the total length of the file is sane
@@ -620,7 +623,7 @@
OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
// Assert one entry can be found and extracted correctly.
- ZipEntry binary_entry;
+ ZipEntry64 binary_entry;
ASSERT_EQ(0, FindEntry(handle, "META-INF/com/google/android/update-binary", &binary_entry));
TemporaryFile tmp_binary;
ASSERT_NE(-1, tmp_binary.fd);
@@ -635,13 +638,13 @@
if (raw) {
stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
if (entry->method == kCompressStored) {
- read_data->resize(entry->uncompressed_length);
+ read_data->resize(static_cast<size_t>(entry->uncompressed_length));
} else {
- read_data->resize(entry->compressed_length);
+ read_data->resize(static_cast<size_t>(entry->compressed_length));
}
} else {
stream.reset(ZipArchiveStreamEntry::Create(handle, *entry));
- read_data->resize(entry->uncompressed_length);
+ read_data->resize(static_cast<size_t>(entry->uncompressed_length));
}
uint8_t* read_data_ptr = read_data->data();
ASSERT_TRUE(stream.get() != nullptr);
@@ -681,7 +684,7 @@
std::vector<uint8_t> read_data;
ZipArchiveStreamTest(handle, entry_name, false, true, &entry, &read_data);
- std::vector<uint8_t> cmp_data(entry.uncompressed_length);
+ std::vector<uint8_t> cmp_data(static_cast<size_t>(entry.uncompressed_length));
ASSERT_EQ(entry.uncompressed_length, read_data.size());
ASSERT_EQ(
0, ExtractToMemory(handle, &entry, cmp_data.data(), static_cast<uint32_t>(cmp_data.size())));
@@ -741,8 +744,8 @@
// FileOutputStream fos = new
// FileOutputStream("/tmp/data_descriptor.zip");
// ZipOutputStream zos = new ZipOutputStream(fos);
-// ZipEntry ze = new ZipEntry("name");
-// ze.setMethod(ZipEntry.DEFLATED);
+// ZipEntry64 ze = new ZipEntry64("name");
+// ze.setMethod(ZipEntry64.DEFLATED);
// zos.putNextEntry(ze);
// zos.write("abdcdefghijk".getBytes());
// zos.closeEntry();
@@ -780,9 +783,9 @@
// This function expects a variant of kDataDescriptorZipFile, for look for
// an entry whose name is "name" and whose size is 12 (contents =
// "abdcdefghijk").
- ZipEntry entry;
+ ZipEntry64 entry;
ASSERT_EQ(0, FindEntry(handle, "name", &entry));
- ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
+ ASSERT_EQ(12u, entry.uncompressed_length);
entry_out->resize(12);
(*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12);
@@ -887,12 +890,12 @@
public:
VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}
- bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
+ bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
if ((offset + len) < input_.size()) {
return false;
}
- memcpy(buf, &input_[offset], len);
+ memcpy(buf, &input_[static_cast<size_t>(offset)], len);
return true;
}
@@ -919,7 +922,7 @@
public:
BadReader() : Reader() {}
- bool ReadAtOffset(uint8_t*, size_t, uint32_t) const { return false; }
+ bool ReadAtOffset(uint8_t*, size_t, off64_t) const { return false; }
};
class BadWriter : public zip_archive::Writer {
@@ -1222,7 +1225,7 @@
ZipArchiveHandle handle;
ASSERT_EQ(
0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
- ZipEntry entry;
+ ZipEntry64 entry;
ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
ASSERT_EQ(200, entry.uncompressed_length);
ASSERT_EQ(200, entry.compressed_length);
@@ -1245,7 +1248,7 @@
ZipArchiveHandle handle;
ASSERT_EQ(
0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
- ZipEntry entry;
+ ZipEntry64 entry;
ASSERT_NE(0, FindEntry(handle, "a.txt", &entry));
CloseArchive(handle);
@@ -1267,7 +1270,7 @@
ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
std::set<std::string_view> result;
std::string_view name;
- ZipEntry entry;
+ ZipEntry64 entry;
while (Next(iteration_cookie, &entry, &name) == 0) result.emplace(name);
ASSERT_EQ(names, result);
@@ -1297,7 +1300,7 @@
ZipArchiveHandle handle;
ASSERT_EQ(
0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
- ZipEntry entry;
+ ZipEntry64 entry;
ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
VectorWriter writer;
@@ -1315,7 +1318,7 @@
ZipArchiveHandle handle;
ASSERT_EQ(
0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
- ZipEntry entry;
+ ZipEntry64 entry;
ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
VectorWriter writer;
diff --git a/libziparchive/zip_error.cpp b/libziparchive/zip_error.cpp
index 107ec47..14e49bb 100644
--- a/libziparchive/zip_error.cpp
+++ b/libziparchive/zip_error.cpp
@@ -33,6 +33,7 @@
"I/O error",
"File mapping failed",
"Allocation failed",
+ "Unsupported zip entry size",
};
const char* ErrorCodeString(int32_t error_code) {
diff --git a/libziparchive/zip_error.h b/libziparchive/zip_error.h
index 37fd55f..3d7285d 100644
--- a/libziparchive/zip_error.h
+++ b/libziparchive/zip_error.h
@@ -66,5 +66,9 @@
// An allocation failed.
kAllocationFailed = -13,
- kLastErrorCode = kAllocationFailed,
+ // The compressed or uncompressed size is larger than UINT32_MAX and
+ // doesn't fit into the 32 bits zip entry.
+ kUnsupportedEntrySize = -14,
+
+ kLastErrorCode = kUnsupportedEntrySize,
};
diff --git a/libziparchive/ziptool.cpp b/libziparchive/ziptool.cpp
index f345ffc..17d4833 100644
--- a/libziparchive/ziptool.cpp
+++ b/libziparchive/ziptool.cpp
@@ -193,21 +193,25 @@
}
}
-static void ExtractToPipe(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+static void ExtractToPipe(ZipArchiveHandle zah, const ZipEntry64& entry, const std::string& name) {
// We need to extract to memory because ExtractEntryToFile insists on
// being able to seek and truncate, and you can't do that with stdout.
- uint8_t* buffer = new uint8_t[entry.uncompressed_length];
- int err = ExtractToMemory(zah, &entry, buffer, entry.uncompressed_length);
+ if (entry.uncompressed_length > SIZE_MAX) {
+ die(0, "entry size %" PRIu64 " is too large to extract.", entry.uncompressed_length);
+ }
+ auto uncompressed_length = static_cast<size_t>(entry.uncompressed_length);
+ uint8_t* buffer = new uint8_t[uncompressed_length];
+ int err = ExtractToMemory(zah, &entry, buffer, uncompressed_length);
if (err < 0) {
die(0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err));
}
- if (!android::base::WriteFully(1, buffer, entry.uncompressed_length)) {
+ if (!android::base::WriteFully(1, buffer, uncompressed_length)) {
die(errno, "failed to write %s to stdout", name.c_str());
}
delete[] buffer;
}
-static void ExtractOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+static void ExtractOne(ZipArchiveHandle zah, const ZipEntry64& entry, const std::string& name) {
// Bad filename?
if (StartsWith(name, "/") || StartsWith(name, "../") || name.find("/../") != std::string::npos) {
die(0, "bad filename %s", name.c_str());
@@ -253,22 +257,22 @@
close(fd);
}
-static void ListOne(const ZipEntry& entry, const std::string& name) {
+static void ListOne(const ZipEntry64& entry, const std::string& name) {
tm t = entry.GetModificationTime();
char time[32];
snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
t.tm_mday, t.tm_hour, t.tm_min);
if (flag_v) {
- printf("%8d %s %7d %3.0f%% %s %08x %s\n", entry.uncompressed_length,
+ printf("%8" PRIu64 " %s %8" PRIu64 " %3.0f%% %s %08x %s\n", entry.uncompressed_length,
(entry.method == kCompressStored) ? "Stored" : "Defl:N", entry.compressed_length,
CompressionRatio(entry.uncompressed_length, entry.compressed_length), time, entry.crc32,
name.c_str());
} else {
- printf("%9d %s %s\n", entry.uncompressed_length, time, name.c_str());
+ printf("%9" PRIu64 " %s %s\n", entry.uncompressed_length, time, name.c_str());
}
}
-static void InfoOne(const ZipEntry& entry, const std::string& name) {
+static void InfoOne(const ZipEntry64& entry, const std::string& name) {
if (flag_1) {
// "android-ndk-r19b/sources/android/NOTICE"
printf("%s\n", name.c_str());
@@ -323,12 +327,12 @@
t.tm_mday, t.tm_hour, t.tm_min);
// "-rw-r--r-- 3.0 unx 577 t- defX 19-Feb-12 16:09 android-ndk-r19b/sources/android/NOTICE"
- printf("%s %2d.%d %s %8d %c%c %s %s %s\n", mode, version / 10, version % 10, src_fs,
+ printf("%s %2d.%d %s %8" PRIu64 " %c%c %s %s %s\n", mode, version / 10, version % 10, src_fs,
entry.uncompressed_length, entry.is_text ? 't' : 'b',
entry.has_data_descriptor ? 'X' : 'x', method, time, name.c_str());
}
-static void ProcessOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+static void ProcessOne(ZipArchiveHandle zah, const ZipEntry64& entry, const std::string& name) {
if (role == kUnzip) {
if (flag_l || flag_v) {
// -l or -lv or -lq or -v.
@@ -361,7 +365,7 @@
die(0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
}
- ZipEntry entry;
+ ZipEntry64 entry;
std::string name;
while ((err = Next(cookie, &entry, &name)) >= 0) {
if (ShouldInclude(name)) ProcessOne(zah, entry, name);
diff --git a/logd/tests/Android.bp b/logd/tests/Android.bp
index 2519a84..9a5defa 100644
--- a/logd/tests/Android.bp
+++ b/logd/tests/Android.bp
@@ -63,7 +63,6 @@
},
test_suites: [
"cts",
- "vts",
"vts10",
],
}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index adfdb7b..6564e8f 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -1063,6 +1063,12 @@
start vold
exec - system system -- /system/bin/vdc checkpoint resetCheckpoint
exec - system system -- /system/bin/vdc checkpoint markBootAttempt
+ # Unmount /data_mirror mounts in the reverse order of corresponding mounts.
+ umount /data_mirror/data_ce/null/0
+ umount /data_mirror/data_ce/null
+ umount /data_mirror/data_de/null
+ umount /data_mirror/cur_profiles
+ umount /data_mirror
remount_userdata
start bootanim