Reland "Turn DriveFS directory deletes into moves into a trash directory."
This is a reland of 9ecd235870
that:
- Updates FakeDriveFs to create a .Trash directory.
- Move files directly instead of delegating to MoveFileLocal.
- Leave (non-recursive) DeleteDirectory unchanged.
Original change's description:
> Turn DriveFS directory deletes into moves into a trash directory.
>
> Change NativeFileUtil to allow moving a directory to an existing
> directory.
>
> Bug: 829696
> Change-Id: I6e233fa2a9ad161d1457d06fc6b6eda3ab3955d7
> Reviewed-on: https://chromium-review.googlesource.com/c/1278624
> Reviewed-by: Hiroki Nakagawa <nhiroki@chromium.org>
> Reviewed-by: Sergei Datsenko <dats@chromium.org>
> Commit-Queue: Sam McNally <sammc@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#600283}
Bug: 829696
Change-Id: Ied7836010a00a8f3226b8f65e02568b3f2c2a47d
Reviewed-on: https://chromium-review.googlesource.com/c/1286025
Commit-Queue: Sam McNally <sammc@chromium.org>
Reviewed-by: Hiroki Nakagawa <nhiroki@chromium.org>
Reviewed-by: Sergei Datsenko <dats@chromium.org>
Cr-Commit-Position: refs/heads/master@{#600713}
This commit is contained in:
chrome/browser/chromeos/drive/fileapi
chromeos/components/drivefs
storage/browser/fileapi
@@ -6,20 +6,26 @@
|
|||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "base/files/file_util.h"
|
||||||
#include "base/task/post_task.h"
|
#include "base/task/post_task.h"
|
||||||
#include "chrome/browser/chromeos/drive/drive_integration_service.h"
|
#include "chrome/browser/chromeos/drive/drive_integration_service.h"
|
||||||
#include "chrome/browser/chromeos/drive/file_system_util.h"
|
#include "chrome/browser/chromeos/drive/file_system_util.h"
|
||||||
#include "content/public/browser/browser_task_traits.h"
|
#include "content/public/browser/browser_task_traits.h"
|
||||||
#include "content/public/browser/browser_thread.h"
|
#include "content/public/browser/browser_thread.h"
|
||||||
#include "mojo/public/cpp/bindings/callback_helpers.h"
|
#include "mojo/public/cpp/bindings/callback_helpers.h"
|
||||||
|
#include "storage/browser/fileapi/file_system_context.h"
|
||||||
|
#include "storage/browser/fileapi/file_system_operation.h"
|
||||||
#include "storage/browser/fileapi/file_system_operation_context.h"
|
#include "storage/browser/fileapi/file_system_operation_context.h"
|
||||||
#include "storage/browser/fileapi/file_system_url.h"
|
#include "storage/browser/fileapi/file_system_url.h"
|
||||||
#include "storage/browser/fileapi/local_file_util.h"
|
#include "storage/browser/fileapi/local_file_util.h"
|
||||||
|
#include "storage/common/fileapi/file_system_util.h"
|
||||||
|
|
||||||
namespace drive {
|
namespace drive {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
constexpr char kTrashDirectoryName[] = ".Trash";
|
||||||
|
|
||||||
class CopyOperation {
|
class CopyOperation {
|
||||||
public:
|
public:
|
||||||
CopyOperation(
|
CopyOperation(
|
||||||
@@ -117,6 +123,67 @@ class CopyOperation {
|
|||||||
DISALLOW_COPY_AND_ASSIGN(CopyOperation);
|
DISALLOW_COPY_AND_ASSIGN(CopyOperation);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Recursively deletes a folder by moving it into the .Trash folder within the
|
||||||
|
// DriveFS mount point.
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
path_in_trash_ = drive_integration_service->GetMountPointPath()
|
||||||
|
.Append(kTrashDirectoryName)
|
||||||
|
.Append(path_.BaseName());
|
||||||
|
|
||||||
|
blocking_task_runner_->PostTask(
|
||||||
|
FROM_HERE,
|
||||||
|
base::BindOnce(&DeleteOperation::MoveToTrash, base::Unretained(this)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoveToTrash() {
|
||||||
|
base::File::Error error = base::Move(path_, path_in_trash_)
|
||||||
|
? 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_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(DeleteOperation);
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
DriveFsAsyncFileUtil::DriveFsAsyncFileUtil(Profile* profile)
|
DriveFsAsyncFileUtil::DriveFsAsyncFileUtil(Profile* profile)
|
||||||
@@ -144,5 +211,18 @@ void DriveFsAsyncFileUtil::CopyFileLocal(
|
|||||||
weak_factory_.GetWeakPtr()))));
|
weak_factory_.GetWeakPtr()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DriveFsAsyncFileUtil::DeleteRecursively(
|
||||||
|
std::unique_ptr<storage::FileSystemOperationContext> context,
|
||||||
|
const storage::FileSystemURL& url,
|
||||||
|
StatusCallback callback) {
|
||||||
|
base::PostTaskWithTraits(
|
||||||
|
FROM_HERE, {content::BrowserThread::UI},
|
||||||
|
base::BindOnce(&DeleteOperation::Start,
|
||||||
|
base::Unretained(new DeleteOperation(
|
||||||
|
profile_, url.path(), std::move(callback),
|
||||||
|
base::SequencedTaskRunnerHandle::Get(),
|
||||||
|
context->task_runner()))));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace drive
|
} // namespace drive
|
||||||
|
@@ -31,6 +31,10 @@ class DriveFsAsyncFileUtil : public storage::AsyncFileUtilAdapter {
|
|||||||
CopyOrMoveOption option,
|
CopyOrMoveOption option,
|
||||||
CopyFileProgressCallback progress_callback,
|
CopyFileProgressCallback progress_callback,
|
||||||
StatusCallback callback) override;
|
StatusCallback callback) override;
|
||||||
|
void DeleteRecursively(
|
||||||
|
std::unique_ptr<storage::FileSystemOperationContext> context,
|
||||||
|
const storage::FileSystemURL& url,
|
||||||
|
StatusCallback callback) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Profile* const profile_;
|
Profile* const profile_;
|
||||||
|
@@ -287,6 +287,7 @@ void FakeDriveFs::SetMetadata(const base::FilePath& path,
|
|||||||
void FakeDriveFs::Init(drivefs::mojom::DriveFsConfigurationPtr config,
|
void FakeDriveFs::Init(drivefs::mojom::DriveFsConfigurationPtr config,
|
||||||
drivefs::mojom::DriveFsRequest drive_fs_request,
|
drivefs::mojom::DriveFsRequest drive_fs_request,
|
||||||
drivefs::mojom::DriveFsDelegatePtr delegate) {
|
drivefs::mojom::DriveFsDelegatePtr delegate) {
|
||||||
|
CHECK(base::CreateDirectory(mount_path_.Append(".Trash")));
|
||||||
mojo::FuseInterface(std::move(pending_delegate_request_),
|
mojo::FuseInterface(std::move(pending_delegate_request_),
|
||||||
delegate.PassInterface());
|
delegate.PassInterface());
|
||||||
if (binding_.is_bound())
|
if (binding_.is_bound())
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "base/files/file_enumerator.h"
|
#include "base/files/file_enumerator.h"
|
||||||
#include "base/files/file_util.h"
|
#include "base/files/file_util.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
#include "storage/browser/fileapi/file_system_operation_context.h"
|
#include "storage/browser/fileapi/file_system_operation_context.h"
|
||||||
#include "storage/browser/fileapi/file_system_url.h"
|
#include "storage/browser/fileapi/file_system_url.h"
|
||||||
#include "storage/common/fileapi/file_system_mount_option.h"
|
#include "storage/common/fileapi/file_system_mount_option.h"
|
||||||
@@ -262,8 +263,17 @@ base::File::Error NativeFileUtil::CopyOrMoveFile(
|
|||||||
if (error != base::File::FILE_OK &&
|
if (error != base::File::FILE_OK &&
|
||||||
error != base::File::FILE_ERROR_NOT_FOUND)
|
error != base::File::FILE_ERROR_NOT_FOUND)
|
||||||
return error;
|
return error;
|
||||||
if (error == base::File::FILE_OK && (info.is_directory || src_is_directory))
|
if (error == base::File::FILE_OK) {
|
||||||
return base::File::FILE_ERROR_INVALID_OPERATION;
|
if (info.is_directory != src_is_directory)
|
||||||
|
return base::File::FILE_ERROR_INVALID_OPERATION;
|
||||||
|
#if defined(OS_WIN)
|
||||||
|
// Overwriting an empty directory with another directory isn't supported
|
||||||
|
// natively on Windows, so treat this an unsupported. A higher layer is
|
||||||
|
// responsible for handling it.
|
||||||
|
if (info.is_directory)
|
||||||
|
return base::File::FILE_ERROR_NOT_A_FILE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
if (error == base::File::FILE_ERROR_NOT_FOUND) {
|
if (error == base::File::FILE_ERROR_NOT_FOUND) {
|
||||||
error = NativeFileUtil::GetFileInfo(dest_path.DirName(), &info);
|
error = NativeFileUtil::GetFileInfo(dest_path.DirName(), &info);
|
||||||
if (error != base::File::FILE_OK)
|
if (error != base::File::FILE_OK)
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
#include "base/files/file_util.h"
|
#include "base/files/file_util.h"
|
||||||
#include "base/files/scoped_temp_dir.h"
|
#include "base/files/scoped_temp_dir.h"
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
#include "storage/browser/fileapi/native_file_util.h"
|
#include "storage/browser/fileapi/native_file_util.h"
|
||||||
#include "testing/gtest/include/gtest/gtest.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|
||||||
@@ -335,10 +336,12 @@ TEST_F(NativeFileUtilTest, MoveFile) {
|
|||||||
NativeFileUtil::CopyOrMoveFile(
|
NativeFileUtil::CopyOrMoveFile(
|
||||||
dir, to_file, FileSystemOperation::OPTION_NONE, move));
|
dir, to_file, FileSystemOperation::OPTION_NONE, move));
|
||||||
|
|
||||||
|
#if defined(OS_WIN)
|
||||||
// Source is a directory, destination is a directory.
|
// Source is a directory, destination is a directory.
|
||||||
EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION,
|
EXPECT_EQ(base::File::FILE_ERROR_NOT_A_FILE,
|
||||||
NativeFileUtil::CopyOrMoveFile(
|
NativeFileUtil::CopyOrMoveFile(
|
||||||
dir, dir2, FileSystemOperation::OPTION_NONE, move));
|
dir, dir2, FileSystemOperation::OPTION_NONE, move));
|
||||||
|
#endif
|
||||||
|
|
||||||
ASSERT_EQ(base::File::FILE_OK,
|
ASSERT_EQ(base::File::FILE_OK,
|
||||||
NativeFileUtil::EnsureFileExists(from_file, &created));
|
NativeFileUtil::EnsureFileExists(from_file, &created));
|
||||||
@@ -372,8 +375,7 @@ TEST_F(NativeFileUtilTest, MoveFile_Directory) {
|
|||||||
const NativeFileUtil::CopyOrMoveMode move = NativeFileUtil::MOVE;
|
const NativeFileUtil::CopyOrMoveMode move = NativeFileUtil::MOVE;
|
||||||
bool created = false;
|
bool created = false;
|
||||||
ASSERT_EQ(base::File::FILE_OK,
|
ASSERT_EQ(base::File::FILE_OK,
|
||||||
NativeFileUtil::EnsureFileExists(
|
NativeFileUtil::EnsureFileExists(from_file, &created));
|
||||||
from_directory.AppendASCII("fromfile"), &created));
|
|
||||||
ASSERT_TRUE(created);
|
ASSERT_TRUE(created);
|
||||||
|
|
||||||
ASSERT_EQ(base::File::FILE_OK, NativeFileUtil::Truncate(from_file, 1020));
|
ASSERT_EQ(base::File::FILE_OK, NativeFileUtil::Truncate(from_file, 1020));
|
||||||
@@ -392,6 +394,37 @@ TEST_F(NativeFileUtilTest, MoveFile_Directory) {
|
|||||||
EXPECT_EQ(1020, GetSize(to_file));
|
EXPECT_EQ(1020, GetSize(to_file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(OS_WIN)
|
||||||
|
TEST_F(NativeFileUtilTest, MoveFile_OverwriteEmptyDirectory) {
|
||||||
|
base::FilePath from_directory = Path("fromdirectory");
|
||||||
|
base::FilePath to_directory = Path("todirectory");
|
||||||
|
base::FilePath from_file = from_directory.AppendASCII("fromfile");
|
||||||
|
base::FilePath to_file = to_directory.AppendASCII("fromfile");
|
||||||
|
ASSERT_TRUE(base::CreateDirectory(from_directory));
|
||||||
|
ASSERT_TRUE(base::CreateDirectory(to_directory));
|
||||||
|
const NativeFileUtil::CopyOrMoveMode move = NativeFileUtil::MOVE;
|
||||||
|
bool created = false;
|
||||||
|
ASSERT_EQ(base::File::FILE_OK,
|
||||||
|
NativeFileUtil::EnsureFileExists(from_file, &created));
|
||||||
|
ASSERT_TRUE(created);
|
||||||
|
|
||||||
|
ASSERT_EQ(base::File::FILE_OK, NativeFileUtil::Truncate(from_file, 1020));
|
||||||
|
|
||||||
|
EXPECT_TRUE(FileExists(from_file));
|
||||||
|
EXPECT_EQ(1020, GetSize(from_file));
|
||||||
|
|
||||||
|
ASSERT_EQ(base::File::FILE_OK, NativeFileUtil::CopyOrMoveFile(
|
||||||
|
from_directory, to_directory,
|
||||||
|
FileSystemOperation::OPTION_NONE, move));
|
||||||
|
|
||||||
|
EXPECT_FALSE(base::DirectoryExists(from_directory));
|
||||||
|
EXPECT_FALSE(FileExists(from_file));
|
||||||
|
EXPECT_TRUE(base::DirectoryExists(to_directory));
|
||||||
|
EXPECT_TRUE(FileExists(to_file));
|
||||||
|
EXPECT_EQ(1020, GetSize(to_file));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST_F(NativeFileUtilTest, PreserveLastModified) {
|
TEST_F(NativeFileUtilTest, PreserveLastModified) {
|
||||||
base::FilePath from_file = Path("fromfile");
|
base::FilePath from_file = Path("fromfile");
|
||||||
base::FilePath to_file1 = Path("tofile1");
|
base::FilePath to_file1 = Path("tofile1");
|
||||||
|
Reference in New Issue
Block a user