blob: 6828c992ca3777f21b16c5639152ef0516ab82a5 [file] [log] [blame]
/*
* Copyright (C) 2007 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.
*/
#define TRACE_TAG SYNC
#include "file_sync_service.h"
// #include "sysdeps.h"
#include <dirent.h>
#include <errno.h>
// #include <linux/xattr.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
// #include <sys/xattr.h>
#include <unistd.h>
#include <utime.h>
// #include <android-base/file.h>
// #include <android-base/stringprintf.h>
// #include <android-base/strings.h>
// #include <private/android_filesystem_config.h>
// #include <private/android_logger.h>
// #include <selinux/android.h>
// #include "adb.h"
// #include "adb_io.h"
// #include "adb_trace.h"
// #include "adb_utils.h"
// #include "security_log_tags.h"
// #include "sysdeps/errno.h"
// using android::base::StringPrintf;
#include <fidl/fuchsia.io/cpp/fidl.h>
#include <lib/syslog/cpp/macros.h>
#include <vector>
#include "adb-file-sync-base.h"
#include "util.h"
/*
static bool should_use_fs_config(const std::string& path) {
// TODO: use fs_config to configure permissions on /data.
return android::base::StartsWith(path, "/system/") ||
android::base::StartsWith(path, "/vendor/") ||
android::base::StartsWith(path, "/oem/");
}
static bool update_capabilities(const char* path, uint64_t capabilities) {
if (capabilities == 0) {
// Ensure we clean up in case the capabilities weren't 0 in the past.
removexattr(path, XATTR_NAME_CAPS);
return true;
}
vfs_cap_data cap_data = {};
cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE;
cap_data.data[0].permitted = (capabilities & 0xffffffff);
cap_data.data[0].inheritable = 0;
cap_data.data[1].permitted = (capabilities >> 32);
cap_data.data[1].inheritable = 0;
return setxattr(path, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) != -1;
}
static bool secure_mkdirs(fidl::WireSyncClient<fuchsia_io::Directory>& parent,
const std::string& path) {
// uid_t uid = -1;
// gid_t gid = -1;
// unsigned int mode = 0775;
// uint64_t capabilities = 0;
// if (path[0] != '/') {
// return false;
// }
auto path_components = split_string(path, "/");
// std::string partial_path;
fidl::WireSyncClient<fuchsia_io::Directory>& cur = parent;
for (const auto& path_component : path_components) {
if (path_component.empty()) {
// ignore empty path component
continue;
}
// if (partial_path.back() != OS_PATH_SEPARATOR) {
// partial_path += OS_PATH_SEPARATOR;
// }
// partial_path += path_component;
// if (should_use_fs_config(partial_path)) {
// fs_config(partial_path.c_str(), 1, nullptr, &uid, &gid, &mode, &capabilities);
// }
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
if (endpoints.is_error()) {
FX_LOGS(ERROR) << "Could not create endpoints " << endpoints.error_value();
return false;
}
if (auto open = cur->Open(
fuchsia_io::OpenFlags::kCreate | fuchsia_io::OpenFlags::kRightReadable,
fuchsia_io::kModeTypeDirectory, fidl::StringView::FromExternal(path_component),
fidl::ServerEnd<fuchsia_io::Node>(endpoints->server.TakeChannel()));
!open.ok()) {
FX_LOGS(ERROR) << "Could not open " << open.error();
return false;
}
cur = fidl::WireSyncClient<fuchsia_io::Directory>(std::move(endpoints->client));
// if (chown(partial_path.c_str(), uid, gid) == -1)
// return false;
// Not all filesystems support setting SELinux labels. http://b/23530370.
// selinux_android_restorecon(partial_path.c_str(), 0);
// if (!update_capabilities(partial_path.c_str(), capabilities))
// return false;
}
return true;
}
*/
namespace {
mode_t mode_from_attributes(const fuchsia_io::wire::NodeAttributes2& attr) {
if (attr.mutable_attributes.has_mode()) {
return attr.mutable_attributes.mode();
}
if (!attr.immutable_attributes.has_protocols()) {
return S_IFREG;
}
if (attr.immutable_attributes.protocols() == fuchsia_io::NodeProtocolKinds::kDirectory) {
return S_IFDIR;
}
if (attr.immutable_attributes.protocols() == fuchsia_io::NodeProtocolKinds::kFile) {
return S_IFREG;
}
if (attr.immutable_attributes.protocols() == fuchsia_io::NodeProtocolKinds::kSymlink) {
return S_IFLNK;
}
return S_IFREG;
}
} // namespace
static bool do_lstat_v1(zx::socket& socket, const std::vector<std::string>& path,
fidl::WireSyncClient<fuchsia_io::Directory>& component) {
syncmsg msg = {};
msg.stat_v1.id = ID_LSTAT_V1;
auto [client, server] = fidl::Endpoints<fuchsia_io::Node>::Create();
if (!path.empty()) {
auto result = component->Open(fidl::StringView::FromExternal(ConcatenateRelativePath(path)),
fuchsia_io::kPermReadable, {}, server.TakeChannel());
if (!result.ok()) {
FX_LOGS(ERROR) << "Failed to open file: " << result.error();
return false;
}
}
fuchsia_io::wire::NodeAttributes2 attr;
fuchsia_io::NodeAttributesQuery query = fuchsia_io::NodeAttributesQuery::kProtocols |
fuchsia_io::NodeAttributesQuery::kMode |
fuchsia_io::NodeAttributesQuery::kStorageSize |
fuchsia_io::NodeAttributesQuery::kModificationTime;
auto component_node = fidl::UnownedClientEnd<fuchsia_io::Node>(component.client_end().handle());
auto result = path.empty() ? fidl::WireCall(component_node)->GetAttributes(query)
: fidl::WireCall(client)->GetAttributes(query);
if (!result.ok()) {
FX_LOGS(ERROR) << "Transport error on GetAttributes: " << result.error();
return false;
}
const fit::result response = result.value();
if (response.is_error()) {
FX_LOGS(ERROR) << "GetAttributes failed: " << response.error_value();
return false;
}
attr = *response.value();
msg.stat_v1.mode = mode_from_attributes(attr);
if (attr.immutable_attributes.has_storage_size()) {
msg.stat_v1.size = static_cast<uint32_t>(attr.immutable_attributes.storage_size());
}
if (attr.mutable_attributes.has_modification_time()) {
msg.stat_v1.time = static_cast<uint32_t>(attr.mutable_attributes.modification_time());
}
return WriteFdExactly(socket, &msg.stat_v1, sizeof(msg.stat_v1));
}
static bool do_stat_v2(zx::socket& socket, uint32_t id, const std::vector<std::string>& path,
fidl::WireSyncClient<fuchsia_io::Directory>& component) {
syncmsg msg = {};
msg.stat_v2.id = id;
auto [client, server] = fidl::Endpoints<fuchsia_io::Node>::Create();
if (!path.empty()) {
auto result = component->Open(fidl::StringView::FromExternal(ConcatenateRelativePath(path)),
fuchsia_io::kPermReadable, {}, server.TakeChannel());
if (!result.ok()) {
FX_LOGS(ERROR) << "Failed to open file: " << result.error();
return false;
}
}
zx_status_t status;
fuchsia_io::wire::NodeAttributes2 attr;
auto component_node = fidl::UnownedClientEnd<fuchsia_io::Node>(component.client_end().handle());
auto result =
path.empty()
? fidl::WireCall(component_node)->GetAttributes(fuchsia_io::NodeAttributesQuery::kMask)
: fidl::WireCall(client)->GetAttributes(fuchsia_io::NodeAttributesQuery::kMask);
status = result.status();
if (result.ok()) {
const fit::result response = result.value();
if (response.is_error()) {
status = response.error_value();
}
attr = *response.value();
}
if (status != ZX_OK) {
FX_LOGS(ERROR) << "GetAttributes failed: " << status;
msg.stat_v2.error = status;
} else {
if (attr.mutable_attributes.has_rdev()) {
msg.stat_v2.dev = attr.mutable_attributes.rdev();
}
msg.stat_v2.mode = mode_from_attributes(attr);
if (attr.immutable_attributes.has_link_count()) {
msg.stat_v2.nlink = static_cast<uint32_t>(attr.immutable_attributes.link_count());
}
if (attr.immutable_attributes.has_storage_size()) {
msg.stat_v2.size = attr.immutable_attributes.storage_size();
}
if (attr.mutable_attributes.has_modification_time()) {
msg.stat_v2.mtime = attr.mutable_attributes.modification_time();
}
if (attr.mutable_attributes.has_uid()) {
msg.stat_v2.uid = attr.mutable_attributes.uid();
}
if (attr.mutable_attributes.has_gid()) {
msg.stat_v2.gid = attr.mutable_attributes.gid();
}
if (attr.immutable_attributes.has_id()) {
msg.stat_v2.ino = attr.immutable_attributes.id();
}
if (attr.mutable_attributes.has_access_time()) {
msg.stat_v2.atime = attr.mutable_attributes.access_time();
}
if (attr.immutable_attributes.has_change_time()) {
msg.stat_v2.ctime = attr.immutable_attributes.change_time();
}
}
return WriteFdExactly(socket, &msg.stat_v2, sizeof(msg.stat_v2));
}
static bool do_list(zx::socket& socket, const std::vector<std::string>& path,
fidl::WireSyncClient<fuchsia_io::Directory>& component) {
struct dirent_t {
// Describes the inode of the entry.
uint64_t ino;
// Describes the length of the dirent name in bytes.
uint8_t size;
// Describes the type of the entry. Aligned with the
// POSIX d_type values. Use `DirentType` constants.
uint8_t type;
// Unterminated name of entry.
} __PACKED;
syncmsg msg = {};
msg.dent.id = ID_DENT;
auto [directory_client, directory_server] = fidl::Endpoints<fuchsia_io::Directory>::Create();
if (!path.empty()) {
if (auto open = component->Open(fidl::StringView::FromExternal(ConcatenateRelativePath(path)),
fuchsia_io::kPermReadable, {}, directory_server.TakeChannel());
!open.ok()) {
FX_LOGS(ERROR) << "Failed to open file " << open.error();
return false;
}
}
fidl::WireSyncClient<fuchsia_io::Directory> directory(std::move(directory_client));
auto& dir_ptr = path.empty() ? component : directory;
if (auto rewind = dir_ptr->Rewind(); !rewind.ok()) {
FX_LOGS(ERROR) << "Rewind failed " << rewind.error();
goto done;
}
while (true) {
auto result = dir_ptr->ReadDirents(fuchsia_io::kMaxBuf);
if (!result.ok()) {
FX_LOGS(ERROR) << "ReadDirents failed with " << result.error();
goto done;
}
if (result->dirents.empty()) {
break;
}
auto it = result->dirents.begin();
while (true) {
if (static_cast<size_t>(std::distance(it, result->dirents.end())) < sizeof(dirent_t)) {
break;
}
const auto* dent = reinterpret_cast<dirent_t*>(&*it);
if (static_cast<size_t>(std::distance(it, result->dirents.end())) <
sizeof(dirent_t) + dent->size) {
break;
}
std::string name(it + sizeof(dirent_t), it + sizeof(dirent_t) + dent->size);
auto [file_client, file_server] = fidl::Endpoints<fuchsia_io::File>::Create();
fidl::WireSyncClient<fuchsia_io::File> file(std::move(file_client));
fuchsia_io::NodeAttributesQuery query = fuchsia_io::NodeAttributesQuery::kProtocols |
fuchsia_io::NodeAttributesQuery::kMode |
fuchsia_io::NodeAttributesQuery::kStorageSize |
fuchsia_io::NodeAttributesQuery::kModificationTime;
if (auto open = dir_ptr->Open(fidl::StringView::FromExternal(name), fuchsia_io::kPermReadable,
{}, file_server.TakeChannel());
!open.ok()) {
FX_LOGS(ERROR) << "Failed to open file " << open.error();
goto increment;
}
if (auto attr = file->GetAttributes(query); !attr.ok()) {
FX_LOGS(ERROR) << "Transport error on GetAttributes " << attr.error();
goto increment;
} else {
const fit::result response = attr.value();
if (response.is_error()) {
FX_LOGS(ERROR) << "GetAttributes failed " << response.error_value();
goto increment;
}
msg.dent.mode = mode_from_attributes(*response.value());
if (response.value()->immutable_attributes.has_storage_size()) {
msg.dent.size =
static_cast<uint32_t>(response.value()->immutable_attributes.storage_size());
}
if (response.value()->mutable_attributes.has_modification_time()) {
msg.dent.time =
static_cast<uint32_t>(response.value()->mutable_attributes.modification_time());
}
msg.dent.namelen = static_cast<uint32_t>(name.length());
}
if (!WriteFdExactly(socket, &msg.dent, sizeof(msg.dent)) ||
!WriteFdExactly(socket, name.data(), dent->size)) {
return false;
}
increment:
// Increment
if (static_cast<size_t>(std::distance(it, result->dirents.end())) >
sizeof(dirent_t) + dent->size) {
std::advance(it, sizeof(dirent_t) + dent->size);
} else {
break;
}
}
}
done:
msg.dent.id = ID_DONE;
msg.dent.mode = 0;
msg.dent.size = 0;
msg.dent.time = 0;
msg.dent.namelen = 0;
return WriteFdExactly(socket, &msg.dent, sizeof(msg.dent));
}
// Make sure that SendFail from adb_io.cpp isn't accidentally used in this file.
// #pragma GCC poison SendFail
static bool SendSyncFail(zx::socket& socket, const std::string& reason) {
// D("sync: failure: %s", reason.c_str());
syncmsg msg;
msg.data.id = ID_FAIL;
msg.data.size = static_cast<uint32_t>(reason.size());
return WriteFdExactly(socket, &msg.data, sizeof(msg.data)) &&
WriteFdExactly(socket, reason.c_str(), reason.size());
}
// static bool SendSyncFailErrno(int fd, const std::string& reason) {
// return SendSyncFail(fd, StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
// }
static bool handle_send_file(zx::socket& socket, const std::vector<std::string>& path,
fidl::WireSyncClient<fuchsia_io::Directory>& component, uid_t uid,
gid_t gid, uint64_t capabilities, mode_t mode,
std::vector<uint8_t>& buffer, bool do_unlink) {
syncmsg msg;
// unsigned int timestamp = 0;
// __android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
// int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
// if (fd < 0 && errno == ENOENT) {
// if (!secure_mkdirs(android::base::Dirname(path))) {
// SendSyncFailErrno(s, "secure_mkdirs failed");
// goto fail;
// }
// fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
// }
// if (fd < 0 && errno == EEXIST) {
// fd = adb_open_mode(path, O_WRONLY | O_CLOEXEC, mode);
// }
// if (fd < 0) {
// SendSyncFailErrno(s, "couldn't create file");
// goto fail;
// } else {
// if (fchown(fd, uid, gid) == -1) {
// SendSyncFailErrno(s, "fchown failed");
// goto fail;
// }
// // Not all filesystems support setting SELinux labels. http://b/23530370.
// selinux_android_restorecon(path, 0);
// // 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, mode);
// }
auto [client, server] = fidl::Endpoints<fuchsia_io::File>::Create();
fidl::WireSyncClient<fuchsia_io::File> file(std::move(client));
if (auto open =
component->Open(fidl::StringView::FromExternal(ConcatenateRelativePath(path)),
fuchsia_io::kPermWritable | fuchsia_io::Flags::kFlagMaybeCreate |
fuchsia_io::Flags::kProtocolFile | fuchsia_io::Flags::kFileTruncate,
{}, server.TakeChannel());
!open.ok()) {
SendSyncFail(socket, "Open failed");
FX_LOGS(INFO) << "Open failed " << open.error();
goto fail;
}
while (true) {
if (!ReadFdExactly(socket, &msg.data, sizeof(msg.data))) {
FX_LOGS(ERROR) << "read failed";
goto fail;
}
if (msg.data.id != ID_DATA) {
if (msg.data.id == ID_DONE) {
// timestamp = msg.data.size;
break;
}
SendSyncFail(socket, "invalid data message");
goto abort;
}
if (msg.data.size > buffer.size()) { // TODO: resize buffer?
SendSyncFail(socket, "oversize data message");
goto abort;
}
if (!ReadFdExactly(socket, buffer.data(), msg.data.size)) {
SendSyncFail(socket, "read failed");
goto abort;
}
uint64_t write_len = 0;
while (write_len < msg.data.size) {
auto cur_len = std::min(fuchsia_io::kMaxTransferSize, msg.data.size - write_len);
if (auto write = file->Write(
fidl::VectorView<uint8_t>::FromExternal(buffer.data() + write_len, cur_len));
!write.ok() || write->is_error()) {
if (write.ok()) {
FX_LOGS(ERROR) << "File Write failed: " << zx_status_get_string(write->error_value());
} else {
FX_LOGS(ERROR) << "Transport error on File Write: " << write.error();
}
SendSyncFail(socket, "File Write failed");
goto abort;
}
write_len += cur_len;
}
}
if (auto close = file->Close(); !close.ok() || close->is_error()) {
if (close.ok()) {
FX_LOGS(ERROR) << "File Close failed: " << zx_status_get_string(close->error_value());
} else {
FX_LOGS(ERROR) << "Transport error on File Close: " << close.error();
}
}
// if (!update_capabilities(path, capabilities)) {
// SendSyncFailErrno(s, "update_capabilities failed");
// goto fail;
// }
// utimbuf u;
// u.actime = timestamp;
// u.modtime = timestamp;
// utime(path.c_str(), &u);
msg.status.id = ID_OKAY;
msg.status.msglen = 0;
return WriteFdExactly(socket, &msg.status, sizeof(msg.status));
fail:
// FX_LOGS(ERROR) << "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
// data away if the other end keeps writing without reading (which is
// the case with old versions of adb). To maintain compatibility, keep
// reading and throwing away ID_DATA packets until the other side notices
// that we've reported an error.
while (true) {
if (!ReadFdExactly(socket, &msg.data, sizeof(msg.data)))
goto fail;
if (msg.data.id == ID_DONE) {
goto abort;
} else if (msg.data.id != ID_DATA) {
char id[5];
memcpy(id, &msg.data.id, sizeof(msg.data.id));
id[4] = '\0';
// D("handle_send_fail received unexpected id '%s' during failure", id);
goto abort;
}
if (msg.data.size > buffer.size()) {
// D("handle_send_fail received oversized packet of length '%u' during failure",
// msg.data.size);
goto abort;
}
if (!ReadFdExactly(socket, &buffer[0], msg.data.size))
goto abort;
}
abort:
if (auto close = file->Close(); !close.ok() || close->is_error()) {
if (close.ok()) {
FX_LOGS(ERROR) << "File Close failed: " << zx_status_get_string(close->error_value());
} else {
FX_LOGS(ERROR) << "Transport error on File Close: " << close.error();
}
}
// TODO: Currently do not support links.
// if (do_unlink) adb_unlink(path);
return false;
}
/*
#if defined(_WIN32)
extern bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer)
__attribute__((error("no symlinks on Windows"))); #else static bool handle_send_link(int s, const
std::string& path, std::vector<char>& buffer) { syncmsg msg; unsigned int len; int ret; if
(!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false; if (msg.data.id != ID_DATA) {
SendSyncFail(s, "invalid data message: expected ID_DATA");
return false;
}
len = msg.data.size;
if (len > buffer.size()) { // TODO: resize buffer?
SendSyncFail(s, "oversize data message");
return false;
}
if (!ReadFdExactly(s, &buffer[0], len)) return false;
ret = symlink(&buffer[0], path.c_str());
if (ret && errno == ENOENT) {
if (!secure_mkdirs(android::base::Dirname(path))) {
SendSyncFailErrno(s, "secure_mkdirs failed");
return false;
}
ret = symlink(&buffer[0], path.c_str());
}
if (ret) {
SendSyncFailErrno(s, "symlink failed");
return false;
}
if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
if (msg.data.id == ID_DONE) {
msg.status.id = ID_OKAY;
msg.status.msglen = 0;
if (!WriteFdExactly(s, &msg.status, sizeof(msg.status))) return false;
} else {
SendSyncFail(s, "invalid data message: expected ID_DONE");
return false;
}
return true;
}
#endif
*/
static bool do_send(zx::socket& socket, std::vector<std::string>& spec,
std::vector<uint8_t>& buffer,
fidl::WireSyncClient<fuchsia_io::Directory>& component) {
// 'spec' is of the form "/some/path,0755". Break it up.
size_t comma = spec.back().find_last_of(',');
if (comma == std::string::npos) {
SendSyncFail(socket, "missing , in ID_SEND");
return false;
}
mode_t mode = static_cast<mode_t>(strtoul(spec.back().substr(comma + 1).c_str(), nullptr, 0));
spec.back() = spec.back().substr(0, comma);
// Don't delete files before copying if they are not "regular" or symlinks.
// struct stat st;
// TODO: Currently do not support links.
// bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) ||
// S_ISLNK(st.st_mode); if (do_unlink) {
// adb_unlink(path.c_str());
// }
// if (S_ISLNK(mode)) {
// return handle_send_link(s, path.c_str(), buffer);
// }
// Copy user permission bits to "group" and "other" permissions.
mode &= 0777;
mode |= ((mode >> 3) & 0070);
mode |= ((mode >> 3) & 0007);
uid_t uid = -1;
gid_t gid = -1;
uint64_t capabilities = 0;
// if (should_use_fs_config(path)) {
// unsigned int broken_api_hack = mode;
// fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &capabilities);
// mode = broken_api_hack;
// }
return handle_send_file(socket, spec, component, uid, gid, capabilities, mode, buffer, false);
}
static bool do_recv(zx::socket& socket, const std::vector<std::string>& path,
fidl::WireSyncClient<fuchsia_io::Directory>& component) {
// __android_log_security_bswrite(SEC_TAG_ADB_RECV_FILE, path);
auto [client, server] = fidl::Endpoints<fuchsia_io::File>::Create();
if (auto open = component->Open(fidl::StringView::FromExternal(ConcatenateRelativePath(path)),
fuchsia_io::kPermReadable | fuchsia_io::Flags::kProtocolFile, {},
server.TakeChannel());
!open.ok()) {
FX_LOGS(INFO) << "Open failed: " << open.error();
SendSyncFail(socket, "open failed");
return false;
}
fidl::WireSyncClient<fuchsia_io::File> file(std::move(client));
syncmsg msg;
msg.data.id = ID_DATA;
while (true) {
auto result = file->Read(fuchsia_io::kMaxBuf);
if (!result.ok() || result->is_error()) {
FX_LOGS(ERROR) << "File Read failed "
<< (result.ok() ? result->error_value() : result.status());
return false;
}
msg.data.size = static_cast<uint32_t>(result->value()->data.size());
if (msg.data.size == 0) {
break;
}
if (!WriteFdExactly(socket, &msg.data, sizeof(msg.data)) ||
!WriteFdExactly(socket, result->value()->data.data(), msg.data.size)) {
if (auto close = file->Close(); !close.ok() || close->is_error()) {
FX_LOGS(ERROR) << "Failed to close file "
<< (close.ok() ? close->error_value() : close.status());
}
return false;
}
}
if (auto close = file->Close(); !close.ok() || close->is_error()) {
FX_LOGS(ERROR) << "Failed to close file "
<< (close.ok() ? close->error_value() : close.status());
}
msg.data.id = ID_DONE;
msg.data.size = 0;
return WriteFdExactly(socket, &msg.data, sizeof(msg.data));
}
static const char* sync_id_to_name(uint32_t id) {
switch (id) {
case ID_LSTAT_V1:
return "lstat_v1";
case ID_LSTAT_V2:
return "lstat_v2";
case ID_STAT_V2:
return "stat_v2";
case ID_LIST:
return "list";
case ID_SEND:
return "send";
case ID_RECV:
return "recv";
case ID_QUIT:
return "quit";
default:
return "???";
}
}
static bool handle_sync_command(void* ctx, zx::socket& socket, std::vector<uint8_t>& buffer) {
zx_signals_t pending;
auto status =
socket.wait_one(ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED, zx::time::infinite(), &pending);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Socket wait failed " << status;
return false;
}
if (pending & ZX_SOCKET_PEER_CLOSED) {
FX_LOGS(DEBUG) << "Peer closed";
return false;
}
// ATRACE_CALL();
SyncRequest request;
if (!ReadFdExactly(socket, &request, sizeof(request))) {
SendSyncFail(socket, "command read failure");
return false;
}
size_t path_length = request.path_length;
if (path_length > 1024) {
SendSyncFail(socket, "path too long");
return false;
}
char name[1025];
if (!ReadFdExactly(socket, name, path_length)) {
SendSyncFail(socket, "filename read failure");
return false;
}
name[path_length] = 0;
std::vector<std::string> path;
fidl::WireSyncClient<fuchsia_io::Directory> component;
auto get_component = [&](void* ctx, const std::string& name) {
// Connect to component
auto dir = static_cast<adb_file_sync::AdbFileSyncBase*>(ctx)->ConnectToComponent(name, &path);
if (dir.is_error()) {
FX_LOGS(ERROR) << "Could not connect to component " << name;
return false;
}
component.Bind(fidl::ClientEnd<fuchsia_io::Directory>(std::move(dir.value())));
return true;
};
std::string id_name = sync_id_to_name(request.id);
// std::string trace_name = StringPrintf("%s(%s)", id_name.c_str(), name);
// ATRACE_NAME(trace_name.c_str());
FX_LOGS(INFO) << "sync: " << id_name.c_str() << "('" << name << "')";
switch (request.id) {
case ID_LSTAT_V1:
if (!get_component(ctx, name) || !do_lstat_v1(socket, path, component))
return false;
break;
case ID_LSTAT_V2:
case ID_STAT_V2:
if (!get_component(ctx, name) || !do_stat_v2(socket, request.id, path, component))
return false;
break;
case ID_LIST:
if (!get_component(ctx, name) || !do_list(socket, path, component))
return false;
break;
case ID_SEND:
if (!get_component(ctx, name) || !do_send(socket, path, buffer, component))
return false;
break;
case ID_RECV:
if (!get_component(ctx, name) || !do_recv(socket, path, component))
return false;
break;
case ID_QUIT:
return false;
default:
SendSyncFail(socket, "unknown command"); // StringPrintf("unknown command %08x",
// request.id));
return false;
}
return true;
}
void file_sync_service(void* ctx, zx::socket socket) {
std::vector<uint8_t> buffer(SYNC_DATA_MAX);
while (handle_sync_command(ctx, socket, buffer)) {
}
socket.signal_peer(0, ZX_ERR_PEER_CLOSED);
FX_LOGS(DEBUG) << "sync: done";
}