blob: 5e7551bc9675d8357b9853fe24f824ad4036ff32 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/drive/fileapi/drivefs_async_file_util.h"
#include <utility>
#include "base/bind.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/task/sequenced_task_runner.h"
#include "chrome/browser/ash/drive/drive_integration_service.h"
#include "chrome/browser/ash/drive/file_system_util.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_operation.h"
#include "storage/browser/file_system/file_system_operation_context.h"
#include "storage/browser/file_system/file_system_url.h"
#include "storage/browser/file_system/local_file_util.h"
#include "storage/browser/file_system/native_file_util.h"
#include "storage/common/file_system/file_system_util.h"
namespace drive {
namespace internal {
namespace {
class DriveFsFileUtil : public storage::LocalFileUtil {
public:
DriveFsFileUtil() = default;
DriveFsFileUtil(const DriveFsFileUtil&) = delete;
DriveFsFileUtil& operator=(const DriveFsFileUtil&) = delete;
~DriveFsFileUtil() override = default;
protected:
bool IsHiddenItem(const base::FilePath& local_file_path) const override {
// DriveFS is a trusted filesystem, allow symlinks.
return false;
}
};
class CopyOperation {
public:
CopyOperation(
Profile* profile,
std::unique_ptr<storage::FileSystemOperationContext> context,
const storage::FileSystemURL& src_url,
const storage::FileSystemURL& dest_url,
storage::AsyncFileUtil::CopyOrMoveOptionSet options,
storage::AsyncFileUtil::CopyFileProgressCallback progress_callback,
storage::AsyncFileUtil::StatusCallback callback,
scoped_refptr<base::SequencedTaskRunner> origin_task_runner,
base::WeakPtr<DriveFsAsyncFileUtil> async_file_util)
: profile_(profile),
context_(std::move(context)),
src_url_(src_url),
dest_url_(dest_url),
options_(options),
progress_callback_(std::move(progress_callback)),
callback_(std::move(callback)),
origin_task_runner_(std::move(origin_task_runner)),
async_file_util_(std::move(async_file_util)) {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
}
CopyOperation(const CopyOperation&) = delete;
CopyOperation& operator=(const CopyOperation&) = delete;
void Start() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
auto* drive_integration_service =
drive::util::GetIntegrationServiceByProfile(profile_);
base::FilePath source_path("/");
base::FilePath destination_path("/");
if (!drive_integration_service ||
!drive_integration_service->GetMountPointPath().AppendRelativePath(
src_url_.path(), &source_path) ||
!drive_integration_service->GetMountPointPath().AppendRelativePath(
dest_url_.path(), &destination_path)) {
origin_task_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(callback_),
base::File::FILE_ERROR_INVALID_OPERATION));
origin_task_runner_->DeleteSoon(FROM_HERE, this);
return;
}
drive_integration_service->GetDriveFsInterface()->CopyFile(
source_path, destination_path,
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
base::BindOnce(&CopyOperation::CopyComplete,
base::Unretained(this)),
drive::FILE_ERROR_ABORT));
}
private:
void CopyComplete(drive::FileError error) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
switch (error) {
case drive::FILE_ERROR_NOT_FOUND:
case drive::FILE_ERROR_NO_CONNECTION:
origin_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&CopyOperation::FallbackToNativeCopyOnOriginThread,
base::Unretained(this)));
break;
default:
origin_task_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(callback_),
FileErrorToBaseFileError(error)));
origin_task_runner_->DeleteSoon(FROM_HERE, this);
}
}
void FallbackToNativeCopyOnOriginThread() {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
if (!async_file_util_) {
std::move(callback_).Run(base::File::FILE_ERROR_ABORT);
return;
}
async_file_util_->AsyncFileUtilAdapter::CopyFileLocal(
std::move(context_), src_url_, dest_url_, options_,
std::move(progress_callback_), std::move(callback_));
delete this;
}
Profile* const profile_;
std::unique_ptr<storage::FileSystemOperationContext> context_;
const storage::FileSystemURL src_url_;
const storage::FileSystemURL dest_url_;
const storage::AsyncFileUtil::CopyOrMoveOptionSet options_;
storage::AsyncFileUtil::CopyFileProgressCallback progress_callback_;
storage::AsyncFileUtil::StatusCallback callback_;
scoped_refptr<base::SequencedTaskRunner> origin_task_runner_;
base::WeakPtr<DriveFsAsyncFileUtil> async_file_util_;
};
// Recursively deletes a folder locally. The folder will still be available in
// Drive cloud Trash.
class DeleteOperation {
public:
DeleteOperation(Profile* profile,
const base::FilePath& path,
storage::AsyncFileUtil::StatusCallback callback,
scoped_refptr<base::SequencedTaskRunner> origin_task_runner,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
: profile_(profile),
path_(path),
callback_(std::move(callback)),
origin_task_runner_(std::move(origin_task_runner)),
blocking_task_runner_(std::move(blocking_task_runner)) {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
}
DeleteOperation(const DeleteOperation&) = delete;
DeleteOperation& operator=(const DeleteOperation&) = delete;
void Start() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
auto* drive_integration_service =
drive::util::GetIntegrationServiceByProfile(profile_);
base::FilePath relative_path;
if (!drive_integration_service ||
!drive_integration_service->GetMountPointPath().IsParent(path_)) {
origin_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback_), base::File::FILE_ERROR_FAILED));
origin_task_runner_->DeleteSoon(FROM_HERE, this);
return;
}
blocking_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&DeleteOperation::Delete, base::Unretained(this)));
}
void Delete() {
base::File::Error error = base::DeletePathRecursively(path_)
? base::File::FILE_OK
: base::File::FILE_ERROR_FAILED;
origin_task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback_), error));
origin_task_runner_->DeleteSoon(FROM_HERE, this);
}
Profile* const profile_;
const base::FilePath path_;
storage::AsyncFileUtil::StatusCallback callback_;
scoped_refptr<base::SequencedTaskRunner> origin_task_runner_;
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
base::FilePath path_in_trash_;
};
} // namespace
DriveFsAsyncFileUtil::DriveFsAsyncFileUtil(Profile* profile)
: AsyncFileUtilAdapter(std::make_unique<DriveFsFileUtil>()),
profile_(profile) {}
DriveFsAsyncFileUtil::~DriveFsAsyncFileUtil() = default;
void DriveFsAsyncFileUtil::CopyFileLocal(
std::unique_ptr<storage::FileSystemOperationContext> context,
const storage::FileSystemURL& src_url,
const storage::FileSystemURL& dest_url,
CopyOrMoveOptionSet options,
CopyFileProgressCallback progress_callback,
StatusCallback callback) {
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(
&CopyOperation::Start,
base::Unretained(new CopyOperation(
profile_, std::move(context), src_url, dest_url, options,
std::move(progress_callback), std::move(callback),
base::SequencedTaskRunner::GetCurrentDefault(),
weak_factory_.GetWeakPtr()))));
}
void DriveFsAsyncFileUtil::DeleteRecursively(
std::unique_ptr<storage::FileSystemOperationContext> context,
const storage::FileSystemURL& url,
StatusCallback callback) {
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&DeleteOperation::Start,
base::Unretained(new DeleteOperation(
profile_, url.path(), std::move(callback),
base::SequencedTaskRunner::GetCurrentDefault(),
context->task_runner()))));
}
} // namespace internal
} // namespace drive