blob: 2d0b1831946ad0391559ab6a7212a004cf718b47 [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_file_system.h"
#include <sys/statvfs.h>
#include <set>
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/sys_info.h"
#include "base/task_runner_util.h"
#include "base/threading/sequenced_worker_pool.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/drive/file_system_util.h"
#include "chrome/browser/chromeos/extensions/file_manager/event_router.h"
#include "chrome/browser/chromeos/extensions/file_manager/event_router_factory.h"
#include "chrome/browser/chromeos/extensions/file_manager/file_stream_md5_digester.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/path_util.h"
#include "chrome/browser/chromeos/file_manager/volume_manager.h"
#include "chrome/browser/chromeos/fileapi/file_system_backend.h"
#include "chrome/browser/extensions/extension_util.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/extensions/api/file_manager_private.h"
#include "chrome/common/extensions/api/file_manager_private_internal.h"
#include "chromeos/disks/disk_mount_manager.h"
#include "components/drive/chromeos/file_system_interface.h"
#include "components/drive/drive.pb.h"
#include "components/drive/event_logger.h"
#include "components/storage_monitor/storage_info.h"
#include "components/storage_monitor/storage_monitor.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 "device/media_transfer_protocol/media_transfer_protocol_manager.h"
#include "net/base/escape.h"
#include "storage/browser/fileapi/file_stream_reader.h"
#include "storage/browser/fileapi/file_system_context.h"
#include "storage/browser/fileapi/file_system_file_util.h"
#include "storage/browser/fileapi/file_system_operation_context.h"
#include "storage/browser/fileapi/file_system_operation_runner.h"
#include "storage/common/fileapi/file_system_info.h"
#include "storage/common/fileapi/file_system_types.h"
#include "storage/common/fileapi/file_system_util.h"
#include "third_party/cros_system_api/constants/cryptohome.h"
using chromeos::disks::DiskMountManager;
using content::BrowserThread;
using content::ChildProcessSecurityPolicy;
using file_manager::util::EntryDefinition;
using file_manager::util::FileDefinition;
using storage::FileSystemURL;
namespace extensions {
namespace {
const char kRootPath[] = "/";
// Retrieves total and remaining available size on |mount_path|.
void GetSizeStatsOnBlockingPool(const std::string& mount_path,
uint64_t* total_size,
uint64_t* remaining_size) {
struct statvfs stat = {}; // Zero-clear
if (HANDLE_EINTR(statvfs(mount_path.c_str(), &stat)) == 0) {
*total_size = static_cast<uint64_t>(stat.f_blocks) * stat.f_frsize;
*remaining_size = static_cast<uint64_t>(stat.f_bavail) * stat.f_frsize;
}
}
// Used for OnCalculateEvictableCacheSize.
typedef base::Callback<void(const uint64_t* total_size,
const uint64_t* remaining_space)>
GetSizeStatsCallback;
// Calculates the real remaining size of Download volume and pass it to
// GetSizeStatsCallback.
void OnCalculateEvictableCacheSize(const GetSizeStatsCallback& callback,
uint64_t total_size,
uint64_t remaining_size,
uint64_t evictable_cache_size) {
// For calculating real remaining size of Download volume
// - Adds evictable cache size since the space is available if they are
// evicted.
// - Subtracts minimum free space of cryptohome since the space is not
// available for file manager.
const uint64_t real_remaining_size =
std::max(static_cast<int64_t>(remaining_size + evictable_cache_size) -
cryptohome::kMinFreeSpaceInBytes,
int64_t(0));
callback.Run(&total_size, &real_remaining_size);
}
// Retrieves the maximum file name length of the file system of |path|.
// Returns 0 if it could not be queried.
size_t GetFileNameMaxLengthOnBlockingPool(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;
}
// Returns EventRouter for the |profile_id| if available.
file_manager::EventRouter* GetEventRouterByProfileId(void* profile_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// |profile_id| needs to be checked with ProfileManager::IsValidProfile
// before using it.
if (!g_browser_process->profile_manager()->IsValidProfile(profile_id))
return NULL;
Profile* profile = reinterpret_cast<Profile*>(profile_id);
return file_manager::EventRouterFactory::GetForProfile(profile);
}
// Notifies the copy progress to extensions via event router.
void NotifyCopyProgress(
void* profile_id,
storage::FileSystemOperationRunner::OperationID operation_id,
storage::FileSystemOperation::CopyProgressType type,
const FileSystemURL& source_url,
const FileSystemURL& destination_url,
int64_t size) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
file_manager::EventRouter* event_router =
GetEventRouterByProfileId(profile_id);
if (event_router) {
event_router->OnCopyProgress(
operation_id, type,
source_url.ToGURL(), destination_url.ToGURL(), size);
}
}
// Callback invoked periodically on progress update of Copy().
void OnCopyProgress(
void* profile_id,
storage::FileSystemOperationRunner::OperationID* operation_id,
storage::FileSystemOperation::CopyProgressType type,
const FileSystemURL& source_url,
const FileSystemURL& destination_url,
int64_t size) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&NotifyCopyProgress,
profile_id, *operation_id, type,
source_url, destination_url, size));
}
// Notifies the copy completion to extensions via event router.
void NotifyCopyCompletion(
void* profile_id,
storage::FileSystemOperationRunner::OperationID operation_id,
const FileSystemURL& source_url,
const FileSystemURL& destination_url,
base::File::Error error) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
file_manager::EventRouter* event_router =
GetEventRouterByProfileId(profile_id);
if (event_router)
event_router->OnCopyCompleted(
operation_id,
source_url.ToGURL(), destination_url.ToGURL(), error);
}
// Callback invoked upon completion of Copy() (regardless of succeeded or
// failed).
void OnCopyCompleted(
void* profile_id,
storage::FileSystemOperationRunner::OperationID* operation_id,
const FileSystemURL& source_url,
const FileSystemURL& destination_url,
base::File::Error error) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&NotifyCopyCompletion,
profile_id, *operation_id,
source_url, destination_url, error));
}
// Starts the copy operation via FileSystemOperationRunner.
storage::FileSystemOperationRunner::OperationID StartCopyOnIOThread(
void* profile_id,
scoped_refptr<storage::FileSystemContext> file_system_context,
const FileSystemURL& source_url,
const FileSystemURL& destination_url) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Note: |operation_id| is owned by the callback for
// FileSystemOperationRunner::Copy(). It is always called in the next message
// loop or later, so at least during this invocation it should alive.
//
// TODO(yawano): change ERROR_BEHAVIOR_ABORT to ERROR_BEHAVIOR_SKIP after
// error messages of individual operations become appear in the Files.app
// UI.
storage::FileSystemOperationRunner::OperationID* operation_id =
new storage::FileSystemOperationRunner::OperationID;
*operation_id = file_system_context->operation_runner()->Copy(
source_url, destination_url,
storage::FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED,
storage::FileSystemOperation::ERROR_BEHAVIOR_ABORT,
base::Bind(&OnCopyProgress, profile_id, base::Unretained(operation_id)),
base::Bind(&OnCopyCompleted, profile_id, base::Owned(operation_id),
source_url, destination_url));
return *operation_id;
}
void OnCopyCancelled(base::File::Error error) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// We just ignore the status if the copy is actually cancelled or not,
// because failing cancellation means the operation is not running now.
DLOG_IF(WARNING, error != base::File::FILE_OK)
<< "Failed to cancel copy: " << error;
}
// Cancels the running copy operation identified by |operation_id|.
void CancelCopyOnIOThread(
scoped_refptr<storage::FileSystemContext> file_system_context,
storage::FileSystemOperationRunner::OperationID operation_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
file_system_context->operation_runner()->Cancel(
operation_id, base::Bind(&OnCopyCancelled));
}
// Converts a status code to a bool value and calls the |callback| with it.
void StatusCallbackToResponseCallback(
const base::Callback<void(bool)>& callback,
base::File::Error result) {
callback.Run(result == base::File::FILE_OK);
}
// Calls a response callback (on the UI thread) with a file content hash
// computed on the IO thread.
void ComputeChecksumRespondOnUIThread(
const base::Callback<void(const std::string&)>& callback,
const std::string& hash) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(callback, hash));
}
// Calls a response callback on the UI thread.
void GetFileMetadataRespondOnUIThread(
const storage::FileSystemOperation::GetMetadataCallback& callback,
base::File::Error result,
const base::File::Info& file_info) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(callback, result, file_info));
}
} // namespace
ExtensionFunction::ResponseAction
FileManagerPrivateEnableExternalFileSchemeFunction::Run() {
ChildProcessSecurityPolicy::GetInstance()->GrantScheme(
render_frame_host()->GetProcess()->GetID(), content::kExternalFileScheme);
return RespondNow(NoArguments());
}
FileManagerPrivateGrantAccessFunction::FileManagerPrivateGrantAccessFunction()
: chrome_details_(this) {
}
ExtensionFunction::ResponseAction FileManagerPrivateGrantAccessFunction::Run() {
using extensions::api::file_manager_private::GrantAccess::Params;
const std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
scoped_refptr<storage::FileSystemContext> file_system_context =
file_manager::util::GetFileSystemContextForRenderFrameHost(
chrome_details_.GetProfile(), render_frame_host());
storage::ExternalFileSystemBackend* const backend =
file_system_context->external_backend();
DCHECK(backend);
const std::vector<Profile*>& profiles =
g_browser_process->profile_manager()->GetLoadedProfiles();
for (const auto& profile : profiles) {
if (profile->IsOffTheRecord())
continue;
const GURL site = util::GetSiteForExtensionId(extension_id(), profile);
storage::FileSystemContext* const context =
content::BrowserContext::GetStoragePartitionForSite(profile, site)
->GetFileSystemContext();
for (const auto& url : params->entry_urls) {
const storage::FileSystemURL file_system_url =
context->CrackURL(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->GrantFileAccessToExtension(extension_->id(),
file_system_url.virtual_path());
content::ChildProcessSecurityPolicy::GetInstance()
->GrantCreateReadWriteFile(render_frame_host()->GetProcess()->GetID(),
file_system_url.path());
}
}
return RespondNow(NoArguments());
}
void FileWatchFunctionBase::Respond(bool success) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
SetResult(base::MakeUnique<base::FundamentalValue>(success));
SendResponse(success);
}
bool FileWatchFunctionBase::RunAsync() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!render_frame_host() || !render_frame_host()->GetProcess())
return false;
// First param is url of a file to watch.
std::string url;
if (!args_->GetString(0, &url) || url.empty())
return false;
scoped_refptr<storage::FileSystemContext> file_system_context =
file_manager::util::GetFileSystemContextForRenderFrameHost(
GetProfile(), render_frame_host());
const FileSystemURL file_system_url =
file_system_context->CrackURL(GURL(url));
if (file_system_url.path().empty()) {
Respond(false);
return true;
}
PerformFileWatchOperation(file_system_context, file_system_url,
extension_id());
return true;
}
void FileManagerPrivateInternalAddFileWatchFunction::PerformFileWatchOperation(
scoped_refptr<storage::FileSystemContext> file_system_context,
const storage::FileSystemURL& file_system_url,
const std::string& extension_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
file_manager::EventRouter* const event_router =
file_manager::EventRouterFactory::GetForProfile(GetProfile());
storage::WatcherManager* const watcher_manager =
file_system_context->GetWatcherManager(file_system_url.type());
if (watcher_manager) {
watcher_manager->AddWatcher(
file_system_url, false /* recursive */,
base::Bind(
&StatusCallbackToResponseCallback,
base::Bind(&FileManagerPrivateInternalAddFileWatchFunction::Respond,
this)),
base::Bind(&file_manager::EventRouter::OnWatcherManagerNotification,
event_router->GetWeakPtr(), file_system_url, extension_id));
return;
}
// Obsolete. Fallback code if storage::WatcherManager is not implemented.
event_router->AddFileWatch(
file_system_url.path(), file_system_url.virtual_path(), extension_id,
base::Bind(&FileManagerPrivateInternalAddFileWatchFunction::Respond,
this));
}
void FileManagerPrivateInternalRemoveFileWatchFunction::
PerformFileWatchOperation(
scoped_refptr<storage::FileSystemContext> file_system_context,
const storage::FileSystemURL& file_system_url,
const std::string& extension_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
file_manager::EventRouter* const event_router =
file_manager::EventRouterFactory::GetForProfile(GetProfile());
storage::WatcherManager* const watcher_manager =
file_system_context->GetWatcherManager(file_system_url.type());
if (watcher_manager) {
watcher_manager->RemoveWatcher(
file_system_url, false /* recursive */,
base::Bind(&StatusCallbackToResponseCallback,
base::Bind(&FileWatchFunctionBase::Respond, this)));
return;
}
// Obsolete. Fallback code if storage::WatcherManager is not implemented.
event_router->RemoveFileWatch(file_system_url.path(), extension_id);
Respond(true);
}
bool FileManagerPrivateGetSizeStatsFunction::RunAsync() {
using extensions::api::file_manager_private::GetSizeStats::Params;
const std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
using file_manager::VolumeManager;
using file_manager::Volume;
VolumeManager* const volume_manager = VolumeManager::Get(GetProfile());
if (!volume_manager)
return false;
base::WeakPtr<Volume> volume =
volume_manager->FindVolumeById(params->volume_id);
if (!volume.get())
return false;
if (volume->type() == file_manager::VOLUME_TYPE_GOOGLE_DRIVE) {
drive::FileSystemInterface* file_system =
drive::util::GetFileSystemByProfile(GetProfile());
if (!file_system) {
// |file_system| is NULL if Drive is disabled.
// If stats couldn't be gotten for drive, result should be left
// undefined. See comments in GetDriveAvailableSpaceCallback().
SendResponse(true);
return true;
}
file_system->GetAvailableSpace(base::Bind(
&FileManagerPrivateGetSizeStatsFunction::OnGetDriveAvailableSpace,
this));
} else 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.
device::MediaTransferProtocolManager* manager =
storage_monitor->media_transfer_protocol_manager();
manager->GetStorageInfoFromDevice(
storage_name,
base::Bind(
&FileManagerPrivateGetSizeStatsFunction::OnGetMtpAvailableSpace,
this));
} else {
uint64_t* total_size = new uint64_t(0);
uint64_t* remaining_size = new uint64_t(0);
BrowserThread::PostBlockingPoolTaskAndReply(
FROM_HERE,
base::Bind(&GetSizeStatsOnBlockingPool, volume->mount_path().value(),
total_size, remaining_size),
base::Bind(
&FileManagerPrivateGetSizeStatsFunction::OnGetLocalSpace, this,
base::Owned(total_size), base::Owned(remaining_size),
volume->type() == file_manager::VOLUME_TYPE_DOWNLOADS_DIRECTORY));
}
return true;
}
void FileManagerPrivateGetSizeStatsFunction::OnGetLocalSpace(
uint64_t* total_size,
uint64_t* remaining_size,
bool is_download) {
drive::FileSystemInterface* const file_system =
drive::util::GetFileSystemByProfile(GetProfile());
if (!is_download || !file_system) {
OnGetSizeStats(total_size, remaining_size);
return;
}
// We need to add evictable cache size to the remaining size of Downloads
// volume if drive is available.
file_system->CalculateEvictableCacheSize(base::Bind(
&OnCalculateEvictableCacheSize,
base::Bind(&FileManagerPrivateGetSizeStatsFunction::OnGetSizeStats, this),
*total_size, *remaining_size));
}
void FileManagerPrivateGetSizeStatsFunction::OnGetDriveAvailableSpace(
drive::FileError error,
int64_t bytes_total,
int64_t bytes_used) {
if (error == drive::FILE_ERROR_OK) {
const uint64_t bytes_total_unsigned = bytes_total;
// bytes_used can be larger than bytes_total (over quota).
const uint64_t bytes_remaining_unsigned =
std::max(bytes_total - bytes_used, int64_t(0));
OnGetSizeStats(&bytes_total_unsigned, &bytes_remaining_unsigned);
} else {
// If stats couldn't be gotten for drive, result should be left undefined.
SendResponse(true);
}
}
void FileManagerPrivateGetSizeStatsFunction::OnGetMtpAvailableSpace(
const MtpStorageInfo& 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.
SendResponse(true);
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::OnGetSizeStats(
const uint64_t* total_size,
const uint64_t* remaining_size) {
std::unique_ptr<base::DictionaryValue> sizes(new base::DictionaryValue());
sizes->SetDouble("totalSize", static_cast<double>(*total_size));
sizes->SetDouble("remainingSize", static_cast<double>(*remaining_size));
SetResult(std::move(sizes));
SendResponse(true);
}
bool FileManagerPrivateInternalValidatePathNameLengthFunction::RunAsync() {
using extensions::api::file_manager_private_internal::ValidatePathNameLength::
Params;
const std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
scoped_refptr<storage::FileSystemContext> file_system_context =
file_manager::util::GetFileSystemContextForRenderFrameHost(
GetProfile(), render_frame_host());
const storage::FileSystemURL file_system_url(
file_system_context->CrackURL(GURL(params->parent_url)));
if (!chromeos::FileSystemBackend::CanHandleURL(file_system_url))
return false;
// No explicit limit on the length of Drive file names.
if (file_system_url.type() == storage::kFileSystemTypeDrive) {
SetResult(base::MakeUnique<base::FundamentalValue>(true));
SendResponse(true);
return true;
}
base::PostTaskAndReplyWithResult(
BrowserThread::GetBlockingPool(), FROM_HERE,
base::Bind(&GetFileNameMaxLengthOnBlockingPool,
file_system_url.path().AsUTF8Unsafe()),
base::Bind(&FileManagerPrivateInternalValidatePathNameLengthFunction::
OnFilePathLimitRetrieved,
this, params->name.size()));
return true;
}
void FileManagerPrivateInternalValidatePathNameLengthFunction::
OnFilePathLimitRetrieved(size_t current_length, size_t max_length) {
SetResult(
base::MakeUnique<base::FundamentalValue>(current_length <= max_length));
SendResponse(true);
}
bool FileManagerPrivateFormatVolumeFunction::RunAsync() {
using extensions::api::file_manager_private::FormatVolume::Params;
const std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
using file_manager::VolumeManager;
using file_manager::Volume;
VolumeManager* const volume_manager = VolumeManager::Get(GetProfile());
if (!volume_manager)
return false;
base::WeakPtr<Volume> volume =
volume_manager->FindVolumeById(params->volume_id);
if (!volume)
return false;
DiskMountManager::GetInstance()->FormatMountedDevice(
volume->mount_path().AsUTF8Unsafe());
SendResponse(true);
return true;
}
// Obtains file size of URL.
void GetFileMetadataOnIOThread(
scoped_refptr<storage::FileSystemContext> file_system_context,
const FileSystemURL& url,
int fields,
const storage::FileSystemOperation::GetMetadataCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
file_system_context->operation_runner()->GetMetadata(
url, fields, base::Bind(&GetFileMetadataRespondOnUIThread, callback));
}
// Checks if the available space of the |path| is enough for required |bytes|.
bool CheckLocalDiskSpaceOnIOThread(const base::FilePath& path, int64_t bytes) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
return bytes <= base::SysInfo::AmountOfFreeDiskSpace(path) -
cryptohome::kMinFreeSpaceInBytes;
}
bool FileManagerPrivateInternalStartCopyFunction::RunAsync() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
using extensions::api::file_manager_private_internal::StartCopy::Params;
const std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
if (params->url.empty() || params->parent_url.empty() ||
params->new_name.empty()) {
// Error code in format of DOMError.name.
SetError("EncodingError");
return false;
}
scoped_refptr<storage::FileSystemContext> file_system_context =
file_manager::util::GetFileSystemContextForRenderFrameHost(
GetProfile(), render_frame_host());
// |parent| may have a trailing slash if it is a root directory.
std::string destination_url_string = params->parent_url;
if (destination_url_string[destination_url_string.size() - 1] != '/')
destination_url_string += '/';
destination_url_string += net::EscapePath(params->new_name);
source_url_ = file_system_context->CrackURL(GURL(params->url));
destination_url_ =
file_system_context->CrackURL(GURL(destination_url_string));
if (!source_url_.is_valid() || !destination_url_.is_valid()) {
// Error code in format of DOMError.name.
SetError("EncodingError");
return false;
}
// Check if the destination directory is downloads. If so, secure available
// spece by freeing drive caches.
if (destination_url_.filesystem_id() ==
file_manager::util::GetDownloadsMountPointName(GetProfile())) {
return BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&GetFileMetadataOnIOThread, file_system_context, source_url_,
storage::FileSystemOperation::GET_METADATA_FIELD_SIZE,
base::Bind(&FileManagerPrivateInternalStartCopyFunction::
RunAfterGetFileMetadata,
this)));
}
return BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(
&FileManagerPrivateInternalStartCopyFunction::RunAfterFreeDiskSpace,
this, true));
}
void FileManagerPrivateInternalStartCopyFunction::RunAfterGetFileMetadata(
base::File::Error result,
const base::File::Info& file_info) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (result != base::File::FILE_OK) {
SetError("NotFoundError");
SendResponse(false);
return;
}
drive::FileSystemInterface* const drive_file_system =
drive::util::GetFileSystemByProfile(GetProfile());
if (drive_file_system) {
drive_file_system->FreeDiskSpaceIfNeededFor(
file_info.size,
base::Bind(
&FileManagerPrivateInternalStartCopyFunction::RunAfterFreeDiskSpace,
this));
} else {
const bool result = BrowserThread::PostTaskAndReplyWithResult(
BrowserThread::IO, FROM_HERE,
base::Bind(
&CheckLocalDiskSpaceOnIOThread,
file_manager::util::GetDownloadsFolderForProfile(GetProfile()),
file_info.size),
base::Bind(
&FileManagerPrivateInternalStartCopyFunction::RunAfterFreeDiskSpace,
this));
if (!result)
SendResponse(false);
}
}
void FileManagerPrivateInternalStartCopyFunction::RunAfterFreeDiskSpace(
bool available) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!available) {
SetError("QuotaExceededError");
SendResponse(false);
return;
}
scoped_refptr<storage::FileSystemContext> file_system_context =
file_manager::util::GetFileSystemContextForRenderFrameHost(
GetProfile(), render_frame_host());
const bool result = BrowserThread::PostTaskAndReplyWithResult(
BrowserThread::IO, FROM_HERE,
base::Bind(&StartCopyOnIOThread, GetProfile(), file_system_context,
source_url_, destination_url_),
base::Bind(
&FileManagerPrivateInternalStartCopyFunction::RunAfterStartCopy,
this));
if (!result)
SendResponse(false);
}
void FileManagerPrivateInternalStartCopyFunction::RunAfterStartCopy(
int operation_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
SetResult(base::MakeUnique<base::FundamentalValue>(operation_id));
SendResponse(true);
}
bool FileManagerPrivateCancelCopyFunction::RunAsync() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
using extensions::api::file_manager_private::CancelCopy::Params;
const std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
scoped_refptr<storage::FileSystemContext> file_system_context =
file_manager::util::GetFileSystemContextForRenderFrameHost(
GetProfile(), render_frame_host());
// We don't much take care about the result of cancellation.
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&CancelCopyOnIOThread, file_system_context, params->copy_id));
SendResponse(true);
return true;
}
bool FileManagerPrivateInternalResolveIsolatedEntriesFunction::RunAsync() {
using extensions::api::file_manager_private_internal::ResolveIsolatedEntries::
Params;
const std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
scoped_refptr<storage::FileSystemContext> file_system_context =
file_manager::util::GetFileSystemContextForRenderFrameHost(
GetProfile(), render_frame_host());
DCHECK(file_system_context.get());
const storage::ExternalFileSystemBackend* external_backend =
file_system_context->external_backend();
DCHECK(external_backend);
file_manager::util::FileDefinitionList file_definition_list;
for (size_t i = 0; i < params->urls.size(); ++i) {
const FileSystemURL file_system_url =
file_system_context->CrackURL(GURL(params->urls[i]));
DCHECK(external_backend->CanHandleType(file_system_url.type()));
FileDefinition file_definition;
const bool result =
file_manager::util::ConvertAbsoluteFilePathToRelativeFileSystemPath(
GetProfile(), extension_->id(), file_system_url.path(),
&file_definition.virtual_path);
if (!result)
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(
GetProfile(),
extension_->id(),
file_definition_list, // Safe, since copied internally.
base::Bind(
&FileManagerPrivateInternalResolveIsolatedEntriesFunction::
RunAsyncAfterConvertFileDefinitionListToEntryDefinitionList,
this));
return true;
}
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)
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));
}
results_ = extensions::api::file_manager_private_internal::
ResolveIsolatedEntries::Results::Create(entries);
SendResponse(true);
}
FileManagerPrivateInternalComputeChecksumFunction::
FileManagerPrivateInternalComputeChecksumFunction()
: digester_(new drive::util::FileStreamMd5Digester()) {
}
FileManagerPrivateInternalComputeChecksumFunction::
~FileManagerPrivateInternalComputeChecksumFunction() {
}
bool FileManagerPrivateInternalComputeChecksumFunction::RunAsync() {
using extensions::api::file_manager_private_internal::ComputeChecksum::Params;
using drive::util::FileStreamMd5Digester;
const std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
if (params->url.empty()) {
SetError("File URL must be provided");
return false;
}
scoped_refptr<storage::FileSystemContext> file_system_context =
file_manager::util::GetFileSystemContextForRenderFrameHost(
GetProfile(), render_frame_host());
FileSystemURL file_system_url(
file_system_context->CrackURL(GURL(params->url)));
if (!file_system_url.is_valid()) {
SetError("File URL was invalid");
return false;
}
std::unique_ptr<storage::FileStreamReader> reader =
file_system_context->CreateFileStreamReader(
file_system_url, 0, storage::kMaximumLength, base::Time());
FileStreamMd5Digester::ResultCallback result_callback = base::Bind(
&ComputeChecksumRespondOnUIThread,
base::Bind(&FileManagerPrivateInternalComputeChecksumFunction::Respond,
this));
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&FileStreamMd5Digester::GetMd5Digest,
base::Unretained(digester_.get()),
base::Passed(&reader), result_callback));
return true;
}
void FileManagerPrivateInternalComputeChecksumFunction::Respond(
const std::string& hash) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
SetResult(base::MakeUnique<base::StringValue>(hash));
SendResponse(true);
}
bool FileManagerPrivateSearchFilesByHashesFunction::RunAsync() {
using api::file_manager_private::SearchFilesByHashes::Params;
const std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
// TODO(hirono): Check the volume ID and fail the function for volumes other
// than Drive.
drive::EventLogger* const logger =
file_manager::util::GetLogger(GetProfile());
if (logger) {
logger->Log(logging::LOG_INFO,
"%s[%d] called. (volume id: %s, number of hashes: %zd)", name(),
request_id(), params->volume_id.c_str(),
params->hash_list.size());
}
set_log_on_completion(true);
drive::FileSystemInterface* const file_system =
drive::util::GetFileSystemByProfile(GetProfile());
if (!file_system) {
// |file_system| is NULL if Drive is disabled.
return false;
}
std::set<std::string> hashes(params->hash_list.begin(),
params->hash_list.end());
file_system->SearchByHashes(
hashes,
base::Bind(
&FileManagerPrivateSearchFilesByHashesFunction::OnSearchByHashes,
this, hashes));
return true;
}
void FileManagerPrivateSearchFilesByHashesFunction::OnSearchByHashes(
const std::set<std::string>& hashes,
drive::FileError error,
const std::vector<drive::HashAndFilePath>& search_results) {
if (error != drive::FileError::FILE_ERROR_OK) {
SendResponse(false);
return;
}
std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
for (const auto& hash : hashes) {
result->SetWithoutPathExpansion(hash,
base::WrapUnique(new base::ListValue()));
}
for (const auto& hashAndPath : search_results) {
DCHECK(result->HasKey(hashAndPath.hash));
base::ListValue* list;
result->GetListWithoutPathExpansion(hashAndPath.hash, &list);
list->AppendString(
file_manager::util::ConvertDrivePathToFileSystemUrl(
GetProfile(), hashAndPath.path, extension_id()).spec());
}
SetResult(std::move(result));
SendResponse(true);
}
ExtensionFunction::ResponseAction
FileManagerPrivateIsUMAEnabledFunction::Run() {
return RespondNow(OneArgument(new base::FundamentalValue(
ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled())));
}
FileManagerPrivateInternalSetEntryTagFunction::
FileManagerPrivateInternalSetEntryTagFunction()
: chrome_details_(this) {}
ExtensionFunction::ResponseAction
FileManagerPrivateInternalSetEntryTagFunction::Run() {
using extensions::api::file_manager_private_internal::SetEntryTag::Params;
const std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
const base::FilePath local_path = file_manager::util::GetLocalPathFromURL(
render_frame_host(), chrome_details_.GetProfile(), GURL(params->url));
const base::FilePath drive_path = drive::util::ExtractDrivePath(local_path);
if (drive_path.empty())
return RespondNow(Error("Only Drive files and directories are supported."));
drive::FileSystemInterface* const file_system =
drive::util::GetFileSystemByProfile(chrome_details_.GetProfile());
// |file_system| is NULL if Drive is disabled.
if (!file_system)
return RespondNow(Error("Drive is disabled."));
google_apis::drive::Property::Visibility visibility;
switch (params->visibility) {
case extensions::api::file_manager_private::ENTRY_TAG_VISIBILITY_PRIVATE:
visibility = google_apis::drive::Property::VISIBILITY_PRIVATE;
break;
case extensions::api::file_manager_private::ENTRY_TAG_VISIBILITY_PUBLIC:
visibility = google_apis::drive::Property::VISIBILITY_PUBLIC;
break;
default:
NOTREACHED();
return RespondNow(Error("Invalid visibility."));
break;
}
file_system->SetProperty(
drive_path, visibility, params->key, params->value,
base::Bind(&FileManagerPrivateInternalSetEntryTagFunction::
OnSetEntryPropertyCompleted,
this));
return RespondLater();
}
void FileManagerPrivateInternalSetEntryTagFunction::OnSetEntryPropertyCompleted(
drive::FileError result) {
Respond(result == drive::FILE_ERROR_OK ? NoArguments()
: Error("Failed to set a tag."));
}
} // namespace extensions