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

// Copyright 2021 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/fmpi_get_volume_root_function.h"

#include "ash/webui/file_manager/url_constants.h"
#include "base/files/file.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/ash/file_manager/fileapi_util.h"
#include "chrome/browser/ash/file_manager/volume_manager.h"
#include "chrome/browser/ash/fileapi/file_system_backend.h"
#include "chrome/browser/extensions/chrome_extension_function_details.h"
#include "chrome/common/extensions/api/file_manager_private_internal.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/storage_partition.h"

namespace extensions {

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

  const std::string& volume_id = params->options.volume_id;
  if (volume_id.empty()) {
    return RespondNow(Error("Volume ID must be provided."));
  }

  file_manager::VolumeManager* const volume_manager =
      file_manager::VolumeManager::Get(
          Profile::FromBrowserContext(browser_context()));
  DCHECK(volume_manager);
  base::WeakPtr<file_manager::Volume> volume =
      volume_manager->FindVolumeById(volume_id);
  if (!volume.get()) {
    return RespondNow(Error("Volume with ID '*' not found", volume_id));
  }

  content::ChildProcessSecurityPolicy* policy =
      content::ChildProcessSecurityPolicy::GetInstance();
  DCHECK(policy);
  const auto process_id = source_process_id();
  // Read-only permisisons.
  policy->GrantReadFile(process_id, volume->mount_path());
  if (params->options.writable.value_or(false)) {
    // Additional write permissions.
    policy->GrantCreateReadWriteFile(process_id, volume->mount_path());
    policy->GrantCopyInto(process_id, volume->mount_path());
  }

  // Convert volume's mount path to a virtual path.
  scoped_refptr<storage::FileSystemContext> file_system_context =
      render_frame_host()->GetStoragePartition()->GetFileSystemContext();
  DCHECK(file_system_context.get());
  auto* const backend = ash::FileSystemBackend::Get(*file_system_context);
  DCHECK(backend);
  file_manager::util::FileDefinition fd;
  if (!backend->GetVirtualPath(volume->mount_path(), &fd.virtual_path)) {
    return RespondNow(
        Error("Cannot get virtual path for volume with ID '*'", volume_id));
  }

  // Grant the caller right rights to crack URLs based on the virtual path.
  const url::Origin origin = url::Origin::Create(source_url());
  backend->GrantFileAccessToOrigin(origin, fd.virtual_path);

  // Convert volume's mount path to an EntryDefinition.
  file_manager::util::ConvertFileDefinitionToEntryDefinition(
      file_system_context, origin, fd,
      base::BindOnce(
          &FileManagerPrivateInternalGetVolumeRootFunction::OnRequestDone,
          this));

  return RespondLater();
}

void FileManagerPrivateInternalGetVolumeRootFunction::OnRequestDone(
    const file_manager::util::EntryDefinition& entry_definition) {
  if (entry_definition.error != base::File::FILE_OK) {
    Respond(Error("Failed to resolve volume's root directory: *",
                  base::NumberToString(entry_definition.error)));
  } else {
    Respond(WithArguments(ConvertEntryDefinitionToValue(entry_definition)));
  }
}

}  // namespace extensions