Merge changes I1a122235,I6b22ead8
* changes:
Revert "Revert "adb: extend sync protocol's stat support.""
Revert "Revert "adb: move adb_strerror to sysdeps/win32/errno.cpp.""
diff --git a/adb/Android.mk b/adb/Android.mk
index be04cfa..16ed991 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -50,6 +50,7 @@
fdevent.cpp \
sockets.cpp \
socket_spec.cpp \
+ sysdeps/errno.cpp \
transport.cpp \
transport_local.cpp \
transport_usb.cpp \
@@ -88,10 +89,12 @@
LIBADB_windows_SRC_FILES := \
sysdeps_win32.cpp \
+ sysdeps/win32/errno.cpp \
sysdeps/win32/stat.cpp \
usb_windows.cpp \
LIBADB_TEST_windows_SRCS := \
+ sysdeps/win32/errno_test.cpp \
sysdeps_win32_test.cpp \
include $(CLEAR_VARS)
diff --git a/adb/adb.h b/adb/adb.h
index df59aaa..19f09a3 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -51,7 +51,7 @@
std::string adb_version();
// Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 37
+#define ADB_SERVER_VERSION 38
class atransport;
struct usb_handle;
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index caa7a5f..76119ef 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -15,12 +15,10 @@
*/
#include <dirent.h>
-#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
@@ -43,6 +41,7 @@
#include "adb_utils.h"
#include "file_sync_service.h"
#include "line_printer.h"
+#include "sysdeps/errno.h"
#include "sysdeps/stat.h"
#include <android-base/file.h>
@@ -75,8 +74,8 @@
struct copyinfo {
std::string lpath;
std::string rpath;
- unsigned int time = 0;
- unsigned int mode;
+ int64_t time = 0;
+ uint32_t mode;
uint64_t size = 0;
bool skip = false;
@@ -203,9 +202,16 @@
max = SYNC_DATA_MAX; // TODO: decide at runtime.
std::string error;
- fd = adb_connect("sync:", &error);
- if (fd < 0) {
- Error("connect failed: %s", error.c_str());
+ FeatureSet features;
+ if (!adb_get_feature_set(&features, &error)) {
+ fd = -1;
+ Error("failed to get feature set: %s", error.c_str());
+ } else {
+ have_stat_v2_ = CanUseFeature(features, kFeatureStat2);
+ fd = adb_connect("sync:", &error);
+ if (fd < 0) {
+ Error("connect failed: %s", error.c_str());
+ }
}
}
@@ -292,6 +298,77 @@
return WriteFdExactly(fd, &buf[0], buf.size());
}
+ bool SendStat(const char* path_and_mode) {
+ if (!have_stat_v2_) {
+ errno = ENOTSUP;
+ return false;
+ }
+ return SendRequest(ID_STAT_V2, path_and_mode);
+ }
+
+ bool SendLstat(const char* path_and_mode) {
+ if (have_stat_v2_) {
+ return SendRequest(ID_LSTAT_V2, path_and_mode);
+ } else {
+ return SendRequest(ID_LSTAT_V1, path_and_mode);
+ }
+ }
+
+ bool FinishStat(struct stat* st) {
+ syncmsg msg;
+
+ memset(st, 0, sizeof(*st));
+ if (have_stat_v2_) {
+ if (!ReadFdExactly(fd, &msg.stat_v2, sizeof(msg.stat_v2))) {
+ fatal_errno("protocol fault: failed to read stat response");
+ }
+
+ if (msg.stat_v2.id != ID_LSTAT_V2 && msg.stat_v2.id != ID_STAT_V2) {
+ fatal_errno("protocol fault: stat response has wrong message id: %" PRIx32,
+ msg.stat_v2.id);
+ }
+
+ if (msg.stat_v2.error != 0) {
+ errno = errno_from_wire(msg.stat_v2.error);
+ return false;
+ }
+
+ st->st_dev = msg.stat_v2.dev;
+ st->st_ino = msg.stat_v2.ino;
+ st->st_mode = msg.stat_v2.mode;
+ st->st_nlink = msg.stat_v2.nlink;
+ st->st_uid = msg.stat_v2.uid;
+ st->st_gid = msg.stat_v2.gid;
+ st->st_size = msg.stat_v2.size;
+ st->st_atime = msg.stat_v2.atime;
+ st->st_mtime = msg.stat_v2.mtime;
+ st->st_ctime = msg.stat_v2.ctime;
+ return true;
+ } else {
+ if (!ReadFdExactly(fd, &msg.stat_v1, sizeof(msg.stat_v1))) {
+ fatal_errno("protocol fault: failed to read stat response");
+ }
+
+ if (msg.stat_v1.id != ID_LSTAT_V1) {
+ fatal_errno("protocol fault: stat response has wrong message id: %" PRIx32,
+ msg.stat_v1.id);
+ }
+
+ if (msg.stat_v1.mode == 0 && msg.stat_v1.size == 0 && msg.stat_v1.time == 0) {
+ // There's no way for us to know what the error was.
+ errno = ENOPROTOOPT;
+ return false;
+ }
+
+ st->st_mode = msg.stat_v1.mode;
+ st->st_size = msg.stat_v1.size;
+ st->st_ctime = msg.stat_v1.time;
+ st->st_mtime = msg.stat_v1.time;
+ }
+
+ return true;
+ }
+
// Sending header, payload, and footer in a single write makes a huge
// difference to "adb sync" performance.
bool SendSmallFile(const char* path_and_mode,
@@ -429,7 +506,7 @@
return false;
}
buf[msg.status.msglen] = 0;
- Error("failed to copy '%s' to '%s': %s", from, to, &buf[0]);
+ Error("failed to copy '%s' to '%s': remote %s", from, to, &buf[0]);
return false;
}
@@ -500,6 +577,7 @@
private:
bool expect_done_;
+ bool have_stat_v2_;
TransferLedger global_ledger_;
TransferLedger current_ledger_;
@@ -555,25 +633,47 @@
}
}
-static bool sync_finish_stat(SyncConnection& sc, unsigned int* timestamp,
- unsigned int* mode, unsigned int* size) {
- syncmsg msg;
- if (!ReadFdExactly(sc.fd, &msg.stat, sizeof(msg.stat)) || msg.stat.id != ID_STAT) {
+static bool sync_stat(SyncConnection& sc, const char* path, struct stat* st) {
+ return sc.SendStat(path) && sc.FinishStat(st);
+}
+
+static bool sync_lstat(SyncConnection& sc, const char* path, struct stat* st) {
+ return sc.SendLstat(path) && sc.FinishStat(st);
+}
+
+static bool sync_stat_fallback(SyncConnection& sc, const char* path, struct stat* st) {
+ if (sync_stat(sc, path, st)) {
+ return true;
+ }
+
+ if (errno != ENOTSUP) {
return false;
}
- if (timestamp) *timestamp = msg.stat.time;
- if (mode) *mode = msg.stat.mode;
- if (size) *size = msg.stat.size;
+ // Try to emulate the parts we can when talking to older adbds.
+ bool lstat_result = sync_lstat(sc, path, st);
+ if (!lstat_result) {
+ return false;
+ }
+ if (S_ISLNK(st->st_mode)) {
+ // If the target is a symlink, figure out whether it's a file or a directory.
+ // Also, zero out the st_size field, since no one actually cares what the path length is.
+ st->st_size = 0;
+ std::string dir_path = path;
+ dir_path.push_back('/');
+ struct stat tmp_st;
+
+ st->st_mode &= ~S_IFMT;
+ if (sync_lstat(sc, dir_path.c_str(), &tmp_st)) {
+ st->st_mode |= S_IFDIR;
+ } else {
+ st->st_mode |= S_IFREG;
+ }
+ }
return true;
}
-static bool sync_stat(SyncConnection& sc, const char* path,
- unsigned int* timestamp, unsigned int* mode, unsigned int* size) {
- return sc.SendRequest(ID_STAT, path) && sync_finish_stat(sc, timestamp, mode, size);
-}
-
static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
unsigned mtime, mode_t mode)
{
@@ -621,8 +721,11 @@
static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath,
const char* name=nullptr) {
- unsigned size = 0;
- if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false;
+ struct stat st;
+ if (!sync_stat_fallback(sc, rpath, &st)) {
+ sc.Error("stat failed when trying to receive %s: %s", rpath, strerror(errno));
+ return false;
+ }
if (!sc.SendRequest(ID_RECV, rpath)) return false;
@@ -675,7 +778,7 @@
bytes_copied += msg.data.size;
sc.RecordBytesTransferred(msg.data.size);
- sc.ReportProgress(name != nullptr ? name : rpath, bytes_copied, size);
+ sc.ReportProgress(name != nullptr ? name : rpath, bytes_copied, st.st_size);
}
sc.RecordFilesTransferred(1);
@@ -778,20 +881,20 @@
if (check_timestamps) {
for (const copyinfo& ci : file_list) {
- if (!sc.SendRequest(ID_STAT, ci.rpath.c_str())) {
+ if (!sc.SendLstat(ci.rpath.c_str())) {
+ sc.Error("failed to send lstat");
return false;
}
}
for (copyinfo& ci : file_list) {
- unsigned int timestamp, mode, size;
- if (!sync_finish_stat(sc, ×tamp, &mode, &size)) {
- return false;
- }
- if (size == ci.size) {
- // For links, we cannot update the atime/mtime.
- if ((S_ISREG(ci.mode & mode) && timestamp == ci.time) ||
- (S_ISLNK(ci.mode & mode) && timestamp >= ci.time)) {
- ci.skip = true;
+ struct stat st;
+ if (sc.FinishStat(&st)) {
+ if (st.st_size == static_cast<off_t>(ci.size)) {
+ // For links, we cannot update the atime/mtime.
+ if ((S_ISREG(ci.mode & st.st_mode) && st.st_mtime == ci.time) ||
+ (S_ISLNK(ci.mode & st.st_mode) && st.st_mtime >= ci.time)) {
+ ci.skip = true;
+ }
}
}
}
@@ -823,10 +926,22 @@
if (!sc.IsValid()) return false;
bool success = true;
- unsigned dst_mode;
- if (!sync_stat(sc, dst, nullptr, &dst_mode, nullptr)) return false;
- bool dst_exists = (dst_mode != 0);
- bool dst_isdir = S_ISDIR(dst_mode);
+ bool dst_exists;
+ bool dst_isdir;
+
+ struct stat st;
+ if (sync_stat_fallback(sc, dst, &st)) {
+ dst_exists = true;
+ dst_isdir = S_ISDIR(st.st_mode);
+ } else {
+ if (errno == ENOENT || errno == ENOPROTOOPT) {
+ dst_exists = false;
+ dst_isdir = false;
+ } else {
+ sc.Error("stat failed when trying to push to %s: %s", dst, strerror(errno));
+ return false;
+ }
+ }
if (!dst_isdir) {
if (srcs.size() > 1) {
@@ -871,8 +986,7 @@
dst_dir.append(adb_basename(src_path));
}
- success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(),
- false, false);
+ success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), false, false);
continue;
} else if (!should_push_file(st.st_mode)) {
sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
@@ -901,17 +1015,6 @@
return success;
}
-static bool remote_symlink_isdir(SyncConnection& sc, const std::string& rpath) {
- unsigned mode;
- std::string dir_path = rpath;
- dir_path.push_back('/');
- if (!sync_stat(sc, dir_path.c_str(), nullptr, &mode, nullptr)) {
- sc.Error("failed to stat remote symlink '%s'", dir_path.c_str());
- return false;
- }
- return S_ISDIR(mode);
-}
-
static bool remote_build_list(SyncConnection& sc, std::vector<copyinfo>* file_list,
const std::string& rpath, const std::string& lpath) {
std::vector<copyinfo> dirlist;
@@ -949,7 +1052,13 @@
// Check each symlink we found to see whether it's a file or directory.
for (copyinfo& link_ci : linklist) {
- if (remote_symlink_isdir(sc, link_ci.rpath)) {
+ struct stat st;
+ if (!sync_stat_fallback(sc, link_ci.rpath.c_str(), &st)) {
+ sc.Warning("stat failed for path %s: %s", link_ci.rpath.c_str(), strerror(errno));
+ continue;
+ }
+
+ if (S_ISDIR(st.st_mode)) {
dirlist.emplace_back(std::move(link_ci));
} else {
file_list->emplace_back(std::move(link_ci));
@@ -1075,22 +1184,19 @@
for (const char* src_path : srcs) {
const char* dst_path = dst;
- unsigned src_mode, src_time, src_size;
- if (!sync_stat(sc, src_path, &src_time, &src_mode, &src_size)) {
- sc.Error("failed to stat remote object '%s'", src_path);
- return false;
- }
- if (src_mode == 0) {
- sc.Error("remote object '%s' does not exist", src_path);
+ struct stat src_st;
+ if (!sync_stat_fallback(sc, src_path, &src_st)) {
+ if (errno == ENOPROTOOPT) {
+ sc.Error("remote object '%s' does not exist", src_path);
+ } else {
+ sc.Error("failed to stat remote object '%s': %s", src_path, strerror(errno));
+ }
+
success = false;
continue;
}
- bool src_isdir = S_ISDIR(src_mode);
- if (S_ISLNK(src_mode)) {
- src_isdir = remote_symlink_isdir(sc, src_path);
- }
-
+ bool src_isdir = S_ISDIR(src_st.st_mode);
if (src_isdir) {
std::string dst_dir = dst;
@@ -1109,8 +1215,8 @@
success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(), copy_attrs);
continue;
- } else if (!should_pull_file(src_mode)) {
- sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, src_mode);
+ } else if (!should_pull_file(src_st.st_mode)) {
+ sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, src_st.st_mode);
continue;
}
@@ -1125,13 +1231,13 @@
}
sc.NewTransfer();
- sc.SetExpectedTotalBytes(src_size);
+ sc.SetExpectedTotalBytes(src_st.st_size);
if (!sync_recv(sc, src_path, dst_path, name)) {
success = false;
continue;
}
- if (copy_attrs && set_time_and_mode(dst_path, src_time, src_mode) != 0) {
+ if (copy_attrs && set_time_and_mode(dst_path, src_st.st_mtime, src_st.st_mode) != 0) {
success = false;
continue;
}
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 7a92d2e..43c877e 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -41,6 +41,7 @@
#include "adb_io.h"
#include "adb_utils.h"
#include "security_log_tags.h"
+#include "sysdeps/errno.h"
static bool should_use_fs_config(const std::string& path) {
// TODO: use fs_config to configure permissions on /data.
@@ -98,18 +99,47 @@
return true;
}
-static bool do_stat(int s, const char* path) {
- syncmsg msg;
- msg.stat.id = ID_STAT;
+static bool do_lstat_v1(int s, const char* path) {
+ syncmsg msg = {};
+ msg.stat_v1.id = ID_LSTAT_V1;
struct stat st = {};
- // TODO: add a way to report that the stat failed!
lstat(path, &st);
- msg.stat.mode = st.st_mode;
- msg.stat.size = st.st_size;
- msg.stat.time = st.st_mtime;
+ msg.stat_v1.mode = st.st_mode;
+ msg.stat_v1.size = st.st_size;
+ msg.stat_v1.time = st.st_mtime;
+ return WriteFdExactly(s, &msg.stat_v1, sizeof(msg.stat_v1));
+}
- return WriteFdExactly(s, &msg.stat, sizeof(msg.stat));
+static bool do_stat_v2(int s, uint32_t id, const char* path) {
+ syncmsg msg = {};
+ msg.stat_v2.id = id;
+
+ decltype(&stat) stat_fn;
+ if (id == ID_STAT_V2) {
+ stat_fn = stat;
+ } else {
+ stat_fn = lstat;
+ }
+
+ struct stat st = {};
+ int rc = stat_fn(path, &st);
+ if (rc == -1) {
+ msg.stat_v2.error = errno_to_wire(errno);
+ } else {
+ msg.stat_v2.dev = st.st_dev;
+ msg.stat_v2.ino = st.st_ino;
+ msg.stat_v2.mode = st.st_mode;
+ msg.stat_v2.nlink = st.st_nlink;
+ msg.stat_v2.uid = st.st_uid;
+ msg.stat_v2.gid = st.st_gid;
+ msg.stat_v2.size = st.st_size;
+ msg.stat_v2.atime = st.st_atime;
+ msg.stat_v2.mtime = st.st_mtime;
+ msg.stat_v2.ctime = st.st_ctime;
+ }
+
+ return WriteFdExactly(s, &msg.stat_v2, sizeof(msg.stat_v2));
}
static bool do_list(int s, const char* path) {
@@ -427,30 +457,34 @@
D("sync: '%.4s' '%s'", id, name);
switch (request.id) {
- case ID_STAT:
- if (!do_stat(fd, name)) return false;
- break;
- case ID_LIST:
- if (!do_list(fd, name)) return false;
- break;
- case ID_SEND:
- if (!do_send(fd, name, buffer)) return false;
- break;
- case ID_RECV:
- if (!do_recv(fd, name, buffer)) return false;
- break;
- case ID_QUIT:
- return false;
- default:
- SendSyncFail(fd, android::base::StringPrintf("unknown command '%.4s' (%08x)",
- id, request.id));
- return false;
+ case ID_LSTAT_V1:
+ if (!do_lstat_v1(fd, name)) return false;
+ break;
+ case ID_LSTAT_V2:
+ case ID_STAT_V2:
+ if (!do_stat_v2(fd, request.id, name)) return false;
+ break;
+ case ID_LIST:
+ if (!do_list(fd, name)) return false;
+ break;
+ case ID_SEND:
+ if (!do_send(fd, name, buffer)) return false;
+ break;
+ case ID_RECV:
+ if (!do_recv(fd, name, buffer)) return false;
+ break;
+ case ID_QUIT:
+ return false;
+ default:
+ SendSyncFail(
+ fd, android::base::StringPrintf("unknown command '%.4s' (%08x)", id, request.id));
+ return false;
}
return true;
}
-void file_sync_service(int fd, void* cookie) {
+void file_sync_service(int fd, void*) {
std::vector<char> buffer(SYNC_DATA_MAX);
while (handle_sync_command(fd, buffer)) {
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 0e25974..90f1965 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -22,7 +22,9 @@
#define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
-#define ID_STAT MKID('S','T','A','T')
+#define ID_LSTAT_V1 MKID('S','T','A','T')
+#define ID_STAT_V2 MKID('S','T','A','2')
+#define ID_LSTAT_V2 MKID('L','S','T','2')
#define ID_LIST MKID('L','I','S','T')
#define ID_SEND MKID('S','E','N','D')
#define ID_RECV MKID('R','E','C','V')
@@ -45,7 +47,21 @@
uint32_t mode;
uint32_t size;
uint32_t time;
- } stat;
+ } stat_v1;
+ struct __attribute__((packed)) {
+ uint32_t id;
+ uint32_t error;
+ uint64_t dev;
+ uint64_t ino;
+ uint32_t mode;
+ uint32_t nlink;
+ uint32_t uid;
+ uint32_t gid;
+ uint64_t size;
+ int64_t atime;
+ int64_t mtime;
+ int64_t ctime;
+ } stat_v2;
struct __attribute__((packed)) {
uint32_t id;
uint32_t mode;
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 05d9fde..654072c 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -34,6 +34,7 @@
#include <android-base/unique_fd.h>
#include <android-base/utf8.h>
+#include "sysdeps/errno.h"
#include "sysdeps/stat.h"
/*
@@ -361,9 +362,6 @@
#define getcwd adb_getcwd
-char* adb_strerror(int err);
-#define strerror adb_strerror
-
// Helper class to convert UTF-16 argv from wmain() to UTF-8 args that can be
// passed to main().
class NarrowArgs {
diff --git a/adb/sysdeps/errno.cpp b/adb/sysdeps/errno.cpp
new file mode 100644
index 0000000..6869947
--- /dev/null
+++ b/adb/sysdeps/errno.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sysdeps/errno.h"
+
+#include <errno.h>
+
+#include <thread>
+#include <unordered_map>
+#include <utility>
+
+#include "adb.h"
+
+#if defined(_WIN32)
+#define ETXTBSY EBUSY
+#endif
+
+// Use the linux asm-generic values for errno (which are used on all android archs but mips).
+#define ERRNO_VALUES() \
+ ERRNO_VALUE(EACCES, 13); \
+ ERRNO_VALUE(EEXIST, 17); \
+ ERRNO_VALUE(EFAULT, 14); \
+ ERRNO_VALUE(EFBIG, 27); \
+ ERRNO_VALUE(EINTR, 4); \
+ ERRNO_VALUE(EINVAL, 22); \
+ ERRNO_VALUE(EIO, 5); \
+ ERRNO_VALUE(EISDIR, 21); \
+ ERRNO_VALUE(ELOOP, 40); \
+ ERRNO_VALUE(EMFILE, 24); \
+ ERRNO_VALUE(ENAMETOOLONG, 36); \
+ ERRNO_VALUE(ENFILE, 23); \
+ ERRNO_VALUE(ENOENT, 2); \
+ ERRNO_VALUE(ENOMEM, 12); \
+ ERRNO_VALUE(ENOSPC, 28); \
+ ERRNO_VALUE(ENOTDIR, 20); \
+ ERRNO_VALUE(EOVERFLOW, 75); \
+ ERRNO_VALUE(EPERM, 1); \
+ ERRNO_VALUE(EROFS, 30); \
+ ERRNO_VALUE(ETXTBSY, 26)
+
+// Make sure these values are actually correct.
+#if defined(__linux__) && !defined(__mips__)
+#define ERRNO_VALUE(error_name, wire_value) static_assert((error_name) == (wire_value), "")
+ERRNO_VALUES();
+#undef ERRNO_VALUE
+#endif
+
+static std::unordered_map<int, int>* generate_host_to_wire() {
+ auto result = new std::unordered_map<int, int>();
+#define ERRNO_VALUE(error_name, wire_value) \
+ result->insert(std::make_pair((error_name), (wire_value)))
+ ERRNO_VALUES();
+#undef ERRNO_VALUE
+ return result;
+}
+
+static std::unordered_map<int, int>* generate_wire_to_host() {
+ auto result = new std::unordered_map<int, int>();
+#define ERRNO_VALUE(error_name, wire_value) \
+ result->insert(std::make_pair((wire_value), (error_name)))
+ ERRNO_VALUES();
+#undef ERRNO_VALUE
+ return result;
+}
+
+static std::unordered_map<int, int>& host_to_wire = *generate_host_to_wire();
+static std::unordered_map<int, int>& wire_to_host = *generate_wire_to_host();
+
+int errno_to_wire(int error) {
+ auto it = host_to_wire.find(error);
+ if (it == host_to_wire.end()) {
+ LOG(ERROR) << "failed to convert errno " << error << " (" << strerror(error) << ") to wire";
+
+ // Return EIO;
+ return 5;
+ }
+ return it->second;
+}
+
+int errno_from_wire(int error) {
+ auto it = host_to_wire.find(error);
+ if (it == host_to_wire.end()) {
+ LOG(ERROR) << "failed to convert errno " << error << " from wire";
+ return EIO;
+ }
+ return it->second;
+}
diff --git a/adb/sysdeps/errno.h b/adb/sysdeps/errno.h
new file mode 100644
index 0000000..72816b1
--- /dev/null
+++ b/adb/sysdeps/errno.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <errno.h>
+#include <string.h>
+
+#if defined(_WIN32)
+char* adb_strerror(int err);
+#define strerror adb_strerror
+#endif
+
+// errno values differ between operating systems and between Linux architectures.
+// Arbitrarily select the Linux asm-generic values to use in the wire protocol.
+int errno_to_wire(int error);
+int errno_from_wire(int error);
diff --git a/adb/sysdeps/win32/errno.cpp b/adb/sysdeps/win32/errno.cpp
new file mode 100644
index 0000000..a3b9d9b
--- /dev/null
+++ b/adb/sysdeps/win32/errno.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sysdeps/errno.h"
+
+#include <windows.h>
+
+#include <string>
+
+// Overrides strerror() to handle error codes not supported by the Windows C
+// Runtime (MSVCRT.DLL).
+char* adb_strerror(int err) {
+// sysdeps.h defines strerror to adb_strerror, but in this function, we
+// want to call the real C Runtime strerror().
+#pragma push_macro("strerror")
+#undef strerror
+ const int saved_err = errno; // Save because we overwrite it later.
+
+ // Lookup the string for an unknown error.
+ char* errmsg = strerror(-1);
+ const std::string unknown_error = (errmsg == nullptr) ? "" : errmsg;
+
+ // Lookup the string for this error to see if the C Runtime has it.
+ errmsg = strerror(err);
+ if (errmsg != nullptr && unknown_error != errmsg) {
+ // The CRT returned an error message and it is different than the error
+ // message for an unknown error, so it is probably valid, so use it.
+ } else {
+ // Check if we have a string for this error code.
+ const char* custom_msg = nullptr;
+ switch (err) {
+#pragma push_macro("ERR")
+#undef ERR
+#define ERR(errnum, desc) case errnum: custom_msg = desc; break
+ // These error strings are from AOSP bionic/libc/include/sys/_errdefs.h.
+ // Note that these cannot be longer than 94 characters because we
+ // pass this to _strerror() which has that requirement.
+ ERR(ECONNRESET, "Connection reset by peer");
+ ERR(EHOSTUNREACH, "No route to host");
+ ERR(ENETDOWN, "Network is down");
+ ERR(ENETRESET, "Network dropped connection because of reset");
+ ERR(ENOBUFS, "No buffer space available");
+ ERR(ENOPROTOOPT, "Protocol not available");
+ ERR(ENOTCONN, "Transport endpoint is not connected");
+ ERR(ENOTSOCK, "Socket operation on non-socket");
+ ERR(EOPNOTSUPP, "Operation not supported on transport endpoint");
+#pragma pop_macro("ERR")
+ }
+
+ if (custom_msg != nullptr) {
+ // Use _strerror() to write our string into the writable per-thread
+ // buffer used by strerror()/_strerror(). _strerror() appends the
+ // msg for the current value of errno, so set errno to a consistent
+ // value for every call so that our code-path is always the same.
+ errno = 0;
+ errmsg = _strerror(custom_msg);
+ const size_t custom_msg_len = strlen(custom_msg);
+ // Just in case _strerror() returned a read-only string, check if
+ // the returned string starts with our custom message because that
+ // implies that the string is not read-only.
+ if ((errmsg != nullptr) && !strncmp(custom_msg, errmsg, custom_msg_len)) {
+ // _strerror() puts other text after our custom message, so
+ // remove that by terminating after our message.
+ errmsg[custom_msg_len] = '\0';
+ } else {
+ // For some reason nullptr was returned or a pointer to a
+ // read-only string was returned, so fallback to whatever
+ // strerror() can muster (probably "Unknown error" or some
+ // generic CRT error string).
+ errmsg = strerror(err);
+ }
+ } else {
+ // We don't have a custom message, so use whatever strerror(err)
+ // returned earlier.
+ }
+ }
+
+ errno = saved_err; // restore
+
+ return errmsg;
+#pragma pop_macro("strerror")
+}
diff --git a/adb/sysdeps/win32/errno_test.cpp b/adb/sysdeps/win32/errno_test.cpp
new file mode 100644
index 0000000..09ec52c
--- /dev/null
+++ b/adb/sysdeps/win32/errno_test.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sysdeps/errno.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+void TestAdbStrError(int err, const char* expected) {
+ errno = 12345;
+ const char* result = adb_strerror(err);
+ // Check that errno is not overwritten.
+ EXPECT_EQ(12345, errno);
+ EXPECT_STREQ(expected, result);
+}
+
+TEST(sysdeps_win32, adb_strerror) {
+ // Test an error code that should not have a mapped string. Use an error
+ // code that is not used by the internal implementation of adb_strerror().
+ TestAdbStrError(-2, "Unknown error");
+ // adb_strerror() uses -1 internally, so test that it can still be passed
+ // as a parameter.
+ TestAdbStrError(-1, "Unknown error");
+ // Test very big, positive unknown error.
+ TestAdbStrError(1000000, "Unknown error");
+
+ // Test success case.
+ // Wine returns "Success" for strerror(0), Windows returns "No error", so accept both.
+ std::string success = adb_strerror(0);
+ EXPECT_TRUE(success == "Success" || success == "No error") << "strerror(0) = " << success;
+
+ // Test error that regular strerror() should have a string for.
+ TestAdbStrError(EPERM, "Operation not permitted");
+ // Test error that regular strerror() doesn't have a string for, but that
+ // adb_strerror() returns.
+ TestAdbStrError(ECONNRESET, "Connection reset by peer");
+}
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 4dd549d..a4b5e69 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -477,81 +477,6 @@
return 0;
}
-// Overrides strerror() to handle error codes not supported by the Windows C
-// Runtime (MSVCRT.DLL).
-char* adb_strerror(int err) {
- // sysdeps.h defines strerror to adb_strerror, but in this function, we
- // want to call the real C Runtime strerror().
-#pragma push_macro("strerror")
-#undef strerror
- const int saved_err = errno; // Save because we overwrite it later.
-
- // Lookup the string for an unknown error.
- char* errmsg = strerror(-1);
- const std::string unknown_error = (errmsg == nullptr) ? "" : errmsg;
-
- // Lookup the string for this error to see if the C Runtime has it.
- errmsg = strerror(err);
- if (errmsg != nullptr && unknown_error != errmsg) {
- // The CRT returned an error message and it is different than the error
- // message for an unknown error, so it is probably valid, so use it.
- } else {
- // Check if we have a string for this error code.
- const char* custom_msg = nullptr;
- switch (err) {
-#pragma push_macro("ERR")
-#undef ERR
-#define ERR(errnum, desc) case errnum: custom_msg = desc; break
- // These error strings are from AOSP bionic/libc/include/sys/_errdefs.h.
- // Note that these cannot be longer than 94 characters because we
- // pass this to _strerror() which has that requirement.
- ERR(ECONNRESET, "Connection reset by peer");
- ERR(EHOSTUNREACH, "No route to host");
- ERR(ENETDOWN, "Network is down");
- ERR(ENETRESET, "Network dropped connection because of reset");
- ERR(ENOBUFS, "No buffer space available");
- ERR(ENOPROTOOPT, "Protocol not available");
- ERR(ENOTCONN, "Transport endpoint is not connected");
- ERR(ENOTSOCK, "Socket operation on non-socket");
- ERR(EOPNOTSUPP, "Operation not supported on transport endpoint");
-#pragma pop_macro("ERR")
- }
-
- if (custom_msg != nullptr) {
- // Use _strerror() to write our string into the writable per-thread
- // buffer used by strerror()/_strerror(). _strerror() appends the
- // msg for the current value of errno, so set errno to a consistent
- // value for every call so that our code-path is always the same.
- errno = 0;
- errmsg = _strerror(custom_msg);
- const size_t custom_msg_len = strlen(custom_msg);
- // Just in case _strerror() returned a read-only string, check if
- // the returned string starts with our custom message because that
- // implies that the string is not read-only.
- if ((errmsg != nullptr) &&
- !strncmp(custom_msg, errmsg, custom_msg_len)) {
- // _strerror() puts other text after our custom message, so
- // remove that by terminating after our message.
- errmsg[custom_msg_len] = '\0';
- } else {
- // For some reason nullptr was returned or a pointer to a
- // read-only string was returned, so fallback to whatever
- // strerror() can muster (probably "Unknown error" or some
- // generic CRT error string).
- errmsg = strerror(err);
- }
- } else {
- // We don't have a custom message, so use whatever strerror(err)
- // returned earlier.
- }
- }
-
- errno = saved_err; // restore
-
- return errmsg;
-#pragma pop_macro("strerror")
-}
-
/**************************************************************************/
/**************************************************************************/
/***** *****/
@@ -565,7 +490,7 @@
static void _socket_set_errno( const DWORD err ) {
// Because the Windows C Runtime (MSVCRT.DLL) strerror() does not support a
// lot of POSIX and socket error codes, some of the resulting error codes
- // are mapped to strings by adb_strerror() above.
+ // are mapped to strings by adb_strerror().
switch ( err ) {
case 0: errno = 0; break;
// Don't map WSAEINTR since that is only for Winsock 1.1 which we don't use.
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
index c3a3fd7..529b212 100755
--- a/adb/sysdeps_win32_test.cpp
+++ b/adb/sysdeps_win32_test.cpp
@@ -70,36 +70,6 @@
}
}
-void TestAdbStrError(int err, const char* expected) {
- errno = 12345;
- const char* result = adb_strerror(err);
- // Check that errno is not overwritten.
- EXPECT_EQ(12345, errno);
- EXPECT_STREQ(expected, result);
-}
-
-TEST(sysdeps_win32, adb_strerror) {
- // Test an error code that should not have a mapped string. Use an error
- // code that is not used by the internal implementation of adb_strerror().
- TestAdbStrError(-2, "Unknown error");
- // adb_strerror() uses -1 internally, so test that it can still be passed
- // as a parameter.
- TestAdbStrError(-1, "Unknown error");
- // Test very big, positive unknown error.
- TestAdbStrError(1000000, "Unknown error");
-
- // Test success case.
- // Wine returns "Success" for strerror(0), Windows returns "No error", so accept both.
- std::string success = adb_strerror(0);
- EXPECT_TRUE(success == "Success" || success == "No error") << "strerror(0) = " << success;
-
- // Test error that regular strerror() should have a string for.
- TestAdbStrError(EPERM, "Operation not permitted");
- // Test error that regular strerror() doesn't have a string for, but that
- // adb_strerror() returns.
- TestAdbStrError(ECONNRESET, "Connection reset by peer");
-}
-
TEST(sysdeps_win32, unix_isatty) {
// stdin and stdout should be consoles. Use CONIN$ and CONOUT$ special files
// so that we can test this even if stdin/stdout have been redirected. Read
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 132702d..7b82b19 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -49,6 +49,7 @@
const char* const kFeatureShell2 = "shell_v2";
const char* const kFeatureCmd = "cmd";
+const char* const kFeatureStat2 = "stat_v2";
static std::string dump_packet(const char* name, const char* func, apacket* p) {
unsigned command = p->msg.command;
@@ -771,7 +772,8 @@
// Local static allocation to avoid global non-POD variables.
static const FeatureSet* features = new FeatureSet{
kFeatureShell2,
- kFeatureCmd
+ kFeatureCmd,
+ kFeatureStat2,
// Increment ADB_SERVER_VERSION whenever the feature list changes to
// make sure that the adb client and server features stay in sync
// (http://b/24370690).
diff --git a/adb/transport.h b/adb/transport.h
index b2df838..3306388 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -46,6 +46,7 @@
extern const char* const kFeatureShell2;
// The 'cmd' command is available
extern const char* const kFeatureCmd;
+extern const char* const kFeatureStat2;
class atransport {
public: