blob: 51b2a3de4fc1ecc13b8488d4a1b6b987a974462d [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/extensions/file_manager/private_api_mount.h"
#include <memory>
#include <string>
#include "base/files/file_util.h"
#include "base/format_macros.h"
#include "base/memory/weak_ptr.h"
#include "base/task_scheduler/post_task.h"
#include "chrome/browser/chromeos/drive/file_system_util.h"
#include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
#include "chrome/browser/chromeos/file_manager/fileapi_util.h"
#include "chrome/browser/chromeos/file_manager/volume_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/file_manager_private.h"
#include "chromeos/disks/disk_mount_manager.h"
#include "components/drive/chromeos/file_system_interface.h"
#include "components/drive/event_logger.h"
#include "content/public/browser/browser_thread.h"
#include "google_apis/drive/task_util.h"
#include "ui/shell_dialogs/selected_file_info.h"
using chromeos::disks::DiskMountManager;
using content::BrowserThread;
namespace file_manager_private = extensions::api::file_manager_private;
namespace extensions {
namespace {
// Does chmod o+r for the given path to ensure the file is readable from avfs.
void EnsureReadableFilePermissionAsync(
const base::FilePath& path,
const base::Callback<void(drive::FileError, const base::FilePath&)>&
callback) {
int mode = 0;
if (!base::GetPosixFilePermissions(path, &mode) ||
!base::SetPosixFilePermissions(path, mode | S_IROTH)) {
callback.Run(drive::FILE_ERROR_ACCESS_DENIED, base::FilePath());
return;
}
callback.Run(drive::FILE_ERROR_OK, path);
}
} // namespace
bool FileManagerPrivateAddMountFunction::RunAsync() {
using file_manager_private::AddMount::Params;
const std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
if (logger) {
logger->Log(logging::LOG_INFO, "%s[%d] called. (source: '%s')", name(),
request_id(),
params->source.empty() ? "(none)" : params->source.c_str());
}
set_log_on_completion(true);
const base::FilePath path = file_manager::util::GetLocalPathFromURL(
render_frame_host(), GetProfile(), GURL(params->source));
if (path.empty())
return false;
// Check if the source path is under Drive cache directory.
if (drive::util::IsUnderDriveMountPoint(path)) {
drive::FileSystemInterface* file_system =
drive::util::GetFileSystemByProfile(GetProfile());
if (!file_system)
return false;
// Ensure that the cache file exists.
const base::FilePath drive_path = drive::util::ExtractDrivePath(path);
file_system->GetFile(
drive_path,
base::Bind(&FileManagerPrivateAddMountFunction::RunAfterGetDriveFile,
this,
drive_path));
} else {
file_manager::VolumeManager* volume_manager =
file_manager::VolumeManager::Get(GetProfile());
DCHECK(volume_manager);
bool is_under_downloads = false;
const std::vector<base::WeakPtr<file_manager::Volume>> volumes =
volume_manager->GetVolumeList();
for (const auto& volume : volumes) {
if (volume->type() == file_manager::VOLUME_TYPE_DOWNLOADS_DIRECTORY &&
volume->mount_path().IsParent(path)) {
is_under_downloads = true;
break;
}
}
if (is_under_downloads) {
// For files under downloads, change the file permission and make it
// readable from avfs/fuse if needed.
base::PostTaskWithTraits(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
base::BindOnce(&EnsureReadableFilePermissionAsync, path,
google_apis::CreateRelayCallback(
base::Bind(&FileManagerPrivateAddMountFunction::
RunAfterMarkCacheFileAsMounted,
this, path.BaseName()))));
} else {
RunAfterMarkCacheFileAsMounted(
path.BaseName(), drive::FILE_ERROR_OK, path);
}
}
return true;
}
void FileManagerPrivateAddMountFunction::RunAfterGetDriveFile(
const base::FilePath& drive_path,
drive::FileError error,
const base::FilePath& cache_path,
std::unique_ptr<drive::ResourceEntry> entry) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (error != drive::FILE_ERROR_OK) {
SendResponse(false);
return;
}
drive::FileSystemInterface* const file_system =
drive::util::GetFileSystemByProfile(GetProfile());
if (!file_system) {
SendResponse(false);
return;
}
file_system->MarkCacheFileAsMounted(
drive_path,
base::Bind(
&FileManagerPrivateAddMountFunction::RunAfterMarkCacheFileAsMounted,
this,
drive_path.BaseName()));
}
void FileManagerPrivateAddMountFunction::RunAfterMarkCacheFileAsMounted(
const base::FilePath& display_name,
drive::FileError error,
const base::FilePath& file_path) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (error != drive::FILE_ERROR_OK) {
SendResponse(false);
return;
}
// Pass back the actual source path of the mount point.
SetResult(std::make_unique<base::Value>(file_path.AsUTF8Unsafe()));
SendResponse(true);
// MountPath() takes a std::string.
DiskMountManager* disk_mount_manager = DiskMountManager::GetInstance();
disk_mount_manager->MountPath(
file_path.AsUTF8Unsafe(),
base::FilePath(display_name.Extension()).AsUTF8Unsafe(),
display_name.AsUTF8Unsafe(), chromeos::MOUNT_TYPE_ARCHIVE,
chromeos::MOUNT_ACCESS_MODE_READ_WRITE);
}
bool FileManagerPrivateRemoveMountFunction::RunAsync() {
using file_manager_private::RemoveMount::Params;
const std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
if (logger) {
logger->Log(logging::LOG_INFO, "%s[%d] called. (volume_id: '%s')", name(),
request_id(), params->volume_id.c_str());
}
set_log_on_completion(true);
using file_manager::VolumeManager;
using file_manager::Volume;
VolumeManager* const volume_manager = VolumeManager::Get(GetProfile());
DCHECK(volume_manager);
base::WeakPtr<Volume> volume =
volume_manager->FindVolumeById(params->volume_id);
if (!volume.get())
return false;
// TODO(tbarzic): Send response when callback is received, it would make more
// sense than remembering issued unmount requests in file manager and showing
// errors for them when MountCompleted event is received.
switch (volume->type()) {
case file_manager::VOLUME_TYPE_REMOVABLE_DISK_PARTITION:
case file_manager::VOLUME_TYPE_MOUNTED_ARCHIVE_FILE: {
DiskMountManager::GetInstance()->UnmountPath(
volume->mount_path().value(), chromeos::UNMOUNT_OPTIONS_NONE,
DiskMountManager::UnmountPathCallback());
break;
}
case file_manager::VOLUME_TYPE_PROVIDED: {
chromeos::file_system_provider::Service* service =
chromeos::file_system_provider::Service::Get(GetProfile());
DCHECK(service);
// TODO(mtomasz): Pass a more detailed error than just a bool.
if (!service->RequestUnmount(volume->provider_id(),
volume->file_system_id())) {
return false;
}
break;
}
default:
// Requested unmounting a device which is not unmountable.
return false;
}
SendResponse(true);
return true;
}
bool FileManagerPrivateGetVolumeMetadataListFunction::RunAsync() {
if (args_->GetSize())
return false;
const std::vector<base::WeakPtr<file_manager::Volume>>& volume_list =
file_manager::VolumeManager::Get(GetProfile())->GetVolumeList();
std::string log_string;
std::vector<file_manager_private::VolumeMetadata> result;
for (const auto& volume : volume_list) {
file_manager_private::VolumeMetadata volume_metadata;
file_manager::util::VolumeToVolumeMetadata(GetProfile(), *volume,
&volume_metadata);
result.push_back(std::move(volume_metadata));
if (!log_string.empty())
log_string += ", ";
log_string += volume->mount_path().AsUTF8Unsafe();
}
drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
if (logger) {
logger->Log(logging::LOG_INFO,
"%s[%d] succeeded. (results: '[%s]', %" PRIuS " mount points)",
name(), request_id(), log_string.c_str(), result.size());
}
results_ =
file_manager_private::GetVolumeMetadataList::Results::Create(result);
SendResponse(true);
return true;
}
} // namespace extensions