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