359 lines
10 KiB
C++
359 lines
10 KiB
C++
|
// 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 "base/files/file_proxy.h"
|
||
|
|
||
|
#include <utility>
|
||
|
|
||
|
#include "base/bind.h"
|
||
|
#include "base/bind_helpers.h"
|
||
|
#include "base/files/file.h"
|
||
|
#include "base/files/file_util.h"
|
||
|
#include "base/location.h"
|
||
|
#include "base/macros.h"
|
||
|
#include "base/task_runner.h"
|
||
|
#include "base/task_runner_util.h"
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
void FileDeleter(base::File file) {
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
namespace base {
|
||
|
|
||
|
class FileHelper {
|
||
|
public:
|
||
|
FileHelper(FileProxy* proxy, File file)
|
||
|
: file_(std::move(file)),
|
||
|
error_(File::FILE_ERROR_FAILED),
|
||
|
task_runner_(proxy->task_runner()),
|
||
|
proxy_(AsWeakPtr(proxy)) {
|
||
|
}
|
||
|
|
||
|
void PassFile() {
|
||
|
if (proxy_)
|
||
|
proxy_->SetFile(std::move(file_));
|
||
|
else if (file_.IsValid())
|
||
|
task_runner_->PostTask(FROM_HERE,
|
||
|
BindOnce(&FileDeleter, std::move(file_)));
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
File file_;
|
||
|
File::Error error_;
|
||
|
|
||
|
private:
|
||
|
scoped_refptr<TaskRunner> task_runner_;
|
||
|
WeakPtr<FileProxy> proxy_;
|
||
|
DISALLOW_COPY_AND_ASSIGN(FileHelper);
|
||
|
};
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
class GenericFileHelper : public FileHelper {
|
||
|
public:
|
||
|
GenericFileHelper(FileProxy* proxy, File file)
|
||
|
: FileHelper(proxy, std::move(file)) {
|
||
|
}
|
||
|
|
||
|
void Close() {
|
||
|
file_.Close();
|
||
|
error_ = File::FILE_OK;
|
||
|
}
|
||
|
|
||
|
void SetTimes(Time last_access_time, Time last_modified_time) {
|
||
|
bool rv = file_.SetTimes(last_access_time, last_modified_time);
|
||
|
error_ = rv ? File::FILE_OK : File::FILE_ERROR_FAILED;
|
||
|
}
|
||
|
|
||
|
void SetLength(int64_t length) {
|
||
|
if (file_.SetLength(length))
|
||
|
error_ = File::FILE_OK;
|
||
|
}
|
||
|
|
||
|
void Flush() {
|
||
|
if (file_.Flush())
|
||
|
error_ = File::FILE_OK;
|
||
|
}
|
||
|
|
||
|
void Reply(FileProxy::StatusCallback callback) {
|
||
|
PassFile();
|
||
|
if (!callback.is_null())
|
||
|
std::move(callback).Run(error_);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
DISALLOW_COPY_AND_ASSIGN(GenericFileHelper);
|
||
|
};
|
||
|
|
||
|
class CreateOrOpenHelper : public FileHelper {
|
||
|
public:
|
||
|
CreateOrOpenHelper(FileProxy* proxy, File file)
|
||
|
: FileHelper(proxy, std::move(file)) {
|
||
|
}
|
||
|
|
||
|
void RunWork(const FilePath& file_path, int file_flags) {
|
||
|
file_.Initialize(file_path, file_flags);
|
||
|
error_ = file_.IsValid() ? File::FILE_OK : file_.error_details();
|
||
|
}
|
||
|
|
||
|
void Reply(FileProxy::StatusCallback callback) {
|
||
|
DCHECK(!callback.is_null());
|
||
|
PassFile();
|
||
|
std::move(callback).Run(error_);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
DISALLOW_COPY_AND_ASSIGN(CreateOrOpenHelper);
|
||
|
};
|
||
|
|
||
|
class CreateTemporaryHelper : public FileHelper {
|
||
|
public:
|
||
|
CreateTemporaryHelper(FileProxy* proxy, File file)
|
||
|
: FileHelper(proxy, std::move(file)) {
|
||
|
}
|
||
|
|
||
|
void RunWork(uint32_t additional_file_flags) {
|
||
|
// TODO(darin): file_util should have a variant of CreateTemporaryFile
|
||
|
// that returns a FilePath and a File.
|
||
|
if (!CreateTemporaryFile(&file_path_)) {
|
||
|
// TODO(davidben): base::CreateTemporaryFile should preserve the error
|
||
|
// code.
|
||
|
error_ = File::FILE_ERROR_FAILED;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
uint32_t file_flags = File::FLAG_WRITE | File::FLAG_TEMPORARY |
|
||
|
File::FLAG_CREATE_ALWAYS | additional_file_flags;
|
||
|
|
||
|
file_.Initialize(file_path_, file_flags);
|
||
|
if (file_.IsValid()) {
|
||
|
error_ = File::FILE_OK;
|
||
|
} else {
|
||
|
error_ = file_.error_details();
|
||
|
DeleteFile(file_path_, false);
|
||
|
file_path_.clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Reply(FileProxy::CreateTemporaryCallback callback) {
|
||
|
DCHECK(!callback.is_null());
|
||
|
PassFile();
|
||
|
std::move(callback).Run(error_, file_path_);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
FilePath file_path_;
|
||
|
DISALLOW_COPY_AND_ASSIGN(CreateTemporaryHelper);
|
||
|
};
|
||
|
|
||
|
class GetInfoHelper : public FileHelper {
|
||
|
public:
|
||
|
GetInfoHelper(FileProxy* proxy, File file)
|
||
|
: FileHelper(proxy, std::move(file)) {
|
||
|
}
|
||
|
|
||
|
void RunWork() {
|
||
|
if (file_.GetInfo(&file_info_))
|
||
|
error_ = File::FILE_OK;
|
||
|
}
|
||
|
|
||
|
void Reply(FileProxy::GetFileInfoCallback callback) {
|
||
|
PassFile();
|
||
|
DCHECK(!callback.is_null());
|
||
|
std::move(callback).Run(error_, file_info_);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
File::Info file_info_;
|
||
|
DISALLOW_COPY_AND_ASSIGN(GetInfoHelper);
|
||
|
};
|
||
|
|
||
|
class ReadHelper : public FileHelper {
|
||
|
public:
|
||
|
ReadHelper(FileProxy* proxy, File file, int bytes_to_read)
|
||
|
: FileHelper(proxy, std::move(file)),
|
||
|
buffer_(new char[bytes_to_read]),
|
||
|
bytes_to_read_(bytes_to_read),
|
||
|
bytes_read_(0) {
|
||
|
}
|
||
|
|
||
|
void RunWork(int64_t offset) {
|
||
|
bytes_read_ = file_.Read(offset, buffer_.get(), bytes_to_read_);
|
||
|
error_ = (bytes_read_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK;
|
||
|
}
|
||
|
|
||
|
void Reply(FileProxy::ReadCallback callback) {
|
||
|
PassFile();
|
||
|
DCHECK(!callback.is_null());
|
||
|
std::move(callback).Run(error_, buffer_.get(), bytes_read_);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
std::unique_ptr<char[]> buffer_;
|
||
|
int bytes_to_read_;
|
||
|
int bytes_read_;
|
||
|
DISALLOW_COPY_AND_ASSIGN(ReadHelper);
|
||
|
};
|
||
|
|
||
|
class WriteHelper : public FileHelper {
|
||
|
public:
|
||
|
WriteHelper(FileProxy* proxy,
|
||
|
File file,
|
||
|
const char* buffer, int bytes_to_write)
|
||
|
: FileHelper(proxy, std::move(file)),
|
||
|
buffer_(new char[bytes_to_write]),
|
||
|
bytes_to_write_(bytes_to_write),
|
||
|
bytes_written_(0) {
|
||
|
memcpy(buffer_.get(), buffer, bytes_to_write);
|
||
|
}
|
||
|
|
||
|
void RunWork(int64_t offset) {
|
||
|
bytes_written_ = file_.Write(offset, buffer_.get(), bytes_to_write_);
|
||
|
error_ = (bytes_written_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK;
|
||
|
}
|
||
|
|
||
|
void Reply(FileProxy::WriteCallback callback) {
|
||
|
PassFile();
|
||
|
if (!callback.is_null())
|
||
|
std::move(callback).Run(error_, bytes_written_);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
std::unique_ptr<char[]> buffer_;
|
||
|
int bytes_to_write_;
|
||
|
int bytes_written_;
|
||
|
DISALLOW_COPY_AND_ASSIGN(WriteHelper);
|
||
|
};
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
FileProxy::FileProxy(TaskRunner* task_runner) : task_runner_(task_runner) {
|
||
|
}
|
||
|
|
||
|
FileProxy::~FileProxy() {
|
||
|
if (file_.IsValid())
|
||
|
task_runner_->PostTask(FROM_HERE, BindOnce(&FileDeleter, std::move(file_)));
|
||
|
}
|
||
|
|
||
|
bool FileProxy::CreateOrOpen(const FilePath& file_path,
|
||
|
uint32_t file_flags,
|
||
|
StatusCallback callback) {
|
||
|
DCHECK(!file_.IsValid());
|
||
|
CreateOrOpenHelper* helper = new CreateOrOpenHelper(this, File());
|
||
|
return task_runner_->PostTaskAndReply(
|
||
|
FROM_HERE,
|
||
|
BindOnce(&CreateOrOpenHelper::RunWork, Unretained(helper), file_path,
|
||
|
file_flags),
|
||
|
BindOnce(&CreateOrOpenHelper::Reply, Owned(helper), std::move(callback)));
|
||
|
}
|
||
|
|
||
|
bool FileProxy::CreateTemporary(uint32_t additional_file_flags,
|
||
|
CreateTemporaryCallback callback) {
|
||
|
DCHECK(!file_.IsValid());
|
||
|
CreateTemporaryHelper* helper = new CreateTemporaryHelper(this, File());
|
||
|
return task_runner_->PostTaskAndReply(
|
||
|
FROM_HERE,
|
||
|
BindOnce(&CreateTemporaryHelper::RunWork, Unretained(helper),
|
||
|
additional_file_flags),
|
||
|
BindOnce(&CreateTemporaryHelper::Reply, Owned(helper),
|
||
|
std::move(callback)));
|
||
|
}
|
||
|
|
||
|
bool FileProxy::IsValid() const {
|
||
|
return file_.IsValid();
|
||
|
}
|
||
|
|
||
|
void FileProxy::SetFile(File file) {
|
||
|
DCHECK(!file_.IsValid());
|
||
|
file_ = std::move(file);
|
||
|
}
|
||
|
|
||
|
File FileProxy::TakeFile() {
|
||
|
return std::move(file_);
|
||
|
}
|
||
|
|
||
|
File FileProxy::DuplicateFile() {
|
||
|
return file_.Duplicate();
|
||
|
}
|
||
|
|
||
|
PlatformFile FileProxy::GetPlatformFile() const {
|
||
|
return file_.GetPlatformFile();
|
||
|
}
|
||
|
|
||
|
bool FileProxy::Close(StatusCallback callback) {
|
||
|
DCHECK(file_.IsValid());
|
||
|
GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_));
|
||
|
return task_runner_->PostTaskAndReply(
|
||
|
FROM_HERE, BindOnce(&GenericFileHelper::Close, Unretained(helper)),
|
||
|
BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback)));
|
||
|
}
|
||
|
|
||
|
bool FileProxy::GetInfo(GetFileInfoCallback callback) {
|
||
|
DCHECK(file_.IsValid());
|
||
|
GetInfoHelper* helper = new GetInfoHelper(this, std::move(file_));
|
||
|
return task_runner_->PostTaskAndReply(
|
||
|
FROM_HERE, BindOnce(&GetInfoHelper::RunWork, Unretained(helper)),
|
||
|
BindOnce(&GetInfoHelper::Reply, Owned(helper), std::move(callback)));
|
||
|
}
|
||
|
|
||
|
bool FileProxy::Read(int64_t offset, int bytes_to_read, ReadCallback callback) {
|
||
|
DCHECK(file_.IsValid());
|
||
|
if (bytes_to_read < 0)
|
||
|
return false;
|
||
|
|
||
|
ReadHelper* helper = new ReadHelper(this, std::move(file_), bytes_to_read);
|
||
|
return task_runner_->PostTaskAndReply(
|
||
|
FROM_HERE, BindOnce(&ReadHelper::RunWork, Unretained(helper), offset),
|
||
|
BindOnce(&ReadHelper::Reply, Owned(helper), std::move(callback)));
|
||
|
}
|
||
|
|
||
|
bool FileProxy::Write(int64_t offset,
|
||
|
const char* buffer,
|
||
|
int bytes_to_write,
|
||
|
WriteCallback callback) {
|
||
|
DCHECK(file_.IsValid());
|
||
|
if (bytes_to_write <= 0 || buffer == nullptr)
|
||
|
return false;
|
||
|
|
||
|
WriteHelper* helper =
|
||
|
new WriteHelper(this, std::move(file_), buffer, bytes_to_write);
|
||
|
return task_runner_->PostTaskAndReply(
|
||
|
FROM_HERE, BindOnce(&WriteHelper::RunWork, Unretained(helper), offset),
|
||
|
BindOnce(&WriteHelper::Reply, Owned(helper), std::move(callback)));
|
||
|
}
|
||
|
|
||
|
bool FileProxy::SetTimes(Time last_access_time,
|
||
|
Time last_modified_time,
|
||
|
StatusCallback callback) {
|
||
|
DCHECK(file_.IsValid());
|
||
|
GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_));
|
||
|
return task_runner_->PostTaskAndReply(
|
||
|
FROM_HERE,
|
||
|
BindOnce(&GenericFileHelper::SetTimes, Unretained(helper),
|
||
|
last_access_time, last_modified_time),
|
||
|
BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback)));
|
||
|
}
|
||
|
|
||
|
bool FileProxy::SetLength(int64_t length, StatusCallback callback) {
|
||
|
DCHECK(file_.IsValid());
|
||
|
GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_));
|
||
|
return task_runner_->PostTaskAndReply(
|
||
|
FROM_HERE,
|
||
|
BindOnce(&GenericFileHelper::SetLength, Unretained(helper), length),
|
||
|
BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback)));
|
||
|
}
|
||
|
|
||
|
bool FileProxy::Flush(StatusCallback callback) {
|
||
|
DCHECK(file_.IsValid());
|
||
|
GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_));
|
||
|
return task_runner_->PostTaskAndReply(
|
||
|
FROM_HERE, BindOnce(&GenericFileHelper::Flush, Unretained(helper)),
|
||
|
BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback)));
|
||
|
}
|
||
|
|
||
|
} // namespace base
|