| // Copyright 2022 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/file_manager/trash_info_validator.h" |
| |
| #include <string_view> |
| |
| #include "base/files/file.h" |
| #include "base/files/file_util.h" |
| #include "base/notreached.h" |
| #include "base/task/bind_post_task.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/task_traits.h" |
| #include "base/task/thread_pool.h" |
| |
| class Profile; |
| |
| namespace file_manager::trash { |
| |
| namespace { |
| |
| void RunCallbackWithError(ValidationError error, |
| ValidateAndParseTrashInfoCallback callback) { |
| std::move(callback).Run(base::unexpected<ValidationError>(std::move(error))); |
| } |
| |
| } // namespace |
| |
| ParsedTrashInfoData::ParsedTrashInfoData() = default; |
| ParsedTrashInfoData::~ParsedTrashInfoData() = default; |
| |
| ParsedTrashInfoData::ParsedTrashInfoData(ParsedTrashInfoData&& other) = default; |
| ParsedTrashInfoData& ParsedTrashInfoData::operator=( |
| ParsedTrashInfoData&& other) = default; |
| |
| std::ostream& operator<<(std::ostream& out, const ValidationError& value) { |
| switch (value) { |
| case ValidationError::kFileNotExist: |
| out << "kFileNotExist"; |
| break; |
| case ValidationError::kInfoNotExist: |
| out << "kInfoNotExist"; |
| break; |
| case ValidationError::kInfoFileInvalidLocation: |
| out << "kInfoFileInvalidLocation"; |
| break; |
| case ValidationError::kInfoFileInvalid: |
| out << "kInfoFileInvalid"; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| return out; |
| } |
| |
| base::File::Error ValidationErrorToFileError(ValidationError error) { |
| switch (error) { |
| case ValidationError::kFileNotExist: |
| return base::File::FILE_ERROR_NOT_FOUND; |
| case ValidationError::kInfoNotExist: |
| return base::File::FILE_ERROR_NOT_FOUND; |
| case ValidationError::kInfoFileInvalidLocation: |
| return base::File::FILE_ERROR_INVALID_URL; |
| case ValidationError::kInfoFileInvalid: |
| return base::File::FILE_ERROR_INVALID_OPERATION; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| TrashInfoValidator::TrashInfoValidator(Profile* profile) { |
| enabled_trash_locations_ = |
| trash::GenerateEnabledTrashLocationsForProfile(profile); |
| |
| parser_ = std::make_unique<ash::trash_service::TrashInfoParser>(); |
| } |
| |
| TrashInfoValidator::~TrashInfoValidator() = default; |
| |
| void TrashInfoValidator::SetDisconnectHandler( |
| base::OnceCallback<void()> disconnect_callback) { |
| DCHECK(parser_) << "Parser should not be null here"; |
| if (parser_) { |
| parser_->SetDisconnectHandler(std::move(disconnect_callback)); |
| } |
| } |
| |
| void TrashInfoValidator::ValidateAndParseTrashInfo( |
| const base::FilePath& trash_info_path, |
| ValidateAndParseTrashInfoCallback callback) { |
| // Validates the supplied file ends in a .trashinfo extension. |
| if (trash_info_path.FinalExtension() != kTrashInfoExtension) { |
| RunCallbackWithError(ValidationError::kInfoFileInvalid, |
| std::move(callback)); |
| return; |
| } |
| |
| // Validate the .trashinfo file belongs in an enabled trash location. |
| base::FilePath trash_folder_location; |
| base::FilePath mount_point_path; |
| for (const auto& [parent_path, info] : enabled_trash_locations_) { |
| if (parent_path.Append(info.relative_folder_path) |
| .IsParent(trash_info_path)) { |
| trash_folder_location = parent_path.Append(info.relative_folder_path); |
| mount_point_path = info.mount_point_path; |
| break; |
| } |
| } |
| |
| if (mount_point_path.empty() || trash_folder_location.empty()) { |
| RunCallbackWithError(ValidationError::kInfoFileInvalidLocation, |
| std::move(callback)); |
| return; |
| } |
| |
| // Ensure the corresponding file that this metadata file refers to actually |
| // exists. |
| base::FilePath trashed_file_location = |
| trash_folder_location.Append(kFilesFolderName) |
| .Append(trash_info_path.BaseName().RemoveFinalExtension()); |
| |
| base::ThreadPool::PostTaskAndReplyWithResult( |
| FROM_HERE, {base::MayBlock()}, |
| base::BindOnce(&base::PathExists, trashed_file_location), |
| base::BindOnce(&TrashInfoValidator::OnTrashedFileExists, |
| weak_ptr_factory_.GetWeakPtr(), mount_point_path, |
| trashed_file_location, std::move(trash_info_path), |
| std::move(callback))); |
| } |
| |
| void TrashInfoValidator::OnTrashedFileExists( |
| const base::FilePath& mount_point_path, |
| const base::FilePath& trashed_file_location, |
| const base::FilePath& trash_info_path, |
| ValidateAndParseTrashInfoCallback callback, |
| bool exists) { |
| if (!exists) { |
| RunCallbackWithError(ValidationError::kFileNotExist, std::move(callback)); |
| return; |
| } |
| |
| auto complete_callback = base::BindPostTaskToCurrentDefault(base::BindOnce( |
| &TrashInfoValidator::OnTrashInfoParsed, weak_ptr_factory_.GetWeakPtr(), |
| trash_info_path, mount_point_path, trashed_file_location, |
| std::move(callback))); |
| |
| parser_->ParseTrashInfoFile(trash_info_path, std::move(complete_callback)); |
| } |
| |
| void TrashInfoValidator::OnTrashInfoParsed( |
| const base::FilePath& trash_info_path, |
| const base::FilePath& mount_point_path, |
| const base::FilePath& trashed_file_location, |
| ValidateAndParseTrashInfoCallback callback, |
| base::File::Error status, |
| const base::FilePath& restore_path, |
| base::Time deletion_date) { |
| if (status != base::File::FILE_OK) { |
| RunCallbackWithError((status == base::File::FILE_ERROR_NOT_FOUND) |
| ? ValidationError::kInfoNotExist |
| : ValidationError::kInfoFileInvalid, |
| std::move(callback)); |
| return; |
| } |
| |
| // The restore path that was parsed could be empty, not have a leading "/" or |
| // only consist of "/". |
| if (restore_path.empty() || |
| restore_path.value()[0] != base::FilePath::kSeparators[0] || |
| (restore_path.value().size() == 1 && |
| restore_path.value()[0] == base::FilePath::kSeparators[0])) { |
| RunCallbackWithError(ValidationError::kInfoFileInvalid, |
| std::move(callback)); |
| return; |
| } |
| |
| // Remove the leading "/" character to make the restore path relative from the |
| // known trash parent path. |
| std::string_view relative_path = |
| std::string_view(restore_path.value()).substr(1); |
| base::FilePath absolute_restore_path = mount_point_path.Append(relative_path); |
| |
| ParsedTrashInfoData parsed_data; |
| parsed_data.trash_info_path = std::move(trash_info_path); |
| parsed_data.trashed_file_path = std::move(trashed_file_location); |
| parsed_data.absolute_restore_path = std::move(absolute_restore_path); |
| parsed_data.deletion_date = std::move(deletion_date); |
| |
| std::move(callback).Run( |
| base::ok<ParsedTrashInfoData>(std::move(parsed_data))); |
| } |
| |
| } // namespace file_manager::trash |