blob: 036fd196c60e186c7b36b66f6b77885ece109191 [file] [log] [blame]
// Copyright (c) 2012 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 "net/base/file_stream.h"
#include "base/location.h"
#include "base/message_loop_proxy.h"
#include "base/task_runner_util.h"
#include "base/threading/thread_restrictions.h"
#include "base/threading/worker_pool.h"
#include "net/base/file_stream_context.h"
#include "net/base/file_stream_net_log_parameters.h"
#include "net/base/net_errors.h"
namespace net {
FileStream::FileStream(NetLog* net_log)
/* To allow never opened stream to be destroyed on any thread we set flags
as if stream was opened asynchronously. */
: open_flags_(base::PLATFORM_FILE_ASYNC),
bound_net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_FILESTREAM)),
context_(new Context(bound_net_log_)) {
bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE);
}
FileStream::FileStream(base::PlatformFile file, int flags, NetLog* net_log)
: open_flags_(flags),
bound_net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_FILESTREAM)),
context_(new Context(file, bound_net_log_, open_flags_)) {
bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE);
}
FileStream::~FileStream() {
if (!is_async()) {
base::ThreadRestrictions::AssertIOAllowed();
context_->CloseSync();
context_.reset();
} else {
context_.release()->Orphan();
}
bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_ALIVE);
}
int FileStream::Open(const FilePath& path, int open_flags,
const CompletionCallback& callback) {
if (IsOpen()) {
DLOG(FATAL) << "File is already open!";
return ERR_UNEXPECTED;
}
open_flags_ = open_flags;
DCHECK(is_async());
context_->OpenAsync(path, open_flags, callback);
return ERR_IO_PENDING;
}
int FileStream::OpenSync(const FilePath& path, int open_flags) {
base::ThreadRestrictions::AssertIOAllowed();
if (IsOpen()) {
DLOG(FATAL) << "File is already open!";
return ERR_UNEXPECTED;
}
open_flags_ = open_flags;
// TODO(satorux): Put a DCHECK once all async clients are migrated
// to use Open(). crbug.com/114783
//
// DCHECK(!is_async());
return context_->OpenSync(path, open_flags_);
}
bool FileStream::IsOpen() const {
return context_->file() != base::kInvalidPlatformFileValue;
}
int FileStream::Seek(Whence whence,
int64 offset,
const Int64CompletionCallback& callback) {
if (!IsOpen())
return ERR_UNEXPECTED;
// Make sure we're async.
DCHECK(is_async());
context_->SeekAsync(whence, offset, callback);
return ERR_IO_PENDING;
}
int64 FileStream::SeekSync(Whence whence, int64 offset) {
base::ThreadRestrictions::AssertIOAllowed();
if (!IsOpen())
return ERR_UNEXPECTED;
// If we're in async, make sure we don't have a request in flight.
DCHECK(!is_async() || !context_->async_in_progress());
return context_->SeekSync(whence, offset);
}
int64 FileStream::Available() {
base::ThreadRestrictions::AssertIOAllowed();
if (!IsOpen())
return ERR_UNEXPECTED;
int64 cur_pos = SeekSync(FROM_CURRENT, 0);
if (cur_pos < 0)
return cur_pos;
int64 size = context_->GetFileSize();
if (size < 0)
return size;
DCHECK_GE(size, cur_pos);
return size - cur_pos;
}
int FileStream::Read(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) {
if (!IsOpen())
return ERR_UNEXPECTED;
// read(..., 0) will return 0, which indicates end-of-file.
DCHECK_GT(buf_len, 0);
DCHECK(open_flags_ & base::PLATFORM_FILE_READ);
DCHECK(is_async());
return context_->ReadAsync(buf, buf_len, callback);
}
int FileStream::ReadSync(char* buf, int buf_len) {
base::ThreadRestrictions::AssertIOAllowed();
if (!IsOpen())
return ERR_UNEXPECTED;
DCHECK(!is_async());
// read(..., 0) will return 0, which indicates end-of-file.
DCHECK_GT(buf_len, 0);
DCHECK(open_flags_ & base::PLATFORM_FILE_READ);
return context_->ReadSync(buf, buf_len);
}
int FileStream::ReadUntilComplete(char *buf, int buf_len) {
base::ThreadRestrictions::AssertIOAllowed();
int to_read = buf_len;
int bytes_total = 0;
do {
int bytes_read = ReadSync(buf, to_read);
if (bytes_read <= 0) {
if (bytes_total == 0)
return bytes_read;
return bytes_total;
}
bytes_total += bytes_read;
buf += bytes_read;
to_read -= bytes_read;
} while (bytes_total < buf_len);
return bytes_total;
}
int FileStream::Write(IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) {
if (!IsOpen())
return ERR_UNEXPECTED;
DCHECK(is_async());
DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
// write(..., 0) will return 0, which indicates end-of-file.
DCHECK_GT(buf_len, 0);
return context_->WriteAsync(buf, buf_len, callback);
}
int FileStream::WriteSync(const char* buf, int buf_len) {
base::ThreadRestrictions::AssertIOAllowed();
if (!IsOpen())
return ERR_UNEXPECTED;
DCHECK(!is_async());
DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
// write(..., 0) will return 0, which indicates end-of-file.
DCHECK_GT(buf_len, 0);
return context_->WriteSync(buf, buf_len);
}
int64 FileStream::Truncate(int64 bytes) {
base::ThreadRestrictions::AssertIOAllowed();
if (!IsOpen())
return ERR_UNEXPECTED;
// We'd better be open for writing.
DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
// Seek to the position to truncate from.
int64 seek_position = SeekSync(FROM_BEGIN, bytes);
if (seek_position != bytes)
return ERR_UNEXPECTED;
// And truncate the file.
return context_->Truncate(bytes);
}
int FileStream::Flush(const CompletionCallback& callback) {
if (!IsOpen())
return ERR_UNEXPECTED;
DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
// Make sure we're async.
DCHECK(is_async());
context_->FlushAsync(callback);
return ERR_IO_PENDING;
}
int FileStream::FlushSync() {
base::ThreadRestrictions::AssertIOAllowed();
if (!IsOpen())
return ERR_UNEXPECTED;
DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
return context_->FlushSync();
}
void FileStream::EnableErrorStatistics() {
context_->set_record_uma(true);
}
void FileStream::SetBoundNetLogSource(const BoundNetLog& owner_bound_net_log) {
if ((owner_bound_net_log.source().id == NetLog::Source::kInvalidId) &&
(bound_net_log_.source().id == NetLog::Source::kInvalidId)) {
// Both |BoundNetLog|s are invalid.
return;
}
// Should never connect to itself.
DCHECK_NE(bound_net_log_.source().id, owner_bound_net_log.source().id);
bound_net_log_.AddEvent(NetLog::TYPE_FILE_STREAM_BOUND_TO_OWNER,
owner_bound_net_log.source().ToEventParametersCallback());
owner_bound_net_log.AddEvent(NetLog::TYPE_FILE_STREAM_SOURCE,
bound_net_log_.source().ToEventParametersCallback());
}
base::PlatformFile FileStream::GetPlatformFileForTesting() {
return context_->file();
}
} // namespace net