adb: add ls_v2.
Add a 64-bit size/time variant of `adb ls`. Bug: http://b/122955521 Test: adb shell dd if=/dev/zero bs=1m count=8192 of=/data/local/tmp/big Test: adb pull /data/local/tmp/big Test: adb ls /data/local/tmp Change-Id: I6ff857239995bc7b5c5f8dfd65a36fad41e67d85
This commit is contained in:
parent
51fcf3222b
commit
34a478f572
5 changed files with 132 additions and 49 deletions
|
@ -52,6 +52,8 @@
|
|||
#include <android-base/strings.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
typedef void(sync_ls_cb)(unsigned mode, uint64_t size, uint64_t time, const char* name);
|
||||
|
||||
struct syncsendbuf {
|
||||
unsigned id;
|
||||
unsigned size;
|
||||
|
@ -210,6 +212,7 @@ class SyncConnection {
|
|||
Error("failed to get feature set: %s", error.c_str());
|
||||
} else {
|
||||
have_stat_v2_ = CanUseFeature(features_, kFeatureStat2);
|
||||
have_ls_v2_ = CanUseFeature(features_, kFeatureLs2);
|
||||
fd.reset(adb_connect("sync:", &error));
|
||||
if (fd < 0) {
|
||||
Error("connect failed: %s", error.c_str());
|
||||
|
@ -372,6 +375,45 @@ class SyncConnection {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SendLs(const char* path) {
|
||||
return SendRequest(have_ls_v2_ ? ID_LIST_V2 : ID_LIST_V1, path);
|
||||
}
|
||||
|
||||
private:
|
||||
template <bool v2>
|
||||
static bool FinishLsImpl(borrowed_fd fd, const std::function<sync_ls_cb>& callback) {
|
||||
using dent_type =
|
||||
std::conditional_t<v2, decltype(syncmsg::dent_v2), decltype(syncmsg::dent_v1)>;
|
||||
|
||||
while (true) {
|
||||
dent_type dent;
|
||||
if (!ReadFdExactly(fd, &dent, sizeof(dent))) return false;
|
||||
|
||||
uint32_t expected_id = v2 ? ID_DENT_V2 : ID_DENT_V1;
|
||||
if (dent.id == ID_DONE) return true;
|
||||
if (dent.id != expected_id) return false;
|
||||
|
||||
// Maximum length of a file name excluding null terminator (NAME_MAX) on Linux is 255.
|
||||
char buf[256];
|
||||
size_t len = dent.namelen;
|
||||
if (len > 255) return false;
|
||||
|
||||
if (!ReadFdExactly(fd, buf, len)) return false;
|
||||
buf[len] = 0;
|
||||
|
||||
callback(dent.mode, dent.size, dent.mtime, buf);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
bool FinishLs(const std::function<sync_ls_cb>& callback) {
|
||||
if (have_ls_v2_) {
|
||||
return FinishLsImpl<true>(this->fd, callback);
|
||||
} else {
|
||||
return FinishLsImpl<false>(this->fd, callback);
|
||||
}
|
||||
}
|
||||
|
||||
// Sending header, payload, and footer in a single write makes a huge
|
||||
// difference to "adb sync" performance.
|
||||
bool SendSmallFile(const char* path_and_mode,
|
||||
|
@ -578,6 +620,7 @@ class SyncConnection {
|
|||
bool expect_done_;
|
||||
FeatureSet features_;
|
||||
bool have_stat_v2_;
|
||||
bool have_ls_v2_;
|
||||
|
||||
TransferLedger global_ledger_;
|
||||
TransferLedger current_ledger_;
|
||||
|
@ -609,28 +652,9 @@ class SyncConnection {
|
|||
}
|
||||
};
|
||||
|
||||
typedef void (sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name);
|
||||
|
||||
static bool sync_ls(SyncConnection& sc, const char* path,
|
||||
const std::function<sync_ls_cb>& func) {
|
||||
if (!sc.SendRequest(ID_LIST, path)) return false;
|
||||
|
||||
while (true) {
|
||||
syncmsg msg;
|
||||
if (!ReadFdExactly(sc.fd, &msg.dent, sizeof(msg.dent))) return false;
|
||||
|
||||
if (msg.dent.id == ID_DONE) return true;
|
||||
if (msg.dent.id != ID_DENT) return false;
|
||||
|
||||
size_t len = msg.dent.namelen;
|
||||
if (len > 256) return false; // TODO: resize buffer? continue?
|
||||
|
||||
char buf[257];
|
||||
if (!ReadFdExactly(sc.fd, buf, len)) return false;
|
||||
buf[len] = 0;
|
||||
|
||||
func(msg.dent.mode, msg.dent.size, msg.dent.mtime, buf);
|
||||
}
|
||||
return sc.SendLs(path) && sc.FinishLs(func);
|
||||
}
|
||||
|
||||
static bool sync_stat(SyncConnection& sc, const char* path, struct stat* st) {
|
||||
|
@ -787,9 +811,8 @@ bool do_sync_ls(const char* path) {
|
|||
SyncConnection sc;
|
||||
if (!sc.IsValid()) return false;
|
||||
|
||||
return sync_ls(sc, path, [](unsigned mode, unsigned size, unsigned time,
|
||||
const char* name) {
|
||||
printf("%08x %08x %08x %s\n", mode, size, time, name);
|
||||
return sync_ls(sc, path, [](unsigned mode, uint64_t size, uint64_t time, const char* name) {
|
||||
printf("%08x %08" PRIx64 " %08" PRIx64 " %s\n", mode, size, time, name);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1052,7 +1075,7 @@ static bool remote_build_list(SyncConnection& sc, std::vector<copyinfo>* file_li
|
|||
file_list->push_back(ci);
|
||||
|
||||
// Put the files/dirs in rpath on the lists.
|
||||
auto callback = [&](unsigned mode, unsigned size, unsigned time, const char* name) {
|
||||
auto callback = [&](unsigned mode, uint64_t size, uint64_t time, const char* name) {
|
||||
if (IsDotOrDotDot(name)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -174,40 +174,73 @@ static bool do_stat_v2(int s, uint32_t id, const char* path) {
|
|||
return WriteFdExactly(s, &msg.stat_v2, sizeof(msg.stat_v2));
|
||||
}
|
||||
|
||||
template <bool v2>
|
||||
static bool do_list(int s, const char* path) {
|
||||
dirent* de;
|
||||
|
||||
syncmsg msg;
|
||||
msg.dent.id = ID_DENT;
|
||||
using MessageType =
|
||||
std::conditional_t<v2, decltype(syncmsg::dent_v2), decltype(syncmsg::dent_v1)>;
|
||||
MessageType msg;
|
||||
uint32_t msg_id;
|
||||
if constexpr (v2) {
|
||||
msg_id = ID_DENT_V2;
|
||||
} else {
|
||||
msg_id = ID_DENT_V1;
|
||||
}
|
||||
|
||||
std::unique_ptr<DIR, int(*)(DIR*)> d(opendir(path), closedir);
|
||||
if (!d) goto done;
|
||||
|
||||
while ((de = readdir(d.get()))) {
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.id = msg_id;
|
||||
|
||||
std::string filename(StringPrintf("%s/%s", path, de->d_name));
|
||||
|
||||
struct stat st;
|
||||
if (lstat(filename.c_str(), &st) == 0) {
|
||||
size_t d_name_length = strlen(de->d_name);
|
||||
msg.dent.mode = st.st_mode;
|
||||
msg.dent.size = st.st_size;
|
||||
msg.dent.mtime = st.st_mtime;
|
||||
msg.dent.namelen = d_name_length;
|
||||
msg.mode = st.st_mode;
|
||||
msg.size = st.st_size;
|
||||
msg.mtime = st.st_mtime;
|
||||
|
||||
if (!WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ||
|
||||
!WriteFdExactly(s, de->d_name, d_name_length)) {
|
||||
return false;
|
||||
if constexpr (v2) {
|
||||
msg.dev = st.st_dev;
|
||||
msg.ino = st.st_ino;
|
||||
msg.nlink = st.st_nlink;
|
||||
msg.uid = st.st_uid;
|
||||
msg.gid = st.st_gid;
|
||||
msg.atime = st.st_atime;
|
||||
msg.ctime = st.st_ctime;
|
||||
}
|
||||
} else {
|
||||
if constexpr (v2) {
|
||||
msg.error = errno;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
size_t d_name_length = strlen(de->d_name);
|
||||
msg.namelen = d_name_length;
|
||||
|
||||
if (!WriteFdExactly(s, &msg, sizeof(msg)) ||
|
||||
!WriteFdExactly(s, de->d_name, d_name_length)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
msg.dent.id = ID_DONE;
|
||||
msg.dent.mode = 0;
|
||||
msg.dent.size = 0;
|
||||
msg.dent.mtime = 0;
|
||||
msg.dent.namelen = 0;
|
||||
return WriteFdExactly(s, &msg.dent, sizeof(msg.dent));
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.id = ID_DONE;
|
||||
return WriteFdExactly(s, &msg, sizeof(msg));
|
||||
}
|
||||
|
||||
static bool do_list_v1(int s, const char* path) {
|
||||
return do_list<false>(s, path);
|
||||
}
|
||||
|
||||
static bool do_list_v2(int s, const char* path) {
|
||||
return do_list<true>(s, path);
|
||||
}
|
||||
|
||||
// Make sure that SendFail from adb_io.cpp isn't accidentally used in this file.
|
||||
|
@ -499,8 +532,10 @@ static const char* sync_id_to_name(uint32_t id) {
|
|||
return "lstat_v2";
|
||||
case ID_STAT_V2:
|
||||
return "stat_v2";
|
||||
case ID_LIST:
|
||||
return "list";
|
||||
case ID_LIST_V1:
|
||||
return "list_v1";
|
||||
case ID_LIST_V2:
|
||||
return "list_v2";
|
||||
case ID_SEND:
|
||||
return "send";
|
||||
case ID_RECV:
|
||||
|
@ -546,8 +581,11 @@ static bool handle_sync_command(int fd, std::vector<char>& buffer) {
|
|||
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;
|
||||
case ID_LIST_V1:
|
||||
if (!do_list_v1(fd, name)) return false;
|
||||
break;
|
||||
case ID_LIST_V2:
|
||||
if (!do_list_v2(fd, name)) return false;
|
||||
break;
|
||||
case ID_SEND:
|
||||
if (!do_send(fd, name, buffer)) return false;
|
||||
|
|
|
@ -21,10 +21,14 @@
|
|||
#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_LIST_V1 MKID('L', 'I', 'S', 'T')
|
||||
#define ID_LIST_V2 MKID('L', 'I', 'S', '2')
|
||||
#define ID_DENT_V1 MKID('D', 'E', 'N', 'T')
|
||||
#define ID_DENT_V2 MKID('D', 'N', 'T', '2')
|
||||
|
||||
#define ID_SEND MKID('S', 'E', 'N', 'D')
|
||||
#define ID_RECV MKID('R', 'E', 'C', 'V')
|
||||
#define ID_DENT MKID('D', 'E', 'N', 'T')
|
||||
#define ID_DONE MKID('D', 'O', 'N', 'E')
|
||||
#define ID_DATA MKID('D', 'A', 'T', 'A')
|
||||
#define ID_OKAY MKID('O', 'K', 'A', 'Y')
|
||||
|
@ -64,15 +68,30 @@ union syncmsg {
|
|||
uint32_t size;
|
||||
uint32_t mtime;
|
||||
uint32_t namelen;
|
||||
} dent;
|
||||
} dent_v1; // followed by `namelen` bytes of the name.
|
||||
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;
|
||||
uint32_t namelen;
|
||||
} dent_v2; // followed by `namelen` bytes of the name.
|
||||
struct __attribute__((packed)) {
|
||||
uint32_t id;
|
||||
uint32_t size;
|
||||
} data;
|
||||
} data; // followed by `size` bytes of data.
|
||||
struct __attribute__((packed)) {
|
||||
uint32_t id;
|
||||
uint32_t msglen;
|
||||
} status;
|
||||
} status; // followed by `msglen` bytes of error message, if id == ID_FAIL.
|
||||
};
|
||||
|
||||
#define SYNC_DATA_MAX (64 * 1024)
|
||||
|
|
|
@ -66,6 +66,7 @@ static auto& transport_lock = *new std::recursive_mutex();
|
|||
const char* const kFeatureShell2 = "shell_v2";
|
||||
const char* const kFeatureCmd = "cmd";
|
||||
const char* const kFeatureStat2 = "stat_v2";
|
||||
const char* const kFeatureLs2 = "ls_v2";
|
||||
const char* const kFeatureLibusb = "libusb";
|
||||
const char* const kFeaturePushSync = "push_sync";
|
||||
const char* const kFeatureApex = "apex";
|
||||
|
@ -1044,6 +1045,7 @@ const FeatureSet& supported_features() {
|
|||
kFeatureShell2,
|
||||
kFeatureCmd,
|
||||
kFeatureStat2,
|
||||
kFeatureLs2,
|
||||
kFeatureFixedPushMkdir,
|
||||
kFeatureApex,
|
||||
kFeatureAbb,
|
||||
|
|
|
@ -57,6 +57,7 @@ extern const char* const kFeatureShell2;
|
|||
// The 'cmd' command is available
|
||||
extern const char* const kFeatureCmd;
|
||||
extern const char* const kFeatureStat2;
|
||||
extern const char* const kFeatureLs2;
|
||||
// The server is running with libusb enabled.
|
||||
extern const char* const kFeatureLibusb;
|
||||
// adbd supports `push --sync`.
|
||||
|
|
Loading…
Reference in a new issue