// 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/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, const scoped_refptr& task_runner) /* 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_, task_runner)) { bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE); } 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_, base::WorkerPool::GetTaskRunner(true /* slow */))) { bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE); } FileStream::FileStream(base::PlatformFile file, int flags, NetLog* net_log, const scoped_refptr& task_runner) : open_flags_(flags), bound_net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_FILESTREAM)), context_(new Context(file, bound_net_log_, open_flags_, task_runner)) { 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_, base::WorkerPool::GetTaskRunner(true /* slow */))) { 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 base::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 base::FilePath& path, int open_flags) { base::ThreadRestrictions::AssertIOAllowed(); if (IsOpen()) { DLOG(FATAL) << "File is already open!"; return ERR_UNEXPECTED; } open_flags_ = open_flags; 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