blob: 5eeaf380692e2230e28883e0be8bb943a2d2d9df [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/forensics/feedback/migration/utils/file_utils.h"
#include <lib/fdio/fd.h>
#include <lib/syslog/cpp/macros.h>
#include <queue>
#include "src/lib/files/directory.h"
#include "src/lib/files/file.h"
#include "src/lib/files/path.h"
#include "src/lib/fxl/strings/join_strings.h"
#include "src/lib/fxl/strings/split_string.h"
namespace forensics::feedback {
fbl::unique_fd IntoFd(::fidl::InterfaceHandle<fuchsia::io::Directory> dir) {
if (!dir.is_valid()) {
return fbl::unique_fd(fbl::unique_fd::InvalidValue());
}
fbl::unique_fd fd;
if (const zx_status_t status =
fdio_fd_create(dir.TakeChannel().release(), fd.reset_and_get_address());
status != ZX_OK) {
FX_PLOGS(WARNING, status) << "Failed to convert directory request into file descriptor";
return fbl::unique_fd(fbl::unique_fd::InvalidValue());
}
return fd;
}
::fidl::InterfaceHandle<fuchsia::io::Directory> IntoInterfaceHandle(fbl::unique_fd fd) {
::fidl::InterfaceHandle<fuchsia::io::Directory> handle;
if (!fd.is_valid()) {
return handle;
}
zx::channel channel;
if (const zx_status_t status = fdio_fd_transfer(fd.release(), channel.reset_and_get_address());
status != ZX_OK) {
FX_PLOGS(WARNING, status) << "Failed to transfer file descriptor";
return handle;
}
handle.set_channel(std::move(channel));
return handle;
}
bool CopyFile(const fbl::unique_fd& source_root_fd, const fbl::unique_fd& sink_root_fd,
const std::string& relative_path) {
if (!sink_root_fd.is_valid()) {
return false;
}
if (!source_root_fd.is_valid()) {
return true;
}
std::string content;
if (!files::ReadFileToStringAt(source_root_fd.get(), relative_path, &content)) {
FX_LOGS(WARNING) << "Failed to read " << relative_path << " from source root";
return false;
}
// Remove the file name from the path and create the necessary directories.
std::vector<std::string> path_elements =
fxl::SplitStringCopy(relative_path, "/", fxl::WhiteSpaceHandling::kKeepWhitespace,
fxl::SplitResult::kSplitWantNonEmpty);
path_elements.pop_back();
if (!files::CreateDirectoryAt(sink_root_fd.get(), fxl::JoinStrings(path_elements, "/"))) {
FX_LOGS(WARNING) << "Failed to create directory for " << relative_path << " under sink root";
return false;
}
if (!files::WriteFileAt(sink_root_fd.get(), relative_path, content.data(), content.size())) {
FX_LOGS(WARNING) << "Failed to write " << relative_path << " to sink root";
return false;
}
return true;
}
bool GetNestedDirectories(const fbl::unique_fd& root_fd, std::vector<std::string>* directories) {
if (!root_fd.is_valid()) {
return false;
}
std::vector<std::string> found_directories;
std::queue<std::string> to_search({"."});
while (!to_search.empty()) {
const std::string relative_path = to_search.front();
to_search.pop();
if (!files::IsDirectoryAt(root_fd.get(), relative_path)) {
continue;
}
found_directories.push_back(relative_path);
if (std::vector<std::string> contents;
files::ReadDirContentsAt(root_fd.get(), relative_path, &contents)) {
for (const auto& c : contents) {
if (c != ".") {
to_search.push(files::JoinPath(relative_path, c));
}
}
}
}
directories->swap(found_directories);
return true;
}
bool GetNestedFiles(const fbl::unique_fd& root_fd, std::vector<std::string>* files) {
std::vector<std::string> dirs;
if (!GetNestedDirectories(root_fd, &dirs)) {
return false;
}
std::vector<std::string> found_files;
for (const auto& dir : dirs) {
std::vector<std::string> contents;
if (!files::ReadDirContentsAt(root_fd.get(), dir, &contents)) {
return false;
}
for (const auto& item : contents) {
const std::string path = files::JoinPath(dir, item);
if (files::IsFileAt(root_fd.get(), path)) {
found_files.push_back(path);
}
}
}
files->swap(found_files);
return true;
}
bool Migrate(const fbl::unique_fd& source_root_fd, const fbl::unique_fd& sink_root_fd) {
if (!sink_root_fd.is_valid()) {
return false;
}
if (!source_root_fd.is_valid()) {
return true;
}
std::vector<std::string> relative_dirs;
if (!GetNestedDirectories(source_root_fd, &relative_dirs)) {
FX_LOGS(WARNING) << "Unable to get nested directories";
return false;
}
relative_dirs.erase(std::remove(relative_dirs.begin(), relative_dirs.end(), "."),
relative_dirs.end());
for (const auto& relative_dir : relative_dirs) {
if (!files::CreateDirectoryAt(sink_root_fd.get(), relative_dir)) {
FX_LOGS(WARNING) << "Unable to create directory " << relative_dir;
return false;
}
}
std::vector<std::string> relative_files;
if (!GetNestedFiles(source_root_fd, &relative_files)) {
FX_LOGS(WARNING) << "Unable to get nested files";
return false;
}
for (const auto& relative_file : relative_files) {
if (!CopyFile(source_root_fd, sink_root_fd, relative_file)) {
FX_LOGS(WARNING) << "Unable to copy file " << relative_file;
return false;
}
if (!files::DeletePathAt(source_root_fd.get(), relative_file, /*recursive=*/true)) {
FX_LOGS(WARNING) << "Unable to delete file " << relative_file;
return false;
}
}
for (const auto& relative_dir : relative_dirs) {
if (!files::DeletePathAt(source_root_fd.get(), relative_dir, /*recursive=*/true)) {
FX_LOGS(WARNING) << "Unable to delete " << relative_dir << " from original root";
return false;
}
}
return true;
}
} // namespace forensics::feedback