1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/chromeos/drive/local_file_reader.h" 6 7 #include <errno.h> 8 9 #include "base/bind.h" 10 #include "base/bind_helpers.h" 11 #include "base/files/file_path.h" 12 #include "base/location.h" 13 #include "base/platform_file.h" 14 #include "base/sequenced_task_runner.h" 15 #include "base/task_runner_util.h" 16 #include "net/base/completion_callback.h" 17 #include "net/base/io_buffer.h" 18 #include "net/base/net_errors.h" 19 20 namespace drive { 21 namespace util { 22 23 namespace { 24 25 // Opens the file at |file_path| and seeks to the |offset| from begin. 26 // Returns the net::Error code. If succeeded, |platform_file| is set to point 27 // the opened file. 28 // This function should run on the blocking pool. 29 int OpenAndSeekOnBlockingPool(const base::FilePath& file_path, 30 int64 offset, 31 base::PlatformFile* platform_file) { 32 DCHECK(platform_file); 33 DCHECK_EQ(base::kInvalidPlatformFileValue, *platform_file); 34 35 // First of all, open the file. 36 const int open_flags = base::PLATFORM_FILE_OPEN | 37 base::PLATFORM_FILE_READ | 38 base::PLATFORM_FILE_ASYNC; 39 base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; 40 base::PlatformFile file = 41 base::CreatePlatformFile(file_path, open_flags, NULL, &error); 42 if (file == base::kInvalidPlatformFileValue) { 43 DCHECK_NE(base::PLATFORM_FILE_OK, error); 44 return net::PlatformFileErrorToNetError(error); 45 } 46 47 // If succeeded, seek to the |offset| from begin. 48 int64 position = base::SeekPlatformFile( 49 file, base::PLATFORM_FILE_FROM_BEGIN, offset); 50 if (position < 0) { 51 // If failed, close the file and return an error. 52 base::ClosePlatformFile(file); 53 return net::ERR_FAILED; 54 } 55 56 *platform_file = file; 57 return net::OK; 58 } 59 60 // Reads the data from the |platform_file| and copies it to the |buffer| at 61 // most |buffer_length| size. Returns the number of copied bytes if succeeded, 62 // or the net::Error code. 63 // This function should run on the blocking pool. 64 int ReadOnBlockingPool(base::PlatformFile platform_file, 65 scoped_refptr<net::IOBuffer> buffer, 66 int buffer_length) { 67 DCHECK_NE(base::kInvalidPlatformFileValue, platform_file); 68 int result = base::ReadPlatformFileCurPosNoBestEffort( 69 platform_file, buffer->data(), buffer_length); 70 return result < 0 ? net::MapSystemError(errno) : result; 71 } 72 73 // Posts a task to close the |platform_file| into |task_runner|. 74 // Or, if |platform_file| is kInvalidPlatformFileValue, does nothing. 75 void PostCloseIfNeeded(base::TaskRunner* task_runner, 76 base::PlatformFile platform_file) { 77 DCHECK(task_runner); 78 if (platform_file != base::kInvalidPlatformFileValue) { 79 task_runner->PostTask( 80 FROM_HERE, 81 base::Bind( 82 base::IgnoreResult(&base::ClosePlatformFile), platform_file)); 83 } 84 } 85 86 } // namespace 87 88 class LocalFileReader::ScopedPlatformFile { 89 public: 90 explicit ScopedPlatformFile(base::TaskRunner* task_runner) 91 : task_runner_(task_runner), 92 platform_file_(base::kInvalidPlatformFileValue) { 93 DCHECK(task_runner); 94 } 95 96 ~ScopedPlatformFile() { 97 PostCloseIfNeeded(task_runner_.get(), platform_file_); 98 } 99 100 base::PlatformFile* ptr() { return &platform_file_; } 101 102 base::PlatformFile release() { 103 base::PlatformFile result = platform_file_; 104 platform_file_ = base::kInvalidPlatformFileValue; 105 return result; 106 } 107 108 private: 109 scoped_refptr<base::TaskRunner> task_runner_; 110 base::PlatformFile platform_file_; 111 112 DISALLOW_COPY_AND_ASSIGN(ScopedPlatformFile); 113 }; 114 115 LocalFileReader::LocalFileReader( 116 base::SequencedTaskRunner* sequenced_task_runner) 117 : sequenced_task_runner_(sequenced_task_runner), 118 platform_file_(base::kInvalidPlatformFileValue), 119 weak_ptr_factory_(this) { 120 DCHECK(sequenced_task_runner_.get()); 121 } 122 123 LocalFileReader::~LocalFileReader() { 124 PostCloseIfNeeded(sequenced_task_runner_.get(), platform_file_); 125 } 126 127 void LocalFileReader::Open(const base::FilePath& file_path, 128 int64 offset, 129 const net::CompletionCallback& callback) { 130 DCHECK(!callback.is_null()); 131 DCHECK_EQ(base::kInvalidPlatformFileValue, platform_file_); 132 133 ScopedPlatformFile* platform_file = 134 new ScopedPlatformFile(sequenced_task_runner_.get()); 135 base::PostTaskAndReplyWithResult( 136 sequenced_task_runner_.get(), 137 FROM_HERE, 138 base::Bind( 139 &OpenAndSeekOnBlockingPool, file_path, offset, platform_file->ptr()), 140 base::Bind(&LocalFileReader::OpenAfterBlockingPoolTask, 141 weak_ptr_factory_.GetWeakPtr(), 142 callback, 143 base::Owned(platform_file))); 144 } 145 146 void LocalFileReader::Read(net::IOBuffer* in_buffer, 147 int buffer_length, 148 const net::CompletionCallback& callback) { 149 DCHECK(!callback.is_null()); 150 DCHECK_NE(base::kInvalidPlatformFileValue, platform_file_); 151 152 scoped_refptr<net::IOBuffer> buffer(in_buffer); 153 base::PostTaskAndReplyWithResult( 154 sequenced_task_runner_.get(), 155 FROM_HERE, 156 base::Bind(&ReadOnBlockingPool, platform_file_, buffer, buffer_length), 157 callback); 158 } 159 160 void LocalFileReader::OpenAfterBlockingPoolTask( 161 const net::CompletionCallback& callback, 162 ScopedPlatformFile* platform_file, 163 int open_result) { 164 DCHECK(!callback.is_null()); 165 DCHECK(platform_file); 166 DCHECK_EQ(base::kInvalidPlatformFileValue, platform_file_); 167 168 if (open_result == net::OK) { 169 DCHECK_NE(base::kInvalidPlatformFileValue, *platform_file->ptr()); 170 platform_file_ = platform_file->release(); 171 } 172 173 callback.Run(open_result); 174 } 175 176 } // namespace util 177 } // namespace drive 178