// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/dbus/cros_disks/fake_cros_disks_client.h"
#include <utility>
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "url/gurl.h"
namespace ash {
namespace {
// Performs fake mounting by creating a directory with a dummy file.
MountError PerformFakeMount(const std::string& source_path,
const base::FilePath& mounted_path,
MountType type) {
if (mounted_path.empty())
return MountError::kInvalidArgument;
// Just create an empty directory and shows it as the mounted directory.
if (!base::CreateDirectory(mounted_path)) {
DLOG(ERROR) << "Failed to create directory at " << mounted_path.value();
return MountError::kDirectoryCreationFailed;
}
// Fake network mounts are responsible for populating their mount paths so
// don't need a dummy file.
if (type == MountType::kNetworkStorage)
return MountError::kSuccess;
// Put a dummy file.
const base::FilePath dummy_file_path =
mounted_path.Append("SUCCESSFULLY_PERFORMED_FAKE_MOUNT.txt");
const std::string dummy_file_content = "This is a dummy file.";
if (!base::WriteFile(dummy_file_path, dummy_file_content)) {
DLOG(ERROR) << "Failed to put a dummy file at " << dummy_file_path.value();
return MountError::kMountProgramFailed;
}
return MountError::kSuccess;
}
} // namespace
FakeCrosDisksClient::FakeCrosDisksClient() = default;
FakeCrosDisksClient::~FakeCrosDisksClient() = default;
void FakeCrosDisksClient::Init(dbus::Bus* bus) {}
void FakeCrosDisksClient::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
void FakeCrosDisksClient::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
void FakeCrosDisksClient::Mount(const std::string& source_path,
const std::string& source_format,
const std::string& mount_label,
const std::vector<std::string>& mount_options,
MountAccessMode access_mode,
RemountOption remount,
chromeos::VoidDBusMethodCallback callback) {
if (block_mount_) {
return;
}
// This fake implementation assumes mounted path is device when source_format
// is empty, or an archive otherwise.
MountType type =
source_format.empty() ? MountType::kDevice : MountType::kArchive;
// Network storage source paths are URIs.
if (GURL(source_path).is_valid())
type = MountType::kNetworkStorage;
base::FilePath mounted_path;
switch (type) {
case MountType::kArchive:
mounted_path = GetArchiveMountPoint().Append(
base::FilePath::FromUTF8Unsafe(mount_label));
break;
case MountType::kDevice:
mounted_path = GetRemovableDiskMountPoint().Append(
base::FilePath::FromUTF8Unsafe(mount_label));
break;
case MountType::kNetworkStorage:
// Call all registered callbacks until mounted_path is non-empty.
for (auto const& mount_point_callback : custom_mount_point_callbacks_) {
mounted_path = mount_point_callback.Run(source_path, mount_options);
if (!mounted_path.empty()) {
break;
}
}
break;
case MountType::kInvalid:
NOTREACHED_IN_MIGRATION();
return;
}
mounted_paths_.insert(mounted_path);
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(&PerformFakeMount, source_path, mounted_path, type),
base::BindOnce(&FakeCrosDisksClient::DidMount,
weak_ptr_factory_.GetWeakPtr(), source_path, type,
mounted_path, std::move(callback)));
}
void FakeCrosDisksClient::DidMount(const std::string& source_path,
MountType type,
const base::FilePath& mounted_path,
chromeos::VoidDBusMethodCallback callback,
MountError mount_error) {
// Tell the caller of Mount() that the mount request was accepted.
// Note that even if PerformFakeMount fails, this calls with |true| to
// emulate the situation that 1) Mount operation is _successfully_ started,
// 2) then failed for some reason.
std::move(callback).Run(true);
// Notify observers that the mount is completed.
NotifyMountCompleted(mount_error, source_path, type,
mounted_path.AsUTF8Unsafe());
}
void FakeCrosDisksClient::Unmount(const std::string& device_path,
UnmountCallback callback) {
DCHECK(!callback.is_null());
unmount_call_count_++;
last_unmount_device_path_ = device_path;
// Remove the dummy mounted directory if it exists.
if (mounted_paths_.erase(base::FilePath::FromUTF8Unsafe(device_path))) {
base::ThreadPool::PostTask(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::GetDeletePathRecursivelyCallback(
base::FilePath::FromUTF8Unsafe(device_path),
base::OnceCallback<void(bool)>(base::DoNothing())
.Then(base::BindOnce(std::move(callback), unmount_error_))));
} else {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), unmount_error_));
}
if (!unmount_listener_.is_null())
unmount_listener_.Run();
}
void FakeCrosDisksClient::EnumerateDevices(EnumerateDevicesCallback callback,
base::OnceClosure error_callback) {}
void FakeCrosDisksClient::EnumerateMountEntries(
EnumerateMountEntriesCallback callback,
base::OnceClosure error_callback) {}
void FakeCrosDisksClient::Format(const std::string& device_path,
const std::string& filesystem,
const std::string& label,
chromeos::VoidDBusMethodCallback callback) {
DCHECK(!callback.is_null());
format_call_count_++;
last_format_device_path_ = device_path;
last_format_filesystem_ = filesystem;
last_format_label_ = label;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), format_success_));
}
void FakeCrosDisksClient::SinglePartitionFormat(const std::string& device_path,
PartitionCallback callback) {
DCHECK(!callback.is_null());
partition_call_count_++;
last_partition_device_path_ = device_path;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), partition_error_));
}
void FakeCrosDisksClient::Rename(const std::string& device_path,
const std::string& volume_name,
chromeos::VoidDBusMethodCallback callback) {
DCHECK(!callback.is_null());
rename_call_count_++;
last_rename_device_path_ = device_path;
last_rename_volume_name_ = volume_name;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), rename_success_));
}
void FakeCrosDisksClient::GetDeviceProperties(
const std::string& device_path,
GetDevicePropertiesCallback callback,
base::OnceClosure error_callback) {
DCHECK(!callback.is_null());
if (!next_get_device_properties_disk_info_ ||
next_get_device_properties_disk_info_->device_path() != device_path) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(error_callback));
return;
}
get_device_properties_success_count_++;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback),
std::cref(*next_get_device_properties_disk_info_)));
}
void FakeCrosDisksClient::NotifyMountCompleted(MountError error_code,
const std::string& source_path,
MountType mount_type,
const std::string& mount_path,
const bool read_only) {
for (auto& observer : observer_list_) {
observer.OnMountCompleted(
{source_path, mount_path, mount_type, error_code, 100, read_only});
}
}
void FakeCrosDisksClient::NotifyFormatCompleted(
FormatError error_code,
const std::string& device_path) {
for (auto& observer : observer_list_)
observer.OnFormatCompleted(error_code, device_path);
}
void FakeCrosDisksClient::NotifyRenameCompleted(
RenameError error_code,
const std::string& device_path) {
for (auto& observer : observer_list_)
observer.OnRenameCompleted(error_code, device_path);
}
void FakeCrosDisksClient::NotifyMountEvent(MountEventType mount_event,
const std::string& device_path) {
for (auto& observer : observer_list_) {
observer.OnMountEvent(mount_event, device_path);
}
}
void FakeCrosDisksClient::AddCustomMountPointCallback(
FakeCrosDisksClient::CustomMountPointCallback custom_mount_point_callback) {
custom_mount_point_callbacks_.emplace_back(custom_mount_point_callback);
}
} // namespace ash