blob: 07f2978bba2e9a46e03afff4de0c832787746b38 [file] [log] [blame]
// Copyright 2014 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/drive/fileapi/fileapi_worker.h"
#include <stddef.h>
#include <memory>
#include <string>
#include <utility>
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/task_scheduler/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/chromeos/drive/file_system_util.h"
#include "components/drive/chromeos/file_system_interface.h"
#include "components/drive/drive.pb.h"
#include "components/drive/file_errors.h"
#include "components/drive/resource_entry_conversion.h"
#include "components/services/filesystem/public/interfaces/types.mojom.h"
#include "content/public/browser/browser_thread.h"
#include "storage/browser/fileapi/file_system_url.h"
using content::BrowserThread;
namespace drive {
namespace fileapi_internal {
namespace {
// The summary of opening mode is:
// - File::FLAG_OPEN: Open the existing file. Fail if not exists.
// - File::FLAG_CREATE: Create the file if not exists. Fail if exists.
// - File::FLAG_OPEN_ALWAYS: Open the existing file. Create a new file
// if not exists.
// - File::FLAG_CREATE_ALWAYS: Create a new file if not exists. If exists
// open it with truncate.
// - File::FLAG_OPEN_TRUNCATE: Open the existing file with truncate.
// Fail if not exists.
OpenMode GetOpenMode(int file_flag) {
if (file_flag & (base::File::FLAG_OPEN | base::File::FLAG_OPEN_TRUNCATED))
return OPEN_FILE;
if (file_flag & base::File::FLAG_CREATE)
return CREATE_FILE;
DCHECK(file_flag &
(base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_CREATE_ALWAYS));
return OPEN_OR_CREATE_FILE;
}
// Runs |callback| with the File::Error converted from |error|.
void RunStatusCallbackByFileError(const StatusCallback& callback,
FileError error) {
callback.Run(FileErrorToBaseFileError(error));
}
// Runs |callback| with arguments converted from |error| and |entry|.
void RunGetFileInfoCallback(const GetFileInfoCallback& callback,
FileError error,
std::unique_ptr<ResourceEntry> entry) {
if (error != FILE_ERROR_OK) {
callback.Run(FileErrorToBaseFileError(error), base::File::Info());
return;
}
DCHECK(entry);
base::File::Info file_info;
ConvertResourceEntryToFileInfo(*entry, &file_info);
callback.Run(base::File::FILE_OK, file_info);
}
// Runs |callback| with entries.
void RunReadDirectoryCallbackWithEntries(
const ReadDirectoryCallback& callback,
std::unique_ptr<ResourceEntryVector> resource_entries) {
DCHECK(resource_entries);
std::vector<filesystem::mojom::DirectoryEntry> entries;
// Convert drive files to File API's directory entry.
entries.reserve(resource_entries->size());
for (size_t i = 0; i < resource_entries->size(); ++i) {
const ResourceEntry& resource_entry = (*resource_entries)[i];
entries.emplace_back(base::FilePath(resource_entry.base_name()),
resource_entry.file_info().is_directory()
? filesystem::mojom::FsFileType::DIRECTORY
: filesystem::mojom::FsFileType::REGULAR_FILE);
}
callback.Run(base::File::FILE_OK, entries, true /*has_more*/);
}
// Runs |callback| with |error|.
void RunReadDirectoryCallbackOnCompletion(const ReadDirectoryCallback& callback,
FileError error) {
callback.Run(FileErrorToBaseFileError(error),
std::vector<filesystem::mojom::DirectoryEntry>(),
false /*has_more*/);
}
// Runs |callback| with arguments based on |error|, |local_path| and |entry|.
void RunCreateSnapshotFileCallback(const CreateSnapshotFileCallback& callback,
FileError error,
const base::FilePath& local_path,
std::unique_ptr<ResourceEntry> entry) {
if (error != FILE_ERROR_OK) {
callback.Run(FileErrorToBaseFileError(error),
base::File::Info(),
base::FilePath(),
storage::ScopedFile::ScopeOutPolicy());
return;
}
DCHECK(entry);
// When reading file, last modified time specified in file info will be
// compared to the last modified time of the local version of the drive file.
// Since those two values don't generally match (last modification time on the
// drive server vs. last modification time of the local, downloaded file), so
// we have to opt out from this check. We do this by unsetting last_modified
// value in the file info passed to the CreateSnapshot caller.
base::File::Info file_info;
ConvertResourceEntryToFileInfo(*entry, &file_info);
file_info.last_modified = base::Time();
// If the file is a hosted document, a temporary JSON file is created to
// represent the document. The JSON file is not cached and its lifetime
// is managed by ShareableFileReference.
storage::ScopedFile::ScopeOutPolicy scope_out_policy =
entry->file_specific_info().is_hosted_document()
? storage::ScopedFile::DELETE_ON_SCOPE_OUT
: storage::ScopedFile::DONT_DELETE_ON_SCOPE_OUT;
callback.Run(base::File::FILE_OK, file_info, local_path, scope_out_policy);
}
// Runs |callback| with arguments converted from |error| and |local_path|.
void RunCreateWritableSnapshotFileCallback(
const CreateWritableSnapshotFileCallback& callback,
FileError error,
const base::FilePath& local_path,
const base::Closure& close_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
callback.Run(FileErrorToBaseFileError(error), local_path, close_callback);
}
// Runs |callback| with |file|.
void RunOpenFileCallback(const OpenFileCallback& callback,
const base::Closure& close_callback,
base::File file) {
callback.Run(std::move(file), close_callback);
}
base::File OpenFile(const base::FilePath& path, int flags) {
return base::File(path, flags);
}
// Part of OpenFile(). Called after FileSystem::OpenFile().
void OpenFileAfterFileSystemOpenFile(int file_flags,
const OpenFileCallback& callback,
FileError error,
const base::FilePath& local_path,
const base::Closure& close_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (error != FILE_ERROR_OK) {
callback.Run(base::File(FileErrorToBaseFileError(error)), base::Closure());
return;
}
// Here, the file should be at |local_path|, but there may be timing issue.
// Because the file is managed by Drive file system, so, in order to avoid
// unexpected file creation, CREATE, OPEN_ALWAYS and CREATE_ALWAYS are
// translated into OPEN or OPEN_TRUNCATED, here. Keep OPEN and OPEN_TRUNCATED
// as is.
if (file_flags & (base::File::FLAG_CREATE |
base::File::FLAG_OPEN_ALWAYS)) {
file_flags &= ~(base::File::FLAG_CREATE |
base::File::FLAG_OPEN_ALWAYS);
file_flags |= base::File::FLAG_OPEN;
} else if (file_flags & base::File::FLAG_CREATE_ALWAYS) {
file_flags &= ~base::File::FLAG_CREATE_ALWAYS;
file_flags |= base::File::FLAG_OPEN_TRUNCATED;
}
// Cache file prepared for modification is available. Open it locally.
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::Bind(&OpenFile, local_path, file_flags),
base::Bind(&RunOpenFileCallback, callback, close_callback));
}
} // namespace
FileSystemInterface* GetFileSystemFromUrl(const storage::FileSystemURL& url) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
Profile* profile = util::ExtractProfileFromPath(url.path());
return profile ? util::GetFileSystemByProfile(profile) : NULL;
}
void RunFileSystemCallback(
const FileSystemGetter& file_system_getter,
const base::Callback<void(FileSystemInterface*)>& callback,
const base::Closure& on_error_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
FileSystemInterface* file_system = file_system_getter.Run();
if (!file_system) {
if (!on_error_callback.is_null())
on_error_callback.Run();
return;
}
callback.Run(file_system);
}
void GetFileInfo(const base::FilePath& file_path,
const GetFileInfoCallback& callback,
FileSystemInterface* file_system) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
file_system->GetResourceEntry(
file_path,
base::Bind(&RunGetFileInfoCallback, callback));
}
void Copy(const base::FilePath& src_file_path,
const base::FilePath& dest_file_path,
bool preserve_last_modified,
const StatusCallback& callback,
FileSystemInterface* file_system) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
file_system->Copy(src_file_path, dest_file_path, preserve_last_modified,
base::Bind(&RunStatusCallbackByFileError, callback));
}
void Move(const base::FilePath& src_file_path,
const base::FilePath& dest_file_path,
const StatusCallback& callback,
FileSystemInterface* file_system) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
file_system->Move(src_file_path, dest_file_path,
base::Bind(&RunStatusCallbackByFileError, callback));
}
void CopyInForeignFile(const base::FilePath& src_foreign_file_path,
const base::FilePath& dest_file_path,
const StatusCallback& callback,
FileSystemInterface* file_system) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
file_system->TransferFileFromLocalToRemote(
src_foreign_file_path, dest_file_path,
base::Bind(&RunStatusCallbackByFileError, callback));
}
void ReadDirectory(const base::FilePath& file_path,
const ReadDirectoryCallback& callback,
FileSystemInterface* file_system) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
file_system->ReadDirectory(
file_path,
base::Bind(&RunReadDirectoryCallbackWithEntries, callback),
base::Bind(&RunReadDirectoryCallbackOnCompletion, callback));
}
void Remove(const base::FilePath& file_path,
bool is_recursive,
const StatusCallback& callback,
FileSystemInterface* file_system) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
file_system->Remove(file_path, is_recursive,
base::Bind(&RunStatusCallbackByFileError, callback));
}
void CreateDirectory(const base::FilePath& file_path,
bool is_exclusive,
bool is_recursive,
const StatusCallback& callback,
FileSystemInterface* file_system) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
file_system->CreateDirectory(
file_path, is_exclusive, is_recursive,
base::Bind(&RunStatusCallbackByFileError, callback));
}
void CreateFile(const base::FilePath& file_path,
bool is_exclusive,
const StatusCallback& callback,
FileSystemInterface* file_system) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
file_system->CreateFile(file_path, is_exclusive,
std::string(), // no mime type; guess from file_path
base::Bind(&RunStatusCallbackByFileError, callback));
}
void Truncate(const base::FilePath& file_path,
int64_t length,
const StatusCallback& callback,
FileSystemInterface* file_system) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
file_system->TruncateFile(
file_path, length,
base::Bind(&RunStatusCallbackByFileError, callback));
}
void CreateSnapshotFile(const base::FilePath& file_path,
const CreateSnapshotFileCallback& callback,
FileSystemInterface* file_system) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
file_system->GetFile(file_path,
base::Bind(&RunCreateSnapshotFileCallback, callback));
}
void CreateWritableSnapshotFile(
const base::FilePath& file_path,
const CreateWritableSnapshotFileCallback& callback,
FileSystemInterface* file_system) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
file_system->OpenFile(
file_path,
OPEN_FILE,
std::string(), // no mime type; we never create a new file here.
base::Bind(&RunCreateWritableSnapshotFileCallback, callback));
}
void OpenFile(const base::FilePath& file_path,
int file_flags,
const OpenFileCallback& callback,
FileSystemInterface* file_system) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Returns an error if any unsupported flag is found.
if (file_flags & ~(base::File::FLAG_OPEN |
base::File::FLAG_CREATE |
base::File::FLAG_OPEN_ALWAYS |
base::File::FLAG_CREATE_ALWAYS |
base::File::FLAG_OPEN_TRUNCATED |
base::File::FLAG_READ |
base::File::FLAG_WRITE |
base::File::FLAG_WRITE_ATTRIBUTES |
base::File::FLAG_APPEND)) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(callback,
Passed(base::File(base::File::FILE_ERROR_FAILED)),
base::Closure()));
return;
}
file_system->OpenFile(
file_path, GetOpenMode(file_flags),
std::string(), // no mime type; guess from file_path
base::Bind(&OpenFileAfterFileSystemOpenFile, file_flags, callback));
}
void TouchFile(const base::FilePath& file_path,
const base::Time& last_access_time,
const base::Time& last_modified_time,
const StatusCallback& callback,
FileSystemInterface* file_system) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
file_system->TouchFile(file_path, last_access_time, last_modified_time,
base::Bind(&RunStatusCallbackByFileError, callback));
}
} // namespace fileapi_internal
} // namespace drive