1 // Copyright 2016 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/arc/fileapi/arc_content_file_system_file_stream_reader.h"
6
7 #include <sys/types.h>
8 #include <unistd.h>
9
10 #include <utility>
11
12 #include "base/bind.h"
13 #include "base/callback_helpers.h"
14 #include "base/files/file.h"
15 #include "base/task/post_task.h"
16 #include "base/task/thread_pool.h"
17 #include "base/threading/scoped_blocking_call.h"
18 #include "chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_util.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "mojo/public/cpp/platform/platform_handle.h"
21 #include "mojo/public/cpp/system/platform_handle.h"
22 #include "net/base/io_buffer.h"
23 #include "net/base/net_errors.h"
24
25 namespace arc {
26
27 namespace {
28
29 // Calls base::File::ReadAtCurrentPosNoBestEffort with the given buffer.
ReadFile(base::File * file,scoped_refptr<net::IOBuffer> buffer,int buffer_length)30 int ReadFile(base::File* file,
31 scoped_refptr<net::IOBuffer> buffer,
32 int buffer_length) {
33 return file->ReadAtCurrentPosNoBestEffort(buffer->data(), buffer_length);
34 }
35
36 // Seeks the file, returns 0 on success, or errno on an error.
SeekFile(base::File * file,size_t offset)37 int SeekFile(base::File* file, size_t offset) {
38 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
39 base::BlockingType::MAY_BLOCK);
40 // lseek() instead of |file|'s method for errno.
41 off_t result = lseek(file->GetPlatformFile(), offset, SEEK_SET);
42 return result < 0 ? errno : 0;
43 }
44
45 } // namespace
46
ArcContentFileSystemFileStreamReader(const GURL & arc_url,int64_t offset)47 ArcContentFileSystemFileStreamReader::ArcContentFileSystemFileStreamReader(
48 const GURL& arc_url,
49 int64_t offset)
50 : arc_url_(arc_url), offset_(offset) {
51 task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
52 {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
53 }
54
~ArcContentFileSystemFileStreamReader()55 ArcContentFileSystemFileStreamReader::~ArcContentFileSystemFileStreamReader() {
56 // Use the task runner to destruct |file_| after the completion of all
57 // in-flight operations.
58 task_runner_->PostTask(
59 FROM_HERE,
60 base::BindOnce(&base::DeletePointer<base::File>, file_.release()));
61 }
62
Read(net::IOBuffer * buffer,int buffer_length,net::CompletionOnceCallback callback)63 int ArcContentFileSystemFileStreamReader::Read(
64 net::IOBuffer* buffer,
65 int buffer_length,
66 net::CompletionOnceCallback callback) {
67 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
68 if (file_) {
69 ReadInternal(buffer, buffer_length, std::move(callback));
70 return net::ERR_IO_PENDING;
71 }
72 file_system_operation_runner_util::OpenFileToReadOnIOThread(
73 arc_url_,
74 base::BindOnce(&ArcContentFileSystemFileStreamReader::OnOpenFile,
75 weak_ptr_factory_.GetWeakPtr(),
76 base::WrapRefCounted(buffer), buffer_length,
77 std::move(callback)));
78 return net::ERR_IO_PENDING;
79 }
80
GetLength(net::Int64CompletionOnceCallback callback)81 int64_t ArcContentFileSystemFileStreamReader::GetLength(
82 net::Int64CompletionOnceCallback callback) {
83 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
84 file_system_operation_runner_util::GetFileSizeOnIOThread(
85 arc_url_,
86 base::BindOnce(&ArcContentFileSystemFileStreamReader::OnGetFileSize,
87 weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
88 return net::ERR_IO_PENDING;
89 }
90
ReadInternal(net::IOBuffer * buffer,int buffer_length,net::CompletionOnceCallback callback)91 void ArcContentFileSystemFileStreamReader::ReadInternal(
92 net::IOBuffer* buffer,
93 int buffer_length,
94 net::CompletionOnceCallback callback) {
95 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
96 DCHECK(file_);
97 DCHECK(file_->IsValid());
98 base::PostTaskAndReplyWithResult(
99 task_runner_.get(), FROM_HERE,
100 base::BindOnce(&ReadFile, file_.get(), base::WrapRefCounted(buffer),
101 buffer_length),
102 base::BindOnce(&ArcContentFileSystemFileStreamReader::OnRead,
103 weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
104 }
105
OnRead(net::CompletionOnceCallback callback,int result)106 void ArcContentFileSystemFileStreamReader::OnRead(
107 net::CompletionOnceCallback callback,
108 int result) {
109 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
110 std::move(callback).Run(result < 0 ? net::ERR_FAILED : result);
111 }
112
OnGetFileSize(net::Int64CompletionOnceCallback callback,int64_t size)113 void ArcContentFileSystemFileStreamReader::OnGetFileSize(
114 net::Int64CompletionOnceCallback callback,
115 int64_t size) {
116 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
117 std::move(callback).Run(size < 0 ? net::ERR_FAILED : size);
118 }
119
OnOpenFile(scoped_refptr<net::IOBuffer> buf,int buffer_length,net::CompletionOnceCallback callback,mojo::ScopedHandle handle)120 void ArcContentFileSystemFileStreamReader::OnOpenFile(
121 scoped_refptr<net::IOBuffer> buf,
122 int buffer_length,
123 net::CompletionOnceCallback callback,
124 mojo::ScopedHandle handle) {
125 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
126 DCHECK(!file_);
127
128 mojo::PlatformHandle platform_handle =
129 mojo::UnwrapPlatformHandle(std::move(handle));
130 if (!platform_handle.is_valid()) {
131 LOG(ERROR) << "PassWrappedInternalPlatformHandle failed";
132 std::move(callback).Run(net::ERR_FAILED);
133 return;
134 }
135 file_ = std::make_unique<base::File>(platform_handle.ReleaseFD());
136 if (!file_->IsValid()) {
137 LOG(ERROR) << "Invalid file.";
138 std::move(callback).Run(net::ERR_FAILED);
139 return;
140 }
141 base::PostTaskAndReplyWithResult(
142 task_runner_.get(), FROM_HERE,
143 base::BindOnce(&SeekFile, file_.get(), offset_),
144 base::BindOnce(&ArcContentFileSystemFileStreamReader::OnSeekFile,
145 weak_ptr_factory_.GetWeakPtr(), buf, buffer_length,
146 std::move(callback)));
147 }
148
OnSeekFile(scoped_refptr<net::IOBuffer> buf,int buffer_length,net::CompletionOnceCallback callback,int seek_result)149 void ArcContentFileSystemFileStreamReader::OnSeekFile(
150 scoped_refptr<net::IOBuffer> buf,
151 int buffer_length,
152 net::CompletionOnceCallback callback,
153 int seek_result) {
154 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
155 DCHECK(file_);
156 DCHECK(file_->IsValid());
157 switch (seek_result) {
158 case 0:
159 // File stream is ready. Resume Read().
160 ReadInternal(buf.get(), buffer_length, std::move(callback));
161 break;
162 case ESPIPE: {
163 // Pipe is not seekable. Just consume the contents.
164 const size_t kTemporaryBufferSize = 1024 * 1024;
165 auto temporary_buffer =
166 base::MakeRefCounted<net::IOBufferWithSize>(kTemporaryBufferSize);
167 ConsumeFileContents(buf, buffer_length, std::move(callback),
168 temporary_buffer, offset_);
169 break;
170 }
171 default:
172 LOG(ERROR) << "Failed to seek: " << seek_result;
173 std::move(callback).Run(net::FileErrorToNetError(
174 base::File::OSErrorToFileError(seek_result)));
175 }
176 }
177
ConsumeFileContents(scoped_refptr<net::IOBuffer> buf,int buffer_length,net::CompletionOnceCallback callback,scoped_refptr<net::IOBufferWithSize> temporary_buffer,int64_t num_bytes_to_consume)178 void ArcContentFileSystemFileStreamReader::ConsumeFileContents(
179 scoped_refptr<net::IOBuffer> buf,
180 int buffer_length,
181 net::CompletionOnceCallback callback,
182 scoped_refptr<net::IOBufferWithSize> temporary_buffer,
183 int64_t num_bytes_to_consume) {
184 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
185 DCHECK(file_);
186 DCHECK(file_->IsValid());
187 if (num_bytes_to_consume == 0) {
188 // File stream is ready. Resume Read().
189 ReadInternal(buf.get(), buffer_length, std::move(callback));
190 return;
191 }
192 auto num_bytes_to_read = std::min(
193 static_cast<int64_t>(temporary_buffer->size()), num_bytes_to_consume);
194 // TODO(hashimoto): This may block the worker thread forever. crbug.com/673222
195 base::PostTaskAndReplyWithResult(
196 task_runner_.get(), FROM_HERE,
197 base::BindOnce(&ReadFile, file_.get(), temporary_buffer,
198 num_bytes_to_read),
199 base::BindOnce(
200 &ArcContentFileSystemFileStreamReader::OnConsumeFileContents,
201 weak_ptr_factory_.GetWeakPtr(), buf, buffer_length,
202 std::move(callback), temporary_buffer, num_bytes_to_consume));
203 }
204
OnConsumeFileContents(scoped_refptr<net::IOBuffer> buf,int buffer_length,net::CompletionOnceCallback callback,scoped_refptr<net::IOBufferWithSize> temporary_buffer,int64_t num_bytes_to_consume,int read_result)205 void ArcContentFileSystemFileStreamReader::OnConsumeFileContents(
206 scoped_refptr<net::IOBuffer> buf,
207 int buffer_length,
208 net::CompletionOnceCallback callback,
209 scoped_refptr<net::IOBufferWithSize> temporary_buffer,
210 int64_t num_bytes_to_consume,
211 int read_result) {
212 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
213 if (read_result < 0) {
214 LOG(ERROR) << "Failed to consume the file stream.";
215 std::move(callback).Run(net::ERR_FAILED);
216 return;
217 }
218 DCHECK_GE(num_bytes_to_consume, read_result);
219 num_bytes_to_consume -= read_result;
220 ConsumeFileContents(buf, buffer_length, std::move(callback), temporary_buffer,
221 num_bytes_to_consume);
222 }
223
224 } // namespace arc
225