// Copyright 2013 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/local_file_reader.h" #include #include "base/bind.h" #include "base/bind_helpers.h" #include "base/files/file_path.h" #include "base/location.h" #include "base/platform_file.h" #include "base/sequenced_task_runner.h" #include "base/task_runner_util.h" #include "net/base/completion_callback.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" namespace drive { namespace util { namespace { // Opens the file at |file_path| and seeks to the |offset| from begin. // Returns the net::Error code. If succeeded, |platform_file| is set to point // the opened file. // This function should run on the blocking pool. int OpenAndSeekOnBlockingPool(const base::FilePath& file_path, int64 offset, base::PlatformFile* platform_file) { DCHECK(platform_file); DCHECK_EQ(base::kInvalidPlatformFileValue, *platform_file); // First of all, open the file. const int open_flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ | base::PLATFORM_FILE_ASYNC; base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; base::PlatformFile file = base::CreatePlatformFile(file_path, open_flags, NULL, &error); if (file == base::kInvalidPlatformFileValue) { DCHECK_NE(base::PLATFORM_FILE_OK, error); return net::FileErrorToNetError(static_cast(error)); } // If succeeded, seek to the |offset| from begin. int64 position = base::SeekPlatformFile( file, base::PLATFORM_FILE_FROM_BEGIN, offset); if (position < 0) { // If failed, close the file and return an error. base::ClosePlatformFile(file); return net::ERR_FAILED; } *platform_file = file; return net::OK; } // Reads the data from the |platform_file| and copies it to the |buffer| at // most |buffer_length| size. Returns the number of copied bytes if succeeded, // or the net::Error code. // This function should run on the blocking pool. int ReadOnBlockingPool(base::PlatformFile platform_file, scoped_refptr buffer, int buffer_length) { DCHECK_NE(base::kInvalidPlatformFileValue, platform_file); int result = base::ReadPlatformFileCurPosNoBestEffort( platform_file, buffer->data(), buffer_length); return result < 0 ? net::MapSystemError(errno) : result; } // Posts a task to close the |platform_file| into |task_runner|. // Or, if |platform_file| is kInvalidPlatformFileValue, does nothing. void PostCloseIfNeeded(base::TaskRunner* task_runner, base::PlatformFile platform_file) { DCHECK(task_runner); if (platform_file != base::kInvalidPlatformFileValue) { task_runner->PostTask( FROM_HERE, base::Bind( base::IgnoreResult(&base::ClosePlatformFile), platform_file)); } } } // namespace class LocalFileReader::ScopedPlatformFile { public: explicit ScopedPlatformFile(base::TaskRunner* task_runner) : task_runner_(task_runner), platform_file_(base::kInvalidPlatformFileValue) { DCHECK(task_runner); } ~ScopedPlatformFile() { PostCloseIfNeeded(task_runner_.get(), platform_file_); } base::PlatformFile* ptr() { return &platform_file_; } base::PlatformFile release() { base::PlatformFile result = platform_file_; platform_file_ = base::kInvalidPlatformFileValue; return result; } private: scoped_refptr task_runner_; base::PlatformFile platform_file_; DISALLOW_COPY_AND_ASSIGN(ScopedPlatformFile); }; LocalFileReader::LocalFileReader( base::SequencedTaskRunner* sequenced_task_runner) : sequenced_task_runner_(sequenced_task_runner), platform_file_(base::kInvalidPlatformFileValue), weak_ptr_factory_(this) { DCHECK(sequenced_task_runner_.get()); } LocalFileReader::~LocalFileReader() { PostCloseIfNeeded(sequenced_task_runner_.get(), platform_file_); } void LocalFileReader::Open(const base::FilePath& file_path, int64 offset, const net::CompletionCallback& callback) { DCHECK(!callback.is_null()); DCHECK_EQ(base::kInvalidPlatformFileValue, platform_file_); ScopedPlatformFile* platform_file = new ScopedPlatformFile(sequenced_task_runner_.get()); base::PostTaskAndReplyWithResult( sequenced_task_runner_.get(), FROM_HERE, base::Bind( &OpenAndSeekOnBlockingPool, file_path, offset, platform_file->ptr()), base::Bind(&LocalFileReader::OpenAfterBlockingPoolTask, weak_ptr_factory_.GetWeakPtr(), callback, base::Owned(platform_file))); } void LocalFileReader::Read(net::IOBuffer* in_buffer, int buffer_length, const net::CompletionCallback& callback) { DCHECK(!callback.is_null()); DCHECK_NE(base::kInvalidPlatformFileValue, platform_file_); scoped_refptr buffer(in_buffer); base::PostTaskAndReplyWithResult( sequenced_task_runner_.get(), FROM_HERE, base::Bind(&ReadOnBlockingPool, platform_file_, buffer, buffer_length), callback); } void LocalFileReader::OpenAfterBlockingPoolTask( const net::CompletionCallback& callback, ScopedPlatformFile* platform_file, int open_result) { DCHECK(!callback.is_null()); DCHECK(platform_file); DCHECK_EQ(base::kInvalidPlatformFileValue, platform_file_); if (open_result == net::OK) { DCHECK_NE(base::kInvalidPlatformFileValue, *platform_file->ptr()); platform_file_ = platform_file->release(); } callback.Run(open_result); } } // namespace util } // namespace drive