text: // 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<base::TaskRunner>& task_runner)
        /* To allow never opened stream to be destroyed on any thread we set flags
           as if stream was opened asynchronously. */
      : 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. */
      : 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<base::TaskRunner>& task_runner)
      : bound_net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_FILESTREAM)),
        context_(new Context(base::File(file), flags, bound_net_log_,
                             task_runner)) {
    bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE);
  }
  
  FileStream::FileStream(base::PlatformFile file, int flags, NetLog* net_log)
      : bound_net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_FILESTREAM)),
        context_(new Context(base::File(file), flags, bound_net_log_,
                             base::WorkerPool::GetTaskRunner(true /* slow */))) {
    bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE);
  }
  
  FileStream::FileStream(base::File file,
                         net::NetLog* net_log,
                         const scoped_refptr<base::TaskRunner>& task_runner)
      : bound_net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_FILESTREAM)),
        context_(new Context(file.Pass(), bound_net_log_, task_runner)) {
    bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE);
  }
  
  FileStream::FileStream(base::File file, net::NetLog* net_log)
      : bound_net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_FILESTREAM)),
        context_(new Context(file.Pass(), bound_net_log_,
                             base::WorkerPool::GetTaskRunner(true /* slow */))) {
    bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE);
  }
  
  FileStream::~FileStream() {
    if (context_->async()) {
      context_.release()->Orphan();
    } else {
      context_->CloseSync();
      context_.reset();
    }
  
    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;
    }
  
    DCHECK(open_flags & base::File::FLAG_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;
    }
  
    DCHECK(!context_->async());
    return context_->OpenSync(path, open_flags);
  }
  
  int FileStream::Close(const CompletionCallback& callback) {
    DCHECK(context_->async());
    context_->CloseAsync(callback);
    return ERR_IO_PENDING;
  }
  
  int FileStream::CloseSync() {
    DCHECK(!context_->async());
    base::ThreadRestrictions::AssertIOAllowed();
    context_->CloseSync();
    return OK;
  }
  
  bool FileStream::IsOpen() const {
    return context_->file().IsValid();
  }
  
  int FileStream::Seek(Whence whence,
                       int64 offset,
                       const Int64CompletionCallback& callback) {
    if (!IsOpen())
      return ERR_UNEXPECTED;
  
    // Make sure we're async.
    DCHECK(context_->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(!context_->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(context_->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(!context_->async());
    // read(..., 0) will return 0, which indicates end-of-file.
    DCHECK_GT(buf_len, 0);
  
    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(context_->async());
    // 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(!context_->async());
    // 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;
  
    // 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;
  
    // Make sure we're async.
    DCHECK(context_->async());
  
    context_->FlushAsync(callback);
    return ERR_IO_PENDING;
  }
  
  int FileStream::FlushSync() {
    base::ThreadRestrictions::AssertIOAllowed();
  
    if (!IsOpen())
      return ERR_UNEXPECTED;
  
    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());
  }
  
  const base::File& FileStream::GetFileForTesting() const {
    return context_->file();
  }
  
  }  // namespace net
  