chromium/chrome/browser/ash/extensions/file_manager/private_api_file_system.cc

// 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 "chrome/browser/ash/extensions/file_manager/private_api_file_system.h"

#include <sys/statvfs.h>
#include <sys/xattr.h>

#include <algorithm>
#include <cstdint>
#include <iterator>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "base/barrier_callback.h"
#include "base/files/file.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/posix/eintr_wrapper.h"
#include "base/ranges/algorithm.h"
#include "base/strings/escape.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
#include "base/task/thread_pool.h"
#include "base/threading/platform_thread.h"
#include "base/types/cxx23_to_underlying.h"
#include "base/values.h"
#include "chrome/browser/ash/app_list/search/local_image_search/local_image_search_service.h"
#include "chrome/browser/ash/app_list/search/local_image_search/local_image_search_service_factory.h"
#include "chrome/browser/ash/app_list/search/search_features.h"
#include "chrome/browser/ash/arc/fileapi/arc_documents_provider_root.h"
#include "chrome/browser/ash/arc/fileapi/arc_documents_provider_root_map.h"
#include "chrome/browser/ash/arc/fileapi/arc_documents_provider_util.h"
#include "chrome/browser/ash/drive/file_system_util.h"
#include "chrome/browser/ash/extensions/file_manager/event_router.h"
#include "chrome/browser/ash/extensions/file_manager/event_router_factory.h"
#include "chrome/browser/ash/extensions/file_manager/private_api_util.h"
#include "chrome/browser/ash/extensions/file_manager/search_by_pattern.h"
#include "chrome/browser/ash/extensions/file_manager/select_file_dialog_extension_user_data.h"
#include "chrome/browser/ash/file_manager/copy_or_move_io_task.h"
#include "chrome/browser/ash/file_manager/delete_io_task.h"
#include "chrome/browser/ash/file_manager/empty_trash_io_task.h"
#include "chrome/browser/ash/file_manager/extract_io_task.h"
#include "chrome/browser/ash/file_manager/file_manager_copy_or_move_hook_delegate.h"
#include "chrome/browser/ash/file_manager/fileapi_util.h"
#include "chrome/browser/ash/file_manager/io_task.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/ash/file_manager/restore_io_task.h"
#include "chrome/browser/ash/file_manager/restore_to_destination_io_task.h"
#include "chrome/browser/ash/file_manager/trash_common_util.h"
#include "chrome/browser/ash/file_manager/trash_io_task.h"
#include "chrome/browser/ash/file_manager/volume_manager.h"
#include "chrome/browser/ash/file_manager/zip_io_task.h"
#include "chrome/browser/ash/fileapi/file_system_backend.h"
#include "chrome/browser/ash/fileapi/recent_disk_source.h"
#include "chrome/browser/ash/policy/dlp/dialogs/files_policy_dialog.h"
#include "chrome/browser/ash/policy/dlp/dlp_files_controller_ash.h"
#include "chrome/browser/ash/policy/dlp/files_policy_notification_manager.h"
#include "chrome/browser/ash/policy/dlp/files_policy_notification_manager_factory.h"
#include "chrome/browser/ash/policy/skyvault/policy_utils.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_file_destination.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_factory.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/extensions/api/file_manager_private.h"
#include "chrome/common/extensions/api/file_manager_private_internal.h"
#include "chromeos/ash/components/disks/disk.h"
#include "chromeos/ash/components/disks/disk_mount_manager.h"
#include "components/drive/event_logger.h"
#include "components/drive/file_system_core_util.h"
#include "components/enterprise/data_controls/core/browser/component.h"
#include "components/prefs/pref_service.h"
#include "components/storage_monitor/storage_info.h"
#include "components/storage_monitor/storage_monitor.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/url_constants.h"
#include "extensions/browser/extension_function.h"
#include "extensions/browser/extension_util.h"
#include "private_api_file_system.h"
#include "services/device/public/mojom/mtp_manager.mojom.h"
#include "services/device/public/mojom/mtp_storage_info.mojom.h"
#include "storage/browser/file_system/external_mount_points.h"
#include "storage/browser/file_system/file_stream_reader.h"
#include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_file_util.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_operation_runner.h"
#include "storage/browser/file_system/file_system_url.h"
#include "storage/common/file_system/file_system_info.h"
#include "storage/common/file_system/file_system_types.h"
#include "storage/common/file_system/file_system_util.h"
#include "third_party/cros_system_api/constants/cryptohome.h"
#include "ui/base/clipboard/clipboard_buffer.h"
#include "ui/base/clipboard/clipboard_non_backed.h"
#include "ui/shell_dialogs/select_file_dialog.h"

using ::ash::disks::DiskMountManager;
using content::BrowserThread;
using content::ChildProcessSecurityPolicy;
using file_manager::util::EntryDefinition;
using file_manager::util::FileDefinition;
using storage::FileSystemURL;

namespace extensions {
namespace {

using file_manager::Volume;
using file_manager::VolumeManager;

std::string Redact(const std::string_view s) {
  return LOG_IS_ON(INFO) ? base::StrCat({"'", s, "'"}) : "(redacted)";
}

std::string Redact(const base::FilePath& path) {
  return Redact(path.value());
}

const char kRootPath[] = "/";

// Retrieves total and remaining available size on |mount_path|.
void GetSizeStatsAsync(const base::FilePath& mount_path,
                       uint64_t* total_size,
                       uint64_t* remaining_size) {
  int64_t size = base::SysInfo::AmountOfTotalDiskSpace(mount_path);
  if (size >= 0) {
    *total_size = size;
  }
  size = base::SysInfo::AmountOfFreeDiskSpace(mount_path);
  if (size >= 0) {
    *remaining_size = size;
  }
}

// Retrieves the maximum file name length of the file system of |path|.
// Returns a default of 255 if it could not be queried.
size_t GetFileNameMaxLengthAsync(const std::string& path) {
  struct statvfs stat = {};
  if (HANDLE_EINTR(statvfs(path.c_str(), &stat)) != 0) {
    // The filesystem seems not supporting statvfs(). Assume it to be a commonly
    // used bound 255, and log the failure.
    LOG(ERROR) << "Cannot statvfs() the name length limit for: " << path;
    return 255;
  }
  return stat.f_namemax;
}

// Converts a status code to a bool value and calls the |callback| with it.
void StatusCallbackToResponseCallback(base::OnceCallback<void(bool)> callback,
                                      base::File::Error result) {
  std::move(callback).Run(result == base::File::FILE_OK);
}

ash::disks::FormatFileSystemType ApiFormatFileSystemToChromeEnum(
    api::file_manager_private::FormatFileSystemType filesystem) {
  switch (filesystem) {
    case api::file_manager_private::FormatFileSystemType::kNone:
      return ash::disks::FormatFileSystemType::kUnknown;
    case api::file_manager_private::FormatFileSystemType::kVfat:
      return ash::disks::FormatFileSystemType::kVfat;
    case api::file_manager_private::FormatFileSystemType::kExfat:
      return ash::disks::FormatFileSystemType::kExfat;
    case api::file_manager_private::FormatFileSystemType::kNtfs:
      return ash::disks::FormatFileSystemType::kNtfs;
  }
  NOTREACHED_IN_MIGRATION()
      << "Unknown format filesystem " << base::to_underlying(filesystem);
  return ash::disks::FormatFileSystemType::kUnknown;
}

std::optional<file_manager::io_task::OperationType> IoTaskTypeToChromeEnum(
    api::file_manager_private::IoTaskType type) {
  switch (type) {
    case api::file_manager_private::IoTaskType::kCopy:
      return file_manager::io_task::OperationType::kCopy;
    case api::file_manager_private::IoTaskType::kDelete:
      return file_manager::io_task::OperationType::kDelete;
    case api::file_manager_private::IoTaskType::kEmptyTrash:
      return file_manager::io_task::OperationType::kEmptyTrash;
    case api::file_manager_private::IoTaskType::kExtract:
      return file_manager::io_task::OperationType::kExtract;
    case api::file_manager_private::IoTaskType::kMove:
      return file_manager::io_task::OperationType::kMove;
    case api::file_manager_private::IoTaskType::kRestore:
      return file_manager::io_task::OperationType::kRestore;
    case api::file_manager_private::IoTaskType::kRestoreToDestination:
      return file_manager::io_task::OperationType::kRestoreToDestination;
    case api::file_manager_private::IoTaskType::kTrash:
      return file_manager::io_task::OperationType::kTrash;
    case api::file_manager_private::IoTaskType::kZip:
      return file_manager::io_task::OperationType::kZip;
    case api::file_manager_private::IoTaskType::kNone:
      return {};
  }
  NOTREACHED_IN_MIGRATION()
      << "Unknown I/O task type " << base::to_underlying(type);
  return {};
}

extensions::api::file_manager_private::DlpLevel DlpRulesManagerLevelToApiEnum(
    policy::DlpRulesManager::Level level) {
  using extensions::api::file_manager_private::DlpLevel;
  switch (level) {
    case policy::DlpRulesManager::Level::kAllow:
      return DlpLevel::kAllow;
    case policy::DlpRulesManager::Level::kBlock:
      return DlpLevel::kBlock;
    case policy::DlpRulesManager::Level::kWarn:
      return DlpLevel::kWarn;
    case policy::DlpRulesManager::Level::kReport:
      return DlpLevel::kReport;
    case policy::DlpRulesManager::Level::kNotSet:
      NOTREACHED_IN_MIGRATION() << "DLP level not set.";
      return DlpLevel::kNone;
  }
  NOTREACHED_IN_MIGRATION() << "Unknown DLP level.";
  return {};
}

extensions::api::file_manager_private::VolumeType
DlpRulesManagerComponentToApiEnum(data_controls::Component component) {
  using ::extensions::api::file_manager_private::VolumeType;
  using Component = ::data_controls::Component;
  switch (component) {
    case Component::kArc:
      return VolumeType::kAndroidFiles;
    case Component::kCrostini:
      return VolumeType::kCrostini;
    case Component::kPluginVm:
      return VolumeType::kGuestOs;
    case Component::kUsb:
      return VolumeType::kRemovable;
    case Component::kDrive:
      return VolumeType::kDrive;
    case Component::kOneDrive:
      return VolumeType::kProvided;
    case Component::kUnknownComponent:
      NOTREACHED_IN_MIGRATION() << "DLP component not set.";
      return {};
  }
  NOTREACHED_IN_MIGRATION() << "Unknown component type.";
  return {};
}

policy::FilesDialogType ApiPolicyDialogTypeToChromeEnum(
    api::file_manager_private::PolicyDialogType type) {
  switch (type) {
    case api::file_manager_private::PolicyDialogType::kNone:
      return policy::FilesDialogType::kUnknown;
    case api::file_manager_private::PolicyDialogType::kWarning:
      return policy::FilesDialogType::kWarning;
    case api::file_manager_private::PolicyDialogType::kError:
      return policy::FilesDialogType::kError;
  }
  NOTREACHED_IN_MIGRATION()
      << "Unknown policy dialog type " << base::to_underlying(type);
  return policy::FilesDialogType::kUnknown;
}

std::optional<policy::Policy> ApiPolicyErrorTypeToChromeEnum(
    api::file_manager_private::PolicyErrorType type) {
  switch (type) {
    case api::file_manager_private::PolicyErrorType::kDlp:
      return policy::Policy::kDlp;
    case api::file_manager_private::PolicyErrorType::kEnterpriseConnectors:
      return policy::Policy::kEnterpriseConnectors;
    case api::file_manager_private::PolicyErrorType::kNone:
      return std::nullopt;
    case api::file_manager_private::PolicyErrorType::kDlpWarningTimeout:
      NOTREACHED_IN_MIGRATION()
          << "Unexpected policy type " << base::to_underlying(type);
  }
  NOTREACHED_IN_MIGRATION()
      << "Unknown policy error type " << base::to_underlying(type);
  return std::nullopt;
}

// Handles a callback from the LocalImageSearchService. The job of this function
// is to process the `matched` results and deliver them to the given callback.
void OnImageSearchDone(
    base::FilePath root_path,
    base::Time modified_time,
    FileManagerPrivateInternalSearchFilesFunction::OnResultsReadyCallback
        callback,
    const std::vector<app_list::FileSearchResult>& matched) {
  std::vector<std::pair<base::FilePath, bool>> results;
  for (const app_list::FileSearchResult& match : matched) {
    DVLOG(1) << "File image search inspecting " << match.file_path;
    if (!root_path.IsParent(match.file_path)) {
      continue;
    }
    if (match.last_modified < modified_time) {
      continue;
    }
    results.emplace_back(match.file_path, false);
  }
  std::move(callback).Run(results);
}

}  // namespace

ExtensionFunction::ResponseAction
FileManagerPrivateEnableExternalFileSchemeFunction::Run() {
  ChildProcessSecurityPolicy::GetInstance()->GrantRequestScheme(
      render_frame_host()->GetProcess()->GetID(), content::kExternalFileScheme);
  return RespondNow(NoArguments());
}

FileManagerPrivateGrantAccessFunction::FileManagerPrivateGrantAccessFunction() =
    default;

ExtensionFunction::ResponseAction FileManagerPrivateGrantAccessFunction::Run() {
  using extensions::api::file_manager_private::GrantAccess::Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  scoped_refptr<storage::FileSystemContext> file_system_context =
      file_manager::util::GetFileSystemContextForRenderFrameHost(
          Profile::FromBrowserContext(browser_context()), render_frame_host());

  auto* const backend = ash::FileSystemBackend::Get(*file_system_context);
  DCHECK(backend);

  const std::vector<Profile*>& profiles =
      g_browser_process->profile_manager()->GetLoadedProfiles();
  for (auto* profile : profiles) {
    if (profile->IsOffTheRecord()) {
      continue;
    }
    storage::FileSystemContext* const context =
        file_manager::util::GetFileSystemContextForSourceURL(profile,
                                                             source_url());
    for (const auto& url : params->entry_urls) {
      const storage::FileSystemURL file_system_url =
          context->CrackURLInFirstPartyContext(GURL(url));
      // Grant permissions only to valid urls backed by the external file system
      // backend.
      if (!file_system_url.is_valid() ||
          file_system_url.mount_type() != storage::kFileSystemTypeExternal) {
        continue;
      }
      backend->GrantFileAccessToOrigin(url::Origin::Create(source_url()),
                                       file_system_url.virtual_path());
      content::ChildProcessSecurityPolicy::GetInstance()
          ->GrantCreateReadWriteFile(render_frame_host()->GetProcess()->GetID(),
                                     file_system_url.path());
    }
  }
  return RespondNow(NoArguments());
}

namespace {

void PostResponseCallbackTaskToUIThread(
    FileWatchFunctionBase::ResponseCallback callback,
    bool success) {
  DCHECK_CURRENTLY_ON(BrowserThread::IO);
  content::GetUIThreadTaskRunner({})->PostTask(
      FROM_HERE, base::BindOnce(std::move(callback), success));
}

void PostNotificationCallbackTaskToUIThread(
    storage::WatcherManager::NotificationCallback callback,
    storage::WatcherManager::ChangeType type) {
  DCHECK_CURRENTLY_ON(BrowserThread::IO);
  content::GetUIThreadTaskRunner({})->PostTask(
      FROM_HERE, base::BindOnce(std::move(callback), type));
}

}  // namespace

void FileWatchFunctionBase::RespondWith(bool success) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  if (success) {
    Respond(WithArguments(success));
  } else {
    Respond(Error(""));
  }
}

ExtensionFunction::ResponseAction FileWatchFunctionBase::Run() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  if (!render_frame_host() || !render_frame_host()->GetProcess()) {
    return RespondNow(Error("Invalid state"));
  }

  // First param is url of a file to watch.
  if (args().empty() || !args()[0].is_string() ||
      args()[0].GetString().empty()) {
    return RespondNow(Error("Empty watch URL"));
  }
  const std::string& url = args()[0].GetString();

  Profile* const profile = Profile::FromBrowserContext(browser_context());
  scoped_refptr<storage::FileSystemContext> file_system_context =
      file_manager::util::GetFileSystemContextForRenderFrameHost(
          profile, render_frame_host());

  const FileSystemURL file_system_url =
      file_system_context->CrackURLInFirstPartyContext(GURL(url));
  if (file_system_url.path().empty()) {
    return RespondNow(Error("Invalid URL"));
  }

  // For removeFileWatch() we can't validate the volume because it might have
  // been unmounted.
  if (IsAddWatch()) {
    VolumeManager* const volume_manager = VolumeManager::Get(profile);
    if (!volume_manager) {
      return RespondNow(Error("Cannot find VolumeManager"));
    }

    const base::WeakPtr<Volume> volume =
        volume_manager->FindVolumeFromPath(file_system_url.path());
    if (!volume) {
      return RespondNow(
          Error("Cannot find volume *", Redact(file_system_url.path())));
    }

    if (!volume->watchable()) {
      return RespondNow(Error("Volume is not watchable"));
    }
  }

  file_manager::EventRouter* const event_router =
      file_manager::EventRouterFactory::GetForProfile(profile);

  content::GetIOThreadTaskRunner({})->PostTask(
      FROM_HERE, base::BindOnce(&FileWatchFunctionBase::RunAsyncOnIOThread,
                                this, file_system_context, file_system_url,
                                event_router->GetWeakPtr()));
  return RespondLater();
}

void FileWatchFunctionBase::RunAsyncOnIOThread(
    scoped_refptr<storage::FileSystemContext> file_system_context,
    const storage::FileSystemURL& file_system_url,
    base::WeakPtr<file_manager::EventRouter> event_router) {
  DCHECK_CURRENTLY_ON(BrowserThread::IO);

  storage::WatcherManager* const watcher_manager =
      file_system_context->GetWatcherManager(file_system_url.type());

  if (!watcher_manager) {
    content::GetUIThreadTaskRunner({})->PostTask(
        FROM_HERE,
        base::BindOnce(
            &FileWatchFunctionBase::PerformFallbackFileWatchOperationOnUIThread,
            this, file_system_url, event_router));
    return;
  }

  PerformFileWatchOperationOnIOThread(file_system_context, watcher_manager,
                                      file_system_url, event_router);
}

void FileManagerPrivateInternalAddFileWatchFunction::
    PerformFileWatchOperationOnIOThread(
        scoped_refptr<storage::FileSystemContext> file_system_context,
        storage::WatcherManager* watcher_manager,
        const storage::FileSystemURL& file_system_url,
        base::WeakPtr<file_manager::EventRouter> event_router) {
  DCHECK_CURRENTLY_ON(BrowserThread::IO);

  watcher_manager->AddWatcher(
      file_system_url, false /* recursive */,
      base::BindOnce(
          &StatusCallbackToResponseCallback,
          base::BindOnce(
              &PostResponseCallbackTaskToUIThread,
              base::BindOnce(&FileWatchFunctionBase::RespondWith, this))),
      base::BindRepeating(
          &PostNotificationCallbackTaskToUIThread,
          base::BindRepeating(
              &file_manager::EventRouter::OnWatcherManagerNotification,
              event_router, file_system_url,
              url::Origin::Create(source_url()))));
}

void FileManagerPrivateInternalAddFileWatchFunction::
    PerformFallbackFileWatchOperationOnUIThread(
        const storage::FileSystemURL& file_system_url,
        base::WeakPtr<file_manager::EventRouter> event_router) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  DCHECK(event_router);

  // Obsolete. Fallback code if storage::WatcherManager is not implemented.
  event_router->AddFileWatch(
      file_system_url.path(), file_system_url.virtual_path(),
      url::Origin::Create(source_url()),
      base::BindOnce(&FileWatchFunctionBase::RespondWith, this));
}

void FileManagerPrivateInternalRemoveFileWatchFunction::
    PerformFileWatchOperationOnIOThread(
        scoped_refptr<storage::FileSystemContext> file_system_context,
        storage::WatcherManager* watcher_manager,
        const storage::FileSystemURL& file_system_url,
        base::WeakPtr<file_manager::EventRouter> event_router) {
  DCHECK_CURRENTLY_ON(BrowserThread::IO);

  watcher_manager->RemoveWatcher(
      file_system_url, false /* recursive */,
      base::BindOnce(
          &StatusCallbackToResponseCallback,
          base::BindOnce(
              &PostResponseCallbackTaskToUIThread,
              base::BindOnce(&FileWatchFunctionBase::RespondWith, this))));
}

void FileManagerPrivateInternalRemoveFileWatchFunction::
    PerformFallbackFileWatchOperationOnUIThread(
        const storage::FileSystemURL& file_system_url,
        base::WeakPtr<file_manager::EventRouter> event_router) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  DCHECK(event_router);

  // Obsolete. Fallback code if storage::WatcherManager is not implemented.
  event_router->RemoveFileWatch(file_system_url.path(),
                                url::Origin::Create(source_url()));
  RespondWith(true);
}

bool FileManagerPrivateInternalAddFileWatchFunction::IsAddWatch() {
  return true;
}

bool FileManagerPrivateInternalRemoveFileWatchFunction::IsAddWatch() {
  return false;
}

ExtensionFunction::ResponseAction
FileManagerPrivateGetSizeStatsFunction::Run() {
  using extensions::api::file_manager_private::GetSizeStats::Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  VolumeManager* const volume_manager =
      VolumeManager::Get(Profile::FromBrowserContext(browser_context()));
  if (!volume_manager) {
    return RespondNow(Error("Cannot find VolumeManager"));
  }

  base::WeakPtr<Volume> volume =
      volume_manager->FindVolumeById(params->volume_id);
  if (!volume.get()) {
    return RespondNow(Error("Cannot find volume with ID *", params->volume_id));
  }

  // For fusebox volumes, get the underlying (aka regular) volume.
  const auto fusebox = std::string_view(file_manager::util::kFuseBox);
  if (base::StartsWith(volume->file_system_type(), fusebox)) {
    std::string volume_id = params->volume_id;

    if ((volume->type() == file_manager::VOLUME_TYPE_MTP) ||
        (volume->type() == file_manager::VOLUME_TYPE_DOCUMENTS_PROVIDER)) {
      volume_id = volume_id.substr(fusebox.length());
    } else if (volume->type() == file_manager::VOLUME_TYPE_PROVIDED) {
      // NB: FileManagerPrivate.GetSizeStats is not called by files app JS
      // because regular PROVIDED volumes do not support size stats.
      volume_manager->ConvertFuseBoxFSPVolumeIdToFSPIfNeeded(&volume_id);
    }

    volume = volume_manager->FindVolumeById(volume_id);
    if (!volume.get()) {
      return RespondNow(Error("Cannot find volume with ID *", volume_id));
    }
  }

  if (volume->type() == file_manager::VOLUME_TYPE_MTP) {
    // Resolve storage_name.
    storage_monitor::StorageMonitor* storage_monitor =
        storage_monitor::StorageMonitor::GetInstance();
    storage_monitor::StorageInfo info;
    storage_monitor->GetStorageInfoForPath(volume->mount_path(), &info);
    std::string storage_name;
    base::RemoveChars(info.location(), kRootPath, &storage_name);
    DCHECK(!storage_name.empty());

    // Get MTP StorageInfo.
    auto* manager = storage_monitor->media_transfer_protocol_manager();
    manager->GetStorageInfoFromDevice(
        storage_name,
        base::BindOnce(
            &FileManagerPrivateGetSizeStatsFunction::OnGetMtpAvailableSpace,
            this));
  } else if (volume->type() == file_manager::VOLUME_TYPE_DOCUMENTS_PROVIDER) {
    std::string authority;
    std::string root_id;
    if (!arc::ParseDocumentsProviderPath(volume->mount_path(), &authority,
                                         &root_id)) {
      return RespondNow(Error("File path was invalid"));
    }

    // Get DocumentsProvider's root available and capacity sizes in bytes.
    auto* root_map = arc::ArcDocumentsProviderRootMap::GetForBrowserContext(
        browser_context());
    if (!root_map) {
      return RespondNow(Error("File not found"));
    }
    auto* root = root_map->Lookup(authority, root_id);
    if (!root) {
      return RespondNow(Error("File not found"));
    }
    root->GetRootSize(base::BindOnce(&FileManagerPrivateGetSizeStatsFunction::
                                         OnGetDocumentsProviderAvailableSpace,
                                     this));
  } else if (volume->type() == file_manager::VOLUME_TYPE_GOOGLE_DRIVE) {
    Profile* const profile = Profile::FromBrowserContext(browser_context());
    drive::DriveIntegrationService* integration_service =
        drive::util::GetIntegrationServiceByProfile(profile);
    if (!integration_service) {
      return RespondNow(Error("Drive not available"));
    }
    integration_service->GetQuotaUsage(base::BindOnce(
        &FileManagerPrivateGetSizeStatsFunction::OnGetDriveQuotaUsage, this));
  } else {
    uint64_t* total_size = new uint64_t(0);
    uint64_t* remaining_size = new uint64_t(0);
    base::ThreadPool::PostTaskAndReply(
        FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
        base::BindOnce(&GetSizeStatsAsync, volume->mount_path(), total_size,
                       remaining_size),
        base::BindOnce(&FileManagerPrivateGetSizeStatsFunction::OnGetSizeStats,
                       this, base::Owned(total_size),
                       base::Owned(remaining_size)));
  }
  return RespondLater();
}

void FileManagerPrivateGetSizeStatsFunction::OnGetMtpAvailableSpace(
    device::mojom::MtpStorageInfoPtr mtp_storage_info,
    const bool error) {
  if (error) {
    // If stats couldn't be gotten from MTP volume, result should be left
    // undefined same as we do for Drive.
    Respond(NoArguments());
    return;
  }

  const uint64_t max_capacity = mtp_storage_info->max_capacity;
  const uint64_t free_space_in_bytes = mtp_storage_info->free_space_in_bytes;
  OnGetSizeStats(&max_capacity, &free_space_in_bytes);
}

void FileManagerPrivateGetSizeStatsFunction::
    OnGetDocumentsProviderAvailableSpace(const bool error,
                                         const uint64_t available_bytes,
                                         const uint64_t capacity_bytes) {
  if (error) {
    // If stats was not successfully retrieved from DocumentsProvider volume,
    // result should be left undefined same as we do for Drive.
    Respond(NoArguments());
    return;
  }
  OnGetSizeStats(&capacity_bytes, &available_bytes);
}

void FileManagerPrivateGetSizeStatsFunction::OnGetDriveQuotaUsage(
    drive::FileError error,
    drivefs::mojom::QuotaUsagePtr usage) {
  if (error != drive::FileError::FILE_ERROR_OK) {
    Respond(NoArguments());
    return;
  }
  OnGetSizeStats(&usage->total_cloud_bytes, &usage->free_cloud_bytes);
}

void FileManagerPrivateGetSizeStatsFunction::OnGetSizeStats(
    const uint64_t* total_size,
    const uint64_t* remaining_size) {
  base::Value::Dict sizes;
  sizes.Set("totalSize", static_cast<double>(*total_size));
  sizes.Set("remainingSize", static_cast<double>(*remaining_size));
  Respond(WithArguments(std::move(sizes)));
}

ExtensionFunction::ResponseAction
FileManagerPrivateInternalGetDriveQuotaMetadataFunction::Run() {
  using extensions::api::file_manager_private_internal::GetDriveQuotaMetadata::
      Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  Profile* const profile = Profile::FromBrowserContext(browser_context());
  scoped_refptr<storage::FileSystemContext> file_system_context =
      file_manager::util::GetFileSystemContextForRenderFrameHost(
          profile, render_frame_host());
  const GURL url = GURL(params->url);
  file_system_url_ = file_system_context->CrackURLInFirstPartyContext(url);

  drive::DriveIntegrationService* integration_service =
      drive::util::GetIntegrationServiceByProfile(profile);
  if (!integration_service) {
    return RespondNow(Error("Drive not available"));
  }
  integration_service->GetPooledQuotaUsage(
      base::BindOnce(&FileManagerPrivateInternalGetDriveQuotaMetadataFunction::
                         OnGetPooledQuotaUsage,
                     this));

  return RespondLater();
}

void FileManagerPrivateInternalGetDriveQuotaMetadataFunction::
    OnGetPooledQuotaUsage(drive::FileError error,
                          drivefs::mojom::PooledQuotaUsagePtr usage) {
  if (error != drive::FileError::FILE_ERROR_OK) {
    Respond(NoArguments());
    return;
  }

  Profile* const profile = Profile::FromBrowserContext(browser_context());
  drive::DriveIntegrationService* integration_service =
      drive::util::GetIntegrationServiceByProfile(profile);
  if (!integration_service) {
    return Respond(Error("Drive not available"));
  }

  quotaMetadata_.user_type =
      usage->user_type == drivefs::mojom::UserType::kUnmanaged
          ? api::file_manager_private::UserType::kUnmanaged
          : api::file_manager_private::UserType::kOrganization;
  quotaMetadata_.used_bytes = static_cast<double>(usage->used_user_bytes);
  quotaMetadata_.total_bytes = static_cast<double>(usage->total_user_bytes);
  quotaMetadata_.organization_limit_exceeded =
      usage->organization_limit_exceeded;
  quotaMetadata_.organization_name = usage->organization_name;

  if (integration_service->IsSharedDrive(file_system_url_.path())) {
    // Init quota to unlimited if no quota set.
    quotaMetadata_.total_bytes = -1;
    quotaMetadata_.used_bytes = 0;
    integration_service->GetMetadata(
        file_system_url_.path(),
        base::BindOnce(
            &FileManagerPrivateInternalGetDriveQuotaMetadataFunction::
                OnGetMetadata,
            this));
    return;
  }

  Respond(
      ArgumentList(api::file_manager_private_internal::GetDriveQuotaMetadata::
                       Results::Create(quotaMetadata_)));
}

void FileManagerPrivateInternalGetDriveQuotaMetadataFunction::OnGetMetadata(
    drive::FileError error,
    drivefs::mojom::FileMetadataPtr metadata) {
  if (error == drive::FileError::FILE_ERROR_OK &&
      metadata->shared_drive_quota) {
    quotaMetadata_.used_bytes =
        metadata->shared_drive_quota->quota_bytes_used_in_drive;
    quotaMetadata_.total_bytes =
        metadata->shared_drive_quota->individual_quota_bytes_total;
  }

  Respond(
      ArgumentList(api::file_manager_private_internal::GetDriveQuotaMetadata::
                       Results::Create(quotaMetadata_)));
}

ExtensionFunction::ResponseAction
FileManagerPrivateInternalValidatePathNameLengthFunction::Run() {
  using extensions::api::file_manager_private_internal::ValidatePathNameLength::
      Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  scoped_refptr<storage::FileSystemContext> file_system_context =
      file_manager::util::GetFileSystemContextForRenderFrameHost(
          Profile::FromBrowserContext(browser_context()), render_frame_host());

  const storage::FileSystemURL file_system_url(
      file_system_context->CrackURLInFirstPartyContext(
          GURL(params->parent_url)));
  if (!ash::FileSystemBackend::CanHandleURL(file_system_url)) {
    return RespondNow(Error("Invalid URL"));
  }

  base::ThreadPool::PostTaskAndReplyWithResult(
      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
      base::BindOnce(&GetFileNameMaxLengthAsync,
                     file_system_url.path().AsUTF8Unsafe()),
      base::BindOnce(&FileManagerPrivateInternalValidatePathNameLengthFunction::
                         OnFilePathLimitRetrieved,
                     this, params->name.size()));
  return RespondLater();
}

void FileManagerPrivateInternalValidatePathNameLengthFunction::
    OnFilePathLimitRetrieved(size_t current_length, size_t max_length) {
  Respond(WithArguments(current_length <= max_length));
}

ExtensionFunction::ResponseAction
FileManagerPrivateFormatVolumeFunction::Run() {
  using extensions::api::file_manager_private::FormatVolume::Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  VolumeManager* const volume_manager =
      VolumeManager::Get(Profile::FromBrowserContext(browser_context()));
  if (!volume_manager) {
    return RespondNow(Error("Cannot find VolumeManager"));
  }

  const base::WeakPtr<Volume> volume =
      volume_manager->FindVolumeById(params->volume_id);
  if (!volume) {
    return RespondNow(Error("Cannot find volume with ID *", params->volume_id));
  }

  DiskMountManager::GetInstance()->FormatMountedDevice(
      volume->mount_path().AsUTF8Unsafe(),
      ApiFormatFileSystemToChromeEnum(params->filesystem),
      params->volume_label);
  return RespondNow(NoArguments());
}

ExtensionFunction::ResponseAction
FileManagerPrivateSinglePartitionFormatFunction::Run() {
  using extensions::api::file_manager_private::SinglePartitionFormat::Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  const DiskMountManager::Disks& disks =
      DiskMountManager::GetInstance()->disks();

  DiskMountManager::Disks::const_iterator it = disks.begin();
  for (; it != disks.end(); ++it) {
    if (it->get()->storage_device_path() == params->device_storage_path &&
        it->get()->is_parent()) {
      break;
    }
  }

  if (it == disks.end()) {
    return RespondNow(Error("Device not found"));
  }

  const ash::disks::Disk* const device_disk = it->get();
  DCHECK(device_disk);

  if (device_disk->is_read_only()) {
    return RespondNow(Error("Invalid device"));
  }

  DiskMountManager::GetInstance()->SinglePartitionFormatDevice(
      device_disk->device_path(),
      ApiFormatFileSystemToChromeEnum(params->filesystem),
      params->volume_label);
  return RespondNow(NoArguments());
}

ExtensionFunction::ResponseAction
FileManagerPrivateRenameVolumeFunction::Run() {
  using extensions::api::file_manager_private::RenameVolume::Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  VolumeManager* const volume_manager =
      VolumeManager::Get(Profile::FromBrowserContext(browser_context()));
  if (!volume_manager) {
    return RespondNow(Error("Cannot find VolumeManager"));
  }

  const base::WeakPtr<Volume> volume =
      volume_manager->FindVolumeById(params->volume_id);
  if (!volume) {
    return RespondNow(Error("Cannot find volume with ID *", params->volume_id));
  }

  DiskMountManager::GetInstance()->RenameMountedDevice(
      volume->mount_path().AsUTF8Unsafe(), params->new_name);
  return RespondNow(NoArguments());
}

FileManagerPrivateInternalGetDisallowedTransfersFunction::
    FileManagerPrivateInternalGetDisallowedTransfersFunction() = default;

FileManagerPrivateInternalGetDisallowedTransfersFunction::
    ~FileManagerPrivateInternalGetDisallowedTransfersFunction() = default;

ExtensionFunction::ResponseAction
FileManagerPrivateInternalGetDisallowedTransfersFunction::Run() {
  if (!base::FeatureList::IsEnabled(
          features::kDataLeakPreventionFilesRestriction)) {
    return RespondNow(WithArguments(base::Value::List()));
  }

  policy::DlpRulesManager* rules_manager =
      policy::DlpRulesManagerFactory::GetForPrimaryProfile();
  if (!rules_manager || !rules_manager->IsFilesPolicyEnabled() ||
      !rules_manager->GetDlpFilesController()) {
    return RespondNow(WithArguments(base::Value::List()));
  }

  using extensions::api::file_manager_private_internal::GetDisallowedTransfers::
      Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  profile_ = Profile::FromBrowserContext(browser_context());
  scoped_refptr<storage::FileSystemContext> file_system_context =
      file_manager::util::GetFileSystemContextForRenderFrameHost(
          profile_, render_frame_host());

  for (const std::string& url : params->entries) {
    FileSystemURL file_system_url(
        file_system_context->CrackURLInFirstPartyContext(GURL(url)));
    if (!file_system_url.is_valid()) {
      return RespondNow(Error("File URL was invalid"));
    }
    source_urls_.push_back(file_system_url);
  }

  destination_url_ = file_system_context->CrackURLInFirstPartyContext(
      GURL(params->destination_entry));
  if (!destination_url_.is_valid()) {
    return RespondNow(Error("File URL was invalid"));
  }

  // If the new UX flow is enabled, return an empty list so the copy/move
  // operation can start.
  if (base::FeatureList::IsEnabled(features::kNewFilesPolicyUX)) {
    return RespondNow(WithArguments(base::Value::List()));
  }

  policy::DlpFilesControllerAsh* files_controller =
      static_cast<policy::DlpFilesControllerAsh*>(
          rules_manager->GetDlpFilesController());
  files_controller->CheckIfTransferAllowed(
      /*task_id=*/std::nullopt, source_urls_, destination_url_, params->is_move,
      base::BindOnce(&FileManagerPrivateInternalGetDisallowedTransfersFunction::
                         OnGetDisallowedFiles,
                     this));

  return RespondLater();
}

void FileManagerPrivateInternalGetDisallowedTransfersFunction::
    OnGetDisallowedFiles(std::vector<storage::FileSystemURL> disallowed_files) {
  file_manager::util::FileDefinitionList file_definition_list;
  for (const auto& file : disallowed_files) {
    file_manager::util::FileDefinition file_definition;
    // Disallowed transfers lists regular files not directories.
    file_definition.is_directory = false;
    file_definition.virtual_path = file.virtual_path();
    file_definition.absolute_path = file.path();
    file_definition_list.emplace_back(std::move(file_definition));
  }

  file_manager::util::ConvertFileDefinitionListToEntryDefinitionList(
      file_manager::util::GetFileSystemContextForSourceURL(profile_,
                                                           source_url()),
      url::Origin::Create(source_url().DeprecatedGetOriginAsURL()),
      file_definition_list,  // Safe, since copied internally.
      base::BindOnce(&FileManagerPrivateInternalGetDisallowedTransfersFunction::
                         OnConvertFileDefinitionListToEntryDefinitionList,
                     this));
}

void FileManagerPrivateInternalGetDisallowedTransfersFunction::
    OnConvertFileDefinitionListToEntryDefinitionList(
        std::unique_ptr<file_manager::util::EntryDefinitionList>
            entry_definition_list) {
  DCHECK(entry_definition_list);

  Respond(
      WithArguments(file_manager::util::ConvertEntryDefinitionListToListValue(
          *entry_definition_list)));
}

FileManagerPrivateInternalGetDlpMetadataFunction::
    FileManagerPrivateInternalGetDlpMetadataFunction() = default;

FileManagerPrivateInternalGetDlpMetadataFunction::
    ~FileManagerPrivateInternalGetDlpMetadataFunction() = default;

ExtensionFunction::ResponseAction
FileManagerPrivateInternalGetDlpMetadataFunction::Run() {
  if (!base::FeatureList::IsEnabled(
          features::kDataLeakPreventionFilesRestriction)) {
    return RespondNow(WithArguments(base::Value::List()));
  }

  policy::DlpRulesManager* rules_manager =
      policy::DlpRulesManagerFactory::GetForPrimaryProfile();
  if (!rules_manager || !rules_manager->IsFilesPolicyEnabled() ||
      !rules_manager->GetDlpFilesController()) {
    return RespondNow(WithArguments(base::Value::List()));
  }

  using extensions::api::file_manager_private_internal::GetDlpMetadata::Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  scoped_refptr<storage::FileSystemContext> file_system_context =
      file_manager::util::GetFileSystemContextForRenderFrameHost(
          Profile::FromBrowserContext(browser_context()), render_frame_host());

  for (const std::string& url : params->entries) {
    FileSystemURL file_system_url(
        file_system_context->CrackURLInFirstPartyContext(GURL(url)));
    if (!file_system_url.is_valid()) {
      return RespondNow(Error("File URL was invalid"));
    }
    source_urls_.push_back(file_system_url);
  }

  policy::DlpFilesControllerAsh* files_controller =
      static_cast<policy::DlpFilesControllerAsh*>(
          rules_manager->GetDlpFilesController());

  std::optional<policy::DlpFileDestination> destination;
  content::WebContents* web_contents = GetSenderWebContents();
  if (!web_contents) {
    LOG(WARNING) << "Failed to locate WebContents";
    return RespondNow(Error("Failed to locate WebContents"));
  }
  ui::SelectFileDialog::Type type =
      SelectFileDialogExtensionUserData::GetDialogTypeForWebContents(
          web_contents);
  if (type != ui::SelectFileDialog::Type::SELECT_SAVEAS_FILE) {
    destination =
        SelectFileDialogExtensionUserData::GetDialogCallerForWebContents(
            web_contents);
  }

  files_controller->GetDlpMetadata(
      source_urls_, destination,
      base::BindOnce(
          &FileManagerPrivateInternalGetDlpMetadataFunction::OnGetDlpMetadata,
          this));

  return RespondLater();
}

void FileManagerPrivateInternalGetDlpMetadataFunction::OnGetDlpMetadata(
    std::vector<policy::DlpFilesControllerAsh::DlpFileMetadata>
        dlp_metadata_list) {
  using extensions::api::file_manager_private::DlpMetadata;
  std::vector<DlpMetadata> converted_list;
  for (const auto& md : dlp_metadata_list) {
    DlpMetadata metadata;
    metadata.is_dlp_restricted = md.is_dlp_restricted;
    metadata.source_url = md.source_url;
    metadata.is_restricted_for_destination = md.is_restricted_for_destination;
    converted_list.emplace_back(std::move(metadata));
  }
  Respond(ArgumentList(
      api::file_manager_private_internal::GetDlpMetadata::Results::Create(
          converted_list)));
}

FileManagerPrivateGetDlpRestrictionDetailsFunction::
    FileManagerPrivateGetDlpRestrictionDetailsFunction() = default;

FileManagerPrivateGetDlpRestrictionDetailsFunction::
    ~FileManagerPrivateGetDlpRestrictionDetailsFunction() = default;

ExtensionFunction::ResponseAction
FileManagerPrivateGetDlpRestrictionDetailsFunction::Run() {
  if (!base::FeatureList::IsEnabled(
          features::kDataLeakPreventionFilesRestriction)) {
    return RespondNow(WithArguments(base::Value::List()));
  }

  policy::DlpRulesManager* rules_manager =
      policy::DlpRulesManagerFactory::GetForPrimaryProfile();
  if (!rules_manager || !rules_manager->IsFilesPolicyEnabled() ||
      !rules_manager->GetDlpFilesController()) {
    return RespondNow(WithArguments(base::Value::List()));
  }

  using extensions::api::file_manager_private::GetDlpRestrictionDetails::Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  policy::DlpFilesControllerAsh* files_controller =
      static_cast<policy::DlpFilesControllerAsh*>(
          rules_manager->GetDlpFilesController());
  const std::vector<policy::DlpFilesControllerAsh::DlpFileRestrictionDetails>
      dlp_restriction_details =
          files_controller->GetDlpRestrictionDetails(params->source_url);

  using extensions::api::file_manager_private::DlpRestrictionDetails;
  std::vector<DlpRestrictionDetails> converted_list;

  for (const auto& [level, urls, components] : dlp_restriction_details) {
    DlpRestrictionDetails details;
    details.level = DlpRulesManagerLevelToApiEnum(level);
    base::ranges::move(urls.begin(), urls.end(),
                       std::back_inserter(details.urls));
    for (const auto& component : components) {
      details.components.push_back(
          DlpRulesManagerComponentToApiEnum(component));
    }
    converted_list.emplace_back(std::move(details));
  }

  return RespondNow(ArgumentList(
      api::file_manager_private::GetDlpRestrictionDetails::Results::Create(
          converted_list)));
}

FileManagerPrivateGetDlpBlockedComponentsFunction::
    FileManagerPrivateGetDlpBlockedComponentsFunction() = default;

FileManagerPrivateGetDlpBlockedComponentsFunction::
    ~FileManagerPrivateGetDlpBlockedComponentsFunction() = default;

ExtensionFunction::ResponseAction
FileManagerPrivateGetDlpBlockedComponentsFunction::Run() {
  if (!base::FeatureList::IsEnabled(
          features::kDataLeakPreventionFilesRestriction)) {
    return RespondNow(WithArguments(base::Value::List()));
  }

  policy::DlpRulesManager* rules_manager =
      policy::DlpRulesManagerFactory::GetForPrimaryProfile();
  policy::DlpFilesControllerAsh* files_controller;
  if (!rules_manager || !rules_manager->IsFilesPolicyEnabled() ||
      !(files_controller = static_cast<policy::DlpFilesControllerAsh*>(
            rules_manager->GetDlpFilesController()))) {
    return RespondNow(WithArguments(base::Value::List()));
  }

  using extensions::api::file_manager_private::GetDlpBlockedComponents::Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  const std::vector<data_controls::Component> components =
      files_controller->GetBlockedComponents(params->source_url);

  using extensions::api::file_manager_private::VolumeType;
  std::vector<VolumeType> converted_list;

  for (const auto& component : components) {
    converted_list.emplace_back(DlpRulesManagerComponentToApiEnum(component));
  }

  return RespondNow(ArgumentList(
      api::file_manager_private::GetDlpBlockedComponents::Results::Create(
          converted_list)));
}

ExtensionFunction::ResponseAction
FileManagerPrivateGetDialogCallerFunction::Run() {
  std::optional<policy::DlpFileDestination> caller =
      SelectFileDialogExtensionUserData::GetDialogCallerForWebContents(
          GetSenderWebContents());
  base::Value::Dict info;
  if (caller.has_value()) {
    if (caller->url().has_value()) {
      info.Set("url", caller->url()->spec());
    }
    if (caller->component().has_value()) {
      info.Set("component",
               base::to_underlying(DlpRulesManagerComponentToApiEnum(
                   caller->component().value())));
    }
  }

  return RespondNow(WithArguments(std::move(info)));
}

ExtensionFunction::ResponseAction
FileManagerPrivateInternalResolveIsolatedEntriesFunction::Run() {
  using extensions::api::file_manager_private_internal::ResolveIsolatedEntries::
      Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  Profile* profile = Profile::FromBrowserContext(browser_context());
  scoped_refptr<storage::FileSystemContext> file_system_context =
      file_manager::util::GetFileSystemContextForRenderFrameHost(
          profile, render_frame_host());
  DCHECK(file_system_context.get());

  const auto* external_backend =
      ash::FileSystemBackend::Get(*file_system_context);
  DCHECK(external_backend);

  file_manager::util::FileDefinitionList file_definition_list;
  for (const auto& url : params->urls) {
    const FileSystemURL file_system_url =
        file_system_context->CrackURLInFirstPartyContext(GURL(url));
    DCHECK(external_backend->CanHandleType(file_system_url.type()))
        << "GURL: " << file_system_url.ToGURL()
        << "type: " << file_system_url.type();
    FileDefinition file_definition;
    const bool result =
        file_manager::util::ConvertAbsoluteFilePathToRelativeFileSystemPath(
            profile, source_url(), file_system_url.path(),
            &file_definition.virtual_path);
    if (!result) {
      LOG(WARNING) << "Failed to convert file_system_url to relative file "
                      "system path, type: "
                   << file_system_url.type();
      continue;
    }
    // The API only supports isolated files. It still works for directories,
    // as the value is ignored for existing entries.
    file_definition.is_directory = false;
    file_definition_list.push_back(file_definition);
  }

  file_manager::util::ConvertFileDefinitionListToEntryDefinitionList(
      file_manager::util::GetFileSystemContextForSourceURL(profile,
                                                           source_url()),
      url::Origin::Create(source_url()),
      file_definition_list,  // Safe, since copied internally.
      base::BindOnce(
          &FileManagerPrivateInternalResolveIsolatedEntriesFunction::
              RunAsyncAfterConvertFileDefinitionListToEntryDefinitionList,
          this));
  return RespondLater();
}

void FileManagerPrivateInternalResolveIsolatedEntriesFunction::
    RunAsyncAfterConvertFileDefinitionListToEntryDefinitionList(
        std::unique_ptr<file_manager::util::EntryDefinitionList>
            entry_definition_list) {
  using extensions::api::file_manager_private_internal::EntryDescription;
  std::vector<EntryDescription> entries;

  for (const auto& definition : *entry_definition_list) {
    if (definition.error != base::File::FILE_OK) {
      LOG(WARNING) << "Error resolving file system URL: " << definition.error;
      continue;
    }
    EntryDescription entry;
    entry.file_system_name = definition.file_system_name;
    entry.file_system_root = definition.file_system_root_url;
    entry.file_full_path = "/" + definition.full_path.AsUTF8Unsafe();
    entry.file_is_directory = definition.is_directory;
    entries.push_back(std::move(entry));
  }

  Respond(ArgumentList(extensions::api::file_manager_private_internal::
                           ResolveIsolatedEntries::Results::Create(entries)));
}

FileManagerPrivateInternalSearchFilesFunction::
    FileManagerPrivateInternalSearchFilesFunction() = default;

ExtensionFunction::ResponseAction
FileManagerPrivateInternalSearchFilesFunction::Run() {
  using api::file_manager_private_internal::SearchFiles::Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);
  const auto& search_params = params->search_params;

  if (search_params.max_results < 0) {
    return RespondNow(Error("maxResults must be non-negative"));
  }

  ash::RecentSource::FileType file_type;
  if (!file_manager::util::ToRecentSourceFileType(search_params.category,
                                                  &file_type)) {
    return RespondNow(
        Error("Cannot convert category * to file type",
              api::file_manager_private::ToString(search_params.category)));
  }

  base::FilePath root_path;
  Profile* profile = Profile::FromBrowserContext(browser_context());
  const std::string root_url = search_params.root_url.value_or("");
  if (root_url.empty()) {
    root_path = file_manager::util::GetMyFilesFolderForProfile(profile);
  } else {
    const scoped_refptr<storage::FileSystemContext> file_system_context =
        file_manager::util::GetFileSystemContextForRenderFrameHost(
            profile, render_frame_host());
    const storage::FileSystemURL url =
        file_system_context->CrackURLInFirstPartyContext(GURL(root_url));
    root_path = url.path();
  }

  size_t max_results =
      base::internal::checked_cast<size_t>(search_params.max_results);
  base::Time modified_time = base::Time::FromMillisecondsSinceUnixEpoch(
      search_params.modified_timestamp);

  // Barrier that collects results from the file search by name and image
  // search by (query) terms. Explicitly waits for 2 tasks to complete.
  auto barrier_callback = base::BarrierCallback<FileSearchResults>(
      2,
      base::BindOnce(
          &FileManagerPrivateInternalSearchFilesFunction::OnSearchByPatternDone,
          this));

  RunFileSearchByName(profile, root_path, search_params.query, modified_time,
                      file_type, max_results, barrier_callback);
  RunImageSearchByQuery(root_path, search_params.query, modified_time,
                        max_results, barrier_callback);

  return RespondLater();
}

void FileManagerPrivateInternalSearchFilesFunction::RunFileSearchByName(
    Profile* profile,
    base::FilePath root_path,
    const std::string& query,
    base::Time modified_time,
    ash::RecentSource::FileType file_type,
    size_t max_results,
    OnResultsReadyCallback callback) {
  // If trash is enabled for the given profile and by local user files policy,
  // generate all trash paths that are to be excluded when searching for
  // matching files.
  std::vector<base::FilePath> excluded_paths;
  if (file_manager::trash::IsTrashEnabledForProfile(profile)) {
    auto enabled_trash_locations =
        file_manager::trash::GenerateEnabledTrashLocationsForProfile(
            profile, /*base_path=*/base::FilePath());
    for (const auto& it : enabled_trash_locations) {
      excluded_paths.emplace_back(
          it.first.Append(it.second.relative_folder_path));
    }
  }
  base::ThreadPool::PostTaskAndReplyWithResult(
      FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
      base::BindOnce(&SearchByPattern, root_path, excluded_paths, query,
                     modified_time, file_type, max_results),
      std::move(callback));
}

void FileManagerPrivateInternalSearchFilesFunction::RunImageSearchByQuery(
    base::FilePath root_path,
    const std::string& query,
    base::Time modified_time,
    size_t max_results,
    OnResultsReadyCallback callback) {
  // If the feature is not enabled or the query is too short return empty match.
  std::u16string q16 = base::UTF8ToUTF16(query);
  if (!ash::features::IsFilesLocalImageSearchEnabled() ||
      !search_features::IsLauncherImageSearchEnabled() ||
      app_list::IsQueryTooShort(q16)) {
    std::move(callback).Run({});
    return;
  }

  app_list::LocalImageSearchServiceFactory::GetForBrowserContext(
      browser_context())
      ->Search(q16, max_results,
               base::BindOnce(&OnImageSearchDone, root_path, modified_time,
                              std::move(callback)));
}

void FileManagerPrivateInternalSearchFilesFunction::OnSearchByPatternDone(
    std::vector<FileSearchResults> all_results) {
  // Remove duplicates as image search and name search do not interact with each
  // other.
  FileSearchResults unique_results;
  std::set<base::FilePath> found;
  for (const auto& results : all_results) {
    for (const auto& [file_path, is_directory] : results) {
      if (base::Contains(found, file_path)) {
        continue;
      }
      found.insert(file_path);
      unique_results.emplace_back(file_path, is_directory);
    }
  }

  base::Value::List entries;
  for (const auto& result : unique_results) {
    std::string mount_name;
    std::string file_system_name;
    std::string full_path;
    if (!file_manager::util::ExtractMountNameFileSystemNameFullPath(
            result.first, &mount_name, &file_system_name, &full_path)) {
      DLOG(WARNING) << "Unable to extract details from "
                    << result.first.value();
      continue;
    }
    std::string fs_root =
        storage::GetExternalFileSystemRootURIString(source_url(), mount_name);

    base::Value::Dict entry;
    entry.Set("fileSystemName", file_system_name);
    entry.Set("fileSystemRoot", fs_root);
    entry.Set("fileFullPath", full_path);
    entry.Set("fileIsDirectory", result.second);
    entries.Append(std::move(entry));
  }

  Respond(WithArguments(std::move(entries)));
}

ExtensionFunction::ResponseAction
FileManagerPrivateInternalGetDirectorySizeFunction::Run() {
  using extensions::api::file_manager_private_internal::GetDirectorySize::
      Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  if (params->url.empty()) {
    return RespondNow(Error("File URL must be provided."));
  }

  Profile* const profile = Profile::FromBrowserContext(browser_context());
  scoped_refptr<storage::FileSystemContext> file_system_context =
      file_manager::util::GetFileSystemContextForRenderFrameHost(
          profile, render_frame_host());
  const storage::FileSystemURL file_system_url(
      file_system_context->CrackURLInFirstPartyContext(GURL(params->url)));
  if (!ash::FileSystemBackend::CanHandleURL(file_system_url)) {
    return RespondNow(
        Error("FileSystemBackend failed to handle the entry's url."));
  }
  if (file_system_url.type() != storage::kFileSystemTypeLocal &&
      file_system_url.type() != storage::kFileSystemTypeDriveFs) {
    return RespondNow(Error("Only local directories are supported."));
  }

  const base::FilePath root_path = file_manager::util::GetLocalPathFromURL(
      file_system_context, GURL(params->url));
  if (root_path.empty()) {
    return RespondNow(
        Error("Failed to get a local path from the entry's url."));
  }

  base::ThreadPool::PostTaskAndReplyWithResult(
      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
      base::BindOnce(&base::ComputeDirectorySize, root_path),
      base::BindOnce(&FileManagerPrivateInternalGetDirectorySizeFunction::
                         OnDirectorySizeRetrieved,
                     this));
  return RespondLater();
}

void FileManagerPrivateInternalGetDirectorySizeFunction::
    OnDirectorySizeRetrieved(int64_t size) {
  Respond(WithArguments(static_cast<double>(size)));
}

ExtensionFunction::ResponseAction
FileManagerPrivateInternalStartIOTaskFunction::Run() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  using extensions::api::file_manager_private_internal::StartIOTask::Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  auto* const profile = Profile::FromBrowserContext(browser_context());
  scoped_refptr<storage::FileSystemContext> file_system_context =
      file_manager::util::GetFileSystemContextForRenderFrameHost(
          profile, render_frame_host());

  VolumeManager* const volume_manager =
      VolumeManager::Get(Profile::FromBrowserContext(browser_context()));
  if (!volume_manager || !volume_manager->io_task_controller()) {
    return RespondNow(Error("Cannot find VolumeManager"));
  }

  storage::ExternalMountPoints* mount_points =
      storage::ExternalMountPoints::GetSystemInstance();

  std::vector<storage::FileSystemURL> source_urls;
  for (const std::string& url : params->urls) {
    GURL gurl(url);
    storage::FileSystemURL cracked_url =
        file_system_context->CrackURLInFirstPartyContext(gurl);
    if (!cracked_url.is_valid()) {
      return RespondNow(Error("Invalid source URL *", Redact(url)));
    }
    base::FilePath virtual_path;
    const bool result =
        file_manager::util::ConvertAbsoluteFilePathToRelativeFileSystemPath(
            profile, gurl, cracked_url.path(), &virtual_path);
    if (!result) {
      LOG(WARNING) << "Failed to convert file_system_url to relative file "
                      "system path, type: "
                   << cracked_url.type();
      continue;
    }
    cracked_url = mount_points->CreateCrackedFileSystemURL(
        cracked_url.storage_key(), storage::kFileSystemTypeExternal,
        virtual_path);
    source_urls.push_back(std::move(cracked_url));
  }

  auto type = IoTaskTypeToChromeEnum(params->type);
  if (!type) {
    return RespondNow(Error("Invalid I/O task type given."));
  }

  storage::FileSystemURL destination_folder_url;
  if (params->params.destination_folder_url) {
    destination_folder_url = file_system_context->CrackURLInFirstPartyContext(
        GURL(*(params->params.destination_folder_url)));
    if (!destination_folder_url.is_valid()) {
      return RespondNow(Error("Invalid destination folder url."));
    }
  }

  // Whether to display this IOTask notification, if undefined this should
  // default to true.
  bool show_notification = params->params.show_notification.value_or(true);

  // Check if Trash is enabled, this pref is mainly used by enterprise policy to
  // disable trash on a per profile basis.
  bool is_trash_enabled = false;
  if (profile && profile->GetPrefs()) {
    is_trash_enabled =
        profile->GetPrefs()->GetBoolean(ash::prefs::kFilesAppTrashEnabled);
  }

  std::unique_ptr<file_manager::io_task::IOTask> task;
  switch (type.value()) {
    case file_manager::io_task::OperationType::kCopy:
    case file_manager::io_task::OperationType::kMove:
      task = std::make_unique<file_manager::io_task::CopyOrMoveIOTask>(
          type.value(), std::move(source_urls),
          std::move(destination_folder_url), profile, file_system_context,
          show_notification);
      break;
    case file_manager::io_task::OperationType::kZip:
      task = std::make_unique<file_manager::io_task::ZipIOTask>(
          std::move(source_urls), std::move(destination_folder_url), profile,
          file_system_context, show_notification);
      break;
    case file_manager::io_task::OperationType::kDelete:
      task = std::make_unique<file_manager::io_task::DeleteIOTask>(
          std::move(source_urls), file_system_context, show_notification);
      break;
    case file_manager::io_task::OperationType::kEmptyTrash:
      if (is_trash_enabled) {
        task = std::make_unique<file_manager::io_task::EmptyTrashIOTask>(
            blink::StorageKey::CreateFirstParty(
                render_frame_host()->GetLastCommittedOrigin()),
            profile, file_system_context,
            /*base_path=*/base::FilePath(), show_notification);
      }
      break;
    case file_manager::io_task::OperationType::kRestore:
      if (is_trash_enabled) {
        task = std::make_unique<file_manager::io_task::RestoreIOTask>(
            std::move(source_urls), profile, file_system_context,
            /*base_path=*/base::FilePath(), show_notification);
      }
      break;
    case file_manager::io_task::OperationType::kRestoreToDestination:
      if (is_trash_enabled) {
        task =
            std::make_unique<file_manager::io_task::RestoreToDestinationIOTask>(
                std::move(source_urls), std::move(destination_folder_url),
                profile, file_system_context,
                /*base_path=*/base::FilePath(), show_notification);
      }
      break;
    case file_manager::io_task::OperationType::kTrash:
      if (is_trash_enabled) {
        task = std::make_unique<file_manager::io_task::TrashIOTask>(
            std::move(source_urls), profile, file_system_context,
            /*base_path=*/base::FilePath(), show_notification);
      }
      break;
    case file_manager::io_task::OperationType::kExtract:
      std::string password;
      if (params->params.password) {
        password = *params->params.password;
      }
      task = std::make_unique<file_manager::io_task::ExtractIOTask>(
          std::move(source_urls), std::move(password),
          std::move(destination_folder_url), profile, file_system_context,
          show_notification);
      break;
  }
  if (!task) {
    return RespondNow(Error("Invalid operation type: *",
                            api::file_manager_private::ToString(params->type)));
  }
  const auto taskId =
      volume_manager->io_task_controller()->Add(std::move(task));
  return RespondNow(WithArguments(static_cast<int>(taskId)));
}

ExtensionFunction::ResponseAction
FileManagerPrivateCancelIOTaskFunction::Run() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  using extensions::api::file_manager_private::CancelIOTask::Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  VolumeManager* const volume_manager =
      VolumeManager::Get(Profile::FromBrowserContext(browser_context()));
  if (!volume_manager || !volume_manager->io_task_controller()) {
    return RespondNow(Error("Cannot find VolumeManager"));
  }

  if (params->task_id <= 0) {
    return RespondNow(Error("Invalid task id"));
  }

  volume_manager->io_task_controller()->Cancel(params->task_id);
  return RespondNow(NoArguments());
}

ExtensionFunction::ResponseAction
FileManagerPrivateResumeIOTaskFunction::Run() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  using extensions::api::file_manager_private::ResumeIOTask::Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  VolumeManager* const volume_manager =
      VolumeManager::Get(Profile::FromBrowserContext(browser_context()));
  if (!volume_manager || !volume_manager->io_task_controller()) {
    return RespondNow(Error("Cannot find VolumeManager"));
  }

  if (params->task_id <= 0) {
    return RespondNow(Error("Invalid task id"));
  }

  file_manager::io_task::ResumeParams io_task_resume_params;
  if (params->params.conflict_params) {
    io_task_resume_params.conflict_params.emplace();
    io_task_resume_params.conflict_params->conflict_resolve =
        params->params.conflict_params->conflict_resolve.value_or("");
    io_task_resume_params.conflict_params->conflict_apply_to_all =
        params->params.conflict_params->conflict_apply_to_all.value_or(false);
  }
  if (params->params.policy_params) {
    std::optional<policy::Policy> policy =
        ApiPolicyErrorTypeToChromeEnum(params->params.policy_params->type);
    if (policy.has_value()) {
      io_task_resume_params.policy_params.emplace();
      io_task_resume_params.policy_params->type = policy.value();
    }
  }

  volume_manager->io_task_controller()->Resume(
      params->task_id, std::move(io_task_resume_params));

  return RespondNow(NoArguments());
}

ExtensionFunction::ResponseAction
FileManagerPrivateDismissIOTaskFunction::Run() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  using extensions::api::file_manager_private::DismissIOTask::Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  if (params->task_id <= 0) {
    return RespondNow(Error("Invalid task id"));
  }

  policy::FilesPolicyNotificationManager* manager =
      policy::FilesPolicyNotificationManagerFactory::GetForBrowserContext(
          browser_context());
  if (!manager) {
    LOG(ERROR) << "No FilesPolicyNotificationManager instantiated,"
                  "can't notify about task_id "
               << params->task_id;
    return RespondNow(NoArguments());
  }
  manager->OnErrorItemDismissed(params->task_id);

  return RespondNow(NoArguments());
}

ExtensionFunction::ResponseAction
FileManagerPrivateShowPolicyDialogFunction::Run() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  using extensions::api::file_manager_private::ShowPolicyDialog::Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  if (params->task_id <= 0) {
    return RespondNow(Error("Invalid task id"));
  }

  policy::FilesDialogType type = ApiPolicyDialogTypeToChromeEnum(params->type);
  if (type == policy::FilesDialogType::kUnknown) {
    return RespondNow(Error("No dialog type passed for task_id *",
                            base::NumberToString(params->task_id)));
  }

  policy::FilesPolicyNotificationManager* manager =
      policy::FilesPolicyNotificationManagerFactory::GetForBrowserContext(
          browser_context());
  if (!manager) {
    LOG(ERROR) << "No FilesPolicyNotificationManager instantiated,"
                  "can't show policy dialog for task_id "
               << params->task_id;
    return RespondNow(NoArguments());
  }
  manager->ShowDialog(params->task_id, type);

  return RespondNow(NoArguments());
}

ExtensionFunction::ResponseAction
FileManagerPrivateProgressPausedTasksFunction::Run() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  VolumeManager* const volume_manager =
      VolumeManager::Get(Profile::FromBrowserContext(browser_context()));
  if (!volume_manager || !volume_manager->io_task_controller()) {
    return RespondNow(Error("Cannot find VolumeManager"));
  }

  volume_manager->io_task_controller()->ProgressPausedTasks();

  return RespondNow(NoArguments());
}

FileManagerPrivateInternalParseTrashInfoFilesFunction::
    FileManagerPrivateInternalParseTrashInfoFilesFunction() = default;

FileManagerPrivateInternalParseTrashInfoFilesFunction::
    ~FileManagerPrivateInternalParseTrashInfoFilesFunction() = default;

ExtensionFunction::ResponseAction
FileManagerPrivateInternalParseTrashInfoFilesFunction::Run() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  using extensions::api::file_manager_private_internal::ParseTrashInfoFiles::
      Params;
  const std::optional<Params> params = Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  auto* const profile = Profile::FromBrowserContext(browser_context());
  file_system_context_ =
      file_manager::util::GetFileSystemContextForRenderFrameHost(
          profile, render_frame_host());

  std::vector<base::FilePath> trash_info_paths;
  for (const std::string& url : params->urls) {
    storage::FileSystemURL cracked_url =
        file_system_context_->CrackURLInFirstPartyContext(GURL(url));
    if (!cracked_url.is_valid()) {
      return RespondNow(Error("Invalid source url."));
    }
    trash_info_paths.push_back(cracked_url.path());
  }

  validator_ = std::make_unique<file_manager::trash::TrashInfoValidator>(
      profile, /*base_path=*/base::FilePath());

  auto barrier_callback =
      base::BarrierCallback<file_manager::trash::ParsedTrashInfoDataOrError>(
          trash_info_paths.size(),
          base::BindOnce(
              &FileManagerPrivateInternalParseTrashInfoFilesFunction::
                  OnTrashInfoFilesParsed,
              this));

  for (const base::FilePath& path : trash_info_paths) {
    validator_->ValidateAndParseTrashInfo(std::move(path), barrier_callback);
  }

  return RespondLater();
}

void FileManagerPrivateInternalParseTrashInfoFilesFunction::
    OnTrashInfoFilesParsed(
        std::vector<file_manager::trash::ParsedTrashInfoDataOrError>
            parsed_data_or_error) {
  // The underlying trash service could potentially live longer than the Files
  // window that invoked this function, ensure the frame host and browser
  // context are alive before continuing.
  if (!render_frame_host() || !browser_context()) {
    LOG(WARNING) << "Parsing trashinfo files finished but no window available "
                    "to respond to";
    Respond(NoArguments());
    return;
  }

  file_manager::util::FileDefinitionList file_definition_list;
  std::vector<file_manager::trash::ParsedTrashInfoData> valid_data;
  url::Origin origin = render_frame_host()->GetLastCommittedOrigin();

  for (auto& trash_info_data_or_error : parsed_data_or_error) {
    if (!trash_info_data_or_error.has_value()) {
      LOG(ERROR) << "Failed parsing trashinfo file: "
                 << trash_info_data_or_error.error();
      continue;
    }

    file_manager::util::FileDefinition file_definition;
    if (!file_manager::util::ConvertAbsoluteFilePathToRelativeFileSystemPath(
            Profile::FromBrowserContext(browser_context()), origin.GetURL(),
            trash_info_data_or_error.value().absolute_restore_path,
            &file_definition.virtual_path)) {
      LOG(ERROR) << "Failed to convert absolute path to relative path";
      continue;
    }

    file_definition_list.push_back(std::move(file_definition));
    valid_data.push_back(std::move(trash_info_data_or_error.value()));
  }

  file_manager::util::ConvertFileDefinitionListToEntryDefinitionList(
      file_system_context_, origin, std::move(file_definition_list),
      base::BindOnce(&FileManagerPrivateInternalParseTrashInfoFilesFunction::
                         OnConvertFileDefinitionListToEntryDefinitionList,
                     this, std::move(valid_data)));
}

void FileManagerPrivateInternalParseTrashInfoFilesFunction::
    OnConvertFileDefinitionListToEntryDefinitionList(
        std::vector<file_manager::trash::ParsedTrashInfoData> parsed_data,
        std::unique_ptr<file_manager::util::EntryDefinitionList>
            entry_definition_list) {
  DCHECK_EQ(parsed_data.size(), entry_definition_list->size());
  std::vector<api::file_manager_private_internal::ParsedTrashInfoFile> results;

  for (size_t i = 0; i < parsed_data.size(); ++i) {
    const auto& [trash_info_path, trashed_file_path, absolute_restore_path,
                 deletion_date] = parsed_data[i];
    api::file_manager_private_internal::ParsedTrashInfoFile info;

    info.restore_entry.file_system_name =
        entry_definition_list->at(i).file_system_name;
    info.restore_entry.file_system_root =
        entry_definition_list->at(i).file_system_root_url;
    info.restore_entry.file_full_path =
        base::FilePath("/")
            .Append(entry_definition_list->at(i).full_path)
            .value();
    info.restore_entry.file_is_directory =
        entry_definition_list->at(i).is_directory;
    info.trash_info_file_name = trash_info_path.BaseName().value();
    info.deletion_date =
        deletion_date.InMillisecondsFSinceUnixEpochIgnoringNull();

    results.push_back(std::move(info));
  }

  Respond(ArgumentList(extensions::api::file_manager_private_internal::
                           ParseTrashInfoFiles::Results::Create(results)));
}

}  // namespace extensions