blob: 75ef0850ab05e21054d5e4b9da568f5c1659843d [file] [log] [blame]
// Copyright (c) 2013 The Chromium 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 "chromeos/dbus/fake_cros_disks_client.h"
#include <utility>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "base/task/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "url/gurl.h"
namespace chromeos {
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 MOUNT_ERROR_INVALID_ARGUMENT;
// 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 MOUNT_ERROR_DIRECTORY_CREATION_FAILED;
}
// Fake network mounts are responsible for populating their mount paths so
// don't need a dummy file.
if (type == MOUNT_TYPE_NETWORK_STORAGE)
return MOUNT_ERROR_NONE;
// 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.";
const int write_result = base::WriteFile(
dummy_file_path, dummy_file_content.data(), dummy_file_content.size());
if (write_result != static_cast<int>(dummy_file_content.size())) {
DLOG(ERROR) << "Failed to put a dummy file at "
<< dummy_file_path.value();
return MOUNT_ERROR_MOUNT_PROGRAM_FAILED;
}
return MOUNT_ERROR_NONE;
}
} // namespace
FakeCrosDisksClient::FakeCrosDisksClient()
: unmount_call_count_(0),
unmount_error_(MOUNT_ERROR_NONE),
format_call_count_(0),
format_success_(true),
rename_call_count_(0),
rename_success_(true),
weak_ptr_factory_(this) {}
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,
VoidDBusMethodCallback callback) {
// This fake implementation assumes mounted path is device when source_format
// is empty, or an archive otherwise.
MountType type =
source_format.empty() ? MOUNT_TYPE_DEVICE : MOUNT_TYPE_ARCHIVE;
// Network storage source paths are URIs.
if (GURL(source_path).is_valid())
type = MOUNT_TYPE_NETWORK_STORAGE;
base::FilePath mounted_path;
switch (type) {
case MOUNT_TYPE_ARCHIVE:
mounted_path = GetArchiveMountPoint().Append(
base::FilePath::FromUTF8Unsafe(mount_label));
break;
case MOUNT_TYPE_DEVICE:
mounted_path = GetRemovableDiskMountPoint().Append(
base::FilePath::FromUTF8Unsafe(mount_label));
break;
case MOUNT_TYPE_NETWORK_STORAGE:
// Call all registered callbacks until mounted_path is non-empty.
for (auto const& callback : custom_mount_point_callbacks_) {
mounted_path = callback.Run(source_path, mount_options);
if (!mounted_path.empty()) {
break;
}
}
break;
case MOUNT_TYPE_INVALID:
NOTREACHED();
return;
}
mounted_paths_.insert(mounted_path);
base::PostTaskWithTraitsAndReplyWithResult(
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,
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,
UnmountOptions options,
UnmountCallback callback) {
DCHECK(!callback.is_null());
unmount_call_count_++;
last_unmount_device_path_ = device_path;
last_unmount_options_ = options;
// Remove the dummy mounted directory if it exists.
if (mounted_paths_.erase(base::FilePath::FromUTF8Unsafe(device_path))) {
base::PostTaskWithTraitsAndReply(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(base::IgnoreResult(&base::DeleteFile),
base::FilePath::FromUTF8Unsafe(device_path),
true /* recursive */),
base::BindOnce(std::move(callback), unmount_error_));
} else {
base::ThreadTaskRunnerHandle::Get()->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,
VoidDBusMethodCallback callback) {
DCHECK(!callback.is_null());
format_call_count_++;
last_format_device_path_ = device_path;
last_format_filesystem_ = filesystem;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), format_success_));
}
void FakeCrosDisksClient::Rename(const std::string& device_path,
const std::string& volume_name,
VoidDBusMethodCallback callback) {
DCHECK(!callback.is_null());
rename_call_count_++;
last_rename_device_path_ = device_path;
last_rename_volume_name_ = volume_name;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), rename_success_));
}
void FakeCrosDisksClient::GetDeviceProperties(
const std::string& device_path,
GetDevicePropertiesCallback callback,
base::OnceClosure error_callback) {}
void FakeCrosDisksClient::NotifyMountCompleted(MountError error_code,
const std::string& source_path,
MountType mount_type,
const std::string& mount_path) {
for (auto& observer : observer_list_) {
observer.OnMountCompleted(
MountEntry(error_code, source_path, mount_type, mount_path));
}
}
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::AddCustomMountPointCallback(
FakeCrosDisksClient::CustomMountPointCallback custom_mount_point_callback) {
custom_mount_point_callbacks_.emplace_back(custom_mount_point_callback);
}
} // namespace chromeos