• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 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 "ppapi/proxy/file_io_resource.h"
6 
7 #include "base/bind.h"
8 #include "base/task_runner_util.h"
9 #include "ipc/ipc_message.h"
10 #include "ppapi/c/pp_errors.h"
11 #include "ppapi/proxy/ppapi_messages.h"
12 #include "ppapi/shared_impl/array_writer.h"
13 #include "ppapi/shared_impl/file_ref_create_info.h"
14 #include "ppapi/shared_impl/file_system_util.h"
15 #include "ppapi/shared_impl/file_type_conversion.h"
16 #include "ppapi/shared_impl/ppapi_globals.h"
17 #include "ppapi/shared_impl/proxy_lock.h"
18 #include "ppapi/shared_impl/resource_tracker.h"
19 #include "ppapi/thunk/enter.h"
20 #include "ppapi/thunk/ppb_file_ref_api.h"
21 #include "ppapi/thunk/ppb_file_system_api.h"
22 
23 using ppapi::thunk::EnterResourceNoLock;
24 using ppapi::thunk::PPB_FileIO_API;
25 using ppapi::thunk::PPB_FileRef_API;
26 using ppapi::thunk::PPB_FileSystem_API;
27 
28 namespace {
29 
30 // We must allocate a buffer sized according to the request of the plugin. To
31 // reduce the chance of out-of-memory errors, we cap the read and write size to
32 // 32MB. This is OK since the API specifies that it may perform a partial read
33 // or write.
34 static const int32_t kMaxReadWriteSize = 32 * 1024 * 1024;  // 32MB
35 
36 // An adapter to let Read() share the same implementation with ReadToArray().
DummyGetDataBuffer(void * user_data,uint32_t count,uint32_t size)37 void* DummyGetDataBuffer(void* user_data, uint32_t count, uint32_t size) {
38   return user_data;
39 }
40 
41 // File thread task to close the file handle.
DoClose(base::PlatformFile file)42 void DoClose(base::PlatformFile file) {
43   base::ClosePlatformFile(file);
44 }
45 
46 }  // namespace
47 
48 namespace ppapi {
49 namespace proxy {
50 
QueryOp(scoped_refptr<FileHandleHolder> file_handle)51 FileIOResource::QueryOp::QueryOp(scoped_refptr<FileHandleHolder> file_handle)
52     : file_handle_(file_handle) {
53   DCHECK(file_handle_);
54 }
55 
~QueryOp()56 FileIOResource::QueryOp::~QueryOp() {
57 }
58 
DoWork()59 int32_t FileIOResource::QueryOp::DoWork() {
60   return base::GetPlatformFileInfo(file_handle_->raw_handle(), &file_info_) ?
61       PP_OK : PP_ERROR_FAILED;
62 }
63 
ReadOp(scoped_refptr<FileHandleHolder> file_handle,int64_t offset,int32_t bytes_to_read)64 FileIOResource::ReadOp::ReadOp(scoped_refptr<FileHandleHolder> file_handle,
65                                int64_t offset,
66                                int32_t bytes_to_read)
67   : file_handle_(file_handle),
68     offset_(offset),
69     bytes_to_read_(bytes_to_read) {
70   DCHECK(file_handle_);
71 }
72 
~ReadOp()73 FileIOResource::ReadOp::~ReadOp() {
74 }
75 
DoWork()76 int32_t FileIOResource::ReadOp::DoWork() {
77   DCHECK(!buffer_.get());
78   buffer_.reset(new char[bytes_to_read_]);
79   return base::ReadPlatformFile(
80       file_handle_->raw_handle(), offset_, buffer_.get(), bytes_to_read_);
81 }
82 
FileIOResource(Connection connection,PP_Instance instance)83 FileIOResource::FileIOResource(Connection connection, PP_Instance instance)
84     : PluginResource(connection, instance),
85       file_system_type_(PP_FILESYSTEMTYPE_INVALID),
86       called_close_(false) {
87   SendCreate(BROWSER, PpapiHostMsg_FileIO_Create());
88 }
89 
~FileIOResource()90 FileIOResource::~FileIOResource() {
91   Close();
92 }
93 
AsPPB_FileIO_API()94 PPB_FileIO_API* FileIOResource::AsPPB_FileIO_API() {
95   return this;
96 }
97 
Open(PP_Resource file_ref,int32_t open_flags,scoped_refptr<TrackedCallback> callback)98 int32_t FileIOResource::Open(PP_Resource file_ref,
99                              int32_t open_flags,
100                              scoped_refptr<TrackedCallback> callback) {
101   EnterResourceNoLock<PPB_FileRef_API> enter_file_ref(file_ref, true);
102   if (enter_file_ref.failed())
103     return PP_ERROR_BADRESOURCE;
104 
105   PPB_FileRef_API* file_ref_api = enter_file_ref.object();
106   const FileRefCreateInfo& create_info = file_ref_api->GetCreateInfo();
107   if (!FileSystemTypeIsValid(create_info.file_system_type)) {
108     NOTREACHED();
109     return PP_ERROR_FAILED;
110   }
111   int32_t rv = state_manager_.CheckOperationState(
112       FileIOStateManager::OPERATION_EXCLUSIVE, false);
113   if (rv != PP_OK)
114     return rv;
115 
116   file_system_type_ = create_info.file_system_type;
117 
118   if (create_info.file_system_plugin_resource) {
119     EnterResourceNoLock<PPB_FileSystem_API> enter_file_system(
120         create_info.file_system_plugin_resource, true);
121     if (enter_file_system.failed())
122       return PP_ERROR_FAILED;
123     // Take a reference on the FileSystem resource. The FileIO host uses the
124     // FileSystem host for running tasks and checking quota.
125     file_system_resource_ = enter_file_system.resource();
126   }
127 
128   // Take a reference on the FileRef resource while we're opening the file; we
129   // don't want the plugin destroying it during the Open operation.
130   file_ref_ = enter_file_ref.resource();
131 
132   Call<PpapiPluginMsg_FileIO_OpenReply>(BROWSER,
133       PpapiHostMsg_FileIO_Open(
134           file_ref,
135           open_flags),
136       base::Bind(&FileIOResource::OnPluginMsgOpenFileComplete, this,
137                  callback));
138 
139   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
140   return PP_OK_COMPLETIONPENDING;
141 }
142 
Query(PP_FileInfo * info,scoped_refptr<TrackedCallback> callback)143 int32_t FileIOResource::Query(PP_FileInfo* info,
144                               scoped_refptr<TrackedCallback> callback) {
145   int32_t rv = state_manager_.CheckOperationState(
146       FileIOStateManager::OPERATION_EXCLUSIVE, true);
147   if (rv != PP_OK)
148     return rv;
149   if (!info)
150     return PP_ERROR_BADARGUMENT;
151   if (!FileHandleHolder::IsValid(file_handle_))
152     return PP_ERROR_FAILED;
153 
154   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
155 
156   // If the callback is blocking, perform the task on the calling thread.
157   if (callback->is_blocking()) {
158     int32_t result = PP_ERROR_FAILED;
159     base::PlatformFileInfo file_info;
160     // The plugin could release its reference to this instance when we release
161     // the proxy lock below.
162     scoped_refptr<FileIOResource> protect(this);
163     {
164       // Release the proxy lock while making a potentially slow file call.
165       ProxyAutoUnlock unlock;
166       if (base::GetPlatformFileInfo(file_handle_->raw_handle(), &file_info))
167         result = PP_OK;
168     }
169     if (result == PP_OK) {
170       // This writes the file info into the plugin's PP_FileInfo struct.
171       ppapi::PlatformFileInfoToPepperFileInfo(file_info,
172                                               file_system_type_,
173                                               info);
174     }
175     state_manager_.SetOperationFinished();
176     return result;
177   }
178 
179   // For the non-blocking case, post a task to the file thread and add a
180   // completion task to write the result.
181   scoped_refptr<QueryOp> query_op(new QueryOp(file_handle_));
182   base::PostTaskAndReplyWithResult(
183       PpapiGlobals::Get()->GetFileTaskRunner(),
184       FROM_HERE,
185       Bind(&FileIOResource::QueryOp::DoWork, query_op),
186       RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
187   callback->set_completion_task(
188       Bind(&FileIOResource::OnQueryComplete, this, query_op, info));
189 
190   return PP_OK_COMPLETIONPENDING;
191 }
192 
Touch(PP_Time last_access_time,PP_Time last_modified_time,scoped_refptr<TrackedCallback> callback)193 int32_t FileIOResource::Touch(PP_Time last_access_time,
194                               PP_Time last_modified_time,
195                               scoped_refptr<TrackedCallback> callback) {
196   int32_t rv = state_manager_.CheckOperationState(
197       FileIOStateManager::OPERATION_EXCLUSIVE, true);
198   if (rv != PP_OK)
199     return rv;
200 
201   Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
202       PpapiHostMsg_FileIO_Touch(last_access_time, last_modified_time),
203       base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
204                  callback));
205 
206   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
207   return PP_OK_COMPLETIONPENDING;
208 }
209 
Read(int64_t offset,char * buffer,int32_t bytes_to_read,scoped_refptr<TrackedCallback> callback)210 int32_t FileIOResource::Read(int64_t offset,
211                              char* buffer,
212                              int32_t bytes_to_read,
213                              scoped_refptr<TrackedCallback> callback) {
214   int32_t rv = state_manager_.CheckOperationState(
215       FileIOStateManager::OPERATION_READ, true);
216   if (rv != PP_OK)
217     return rv;
218 
219   PP_ArrayOutput output_adapter;
220   output_adapter.GetDataBuffer = &DummyGetDataBuffer;
221   output_adapter.user_data = buffer;
222   return ReadValidated(offset, bytes_to_read, output_adapter, callback);
223 }
224 
ReadToArray(int64_t offset,int32_t max_read_length,PP_ArrayOutput * array_output,scoped_refptr<TrackedCallback> callback)225 int32_t FileIOResource::ReadToArray(int64_t offset,
226                                     int32_t max_read_length,
227                                     PP_ArrayOutput* array_output,
228                                     scoped_refptr<TrackedCallback> callback) {
229   DCHECK(array_output);
230   int32_t rv = state_manager_.CheckOperationState(
231       FileIOStateManager::OPERATION_READ, true);
232   if (rv != PP_OK)
233     return rv;
234 
235   return ReadValidated(offset, max_read_length, *array_output, callback);
236 }
237 
Write(int64_t offset,const char * buffer,int32_t bytes_to_write,scoped_refptr<TrackedCallback> callback)238 int32_t FileIOResource::Write(int64_t offset,
239                               const char* buffer,
240                               int32_t bytes_to_write,
241                               scoped_refptr<TrackedCallback> callback) {
242   int32_t rv = state_manager_.CheckOperationState(
243       FileIOStateManager::OPERATION_WRITE, true);
244   if (rv != PP_OK)
245     return rv;
246 
247   // TODO(brettw) it would be nice to use a shared memory buffer for large
248   // writes rather than having to copy to a string (which will involve a number
249   // of extra copies to serialize over IPC).
250   bytes_to_write = std::min(bytes_to_write, kMaxReadWriteSize);
251   Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
252       PpapiHostMsg_FileIO_Write(offset, std::string(buffer, bytes_to_write)),
253       base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
254                  callback));
255 
256   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_WRITE);
257   return PP_OK_COMPLETIONPENDING;
258 }
259 
SetLength(int64_t length,scoped_refptr<TrackedCallback> callback)260 int32_t FileIOResource::SetLength(int64_t length,
261                                   scoped_refptr<TrackedCallback> callback) {
262   int32_t rv = state_manager_.CheckOperationState(
263       FileIOStateManager::OPERATION_EXCLUSIVE, true);
264   if (rv != PP_OK)
265     return rv;
266 
267   Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
268       PpapiHostMsg_FileIO_SetLength(length),
269       base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
270                  callback));
271 
272   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
273   return PP_OK_COMPLETIONPENDING;
274 }
275 
Flush(scoped_refptr<TrackedCallback> callback)276 int32_t FileIOResource::Flush(scoped_refptr<TrackedCallback> callback) {
277   int32_t rv = state_manager_.CheckOperationState(
278       FileIOStateManager::OPERATION_EXCLUSIVE, true);
279   if (rv != PP_OK)
280     return rv;
281 
282   Call<PpapiPluginMsg_FileIO_GeneralReply>(BROWSER,
283       PpapiHostMsg_FileIO_Flush(),
284       base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
285                  callback));
286 
287   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
288   return PP_OK_COMPLETIONPENDING;
289 }
290 
Close()291 void FileIOResource::Close() {
292   if (called_close_)
293     return;
294 
295   called_close_ = true;
296   if (file_handle_)
297     file_handle_ = NULL;
298 
299   Post(BROWSER, PpapiHostMsg_FileIO_Close());
300 }
301 
RequestOSFileHandle(PP_FileHandle * handle,scoped_refptr<TrackedCallback> callback)302 int32_t FileIOResource::RequestOSFileHandle(
303     PP_FileHandle* handle,
304     scoped_refptr<TrackedCallback> callback) {
305   int32_t rv = state_manager_.CheckOperationState(
306       FileIOStateManager::OPERATION_EXCLUSIVE, true);
307   if (rv != PP_OK)
308     return rv;
309 
310   Call<PpapiPluginMsg_FileIO_RequestOSFileHandleReply>(BROWSER,
311       PpapiHostMsg_FileIO_RequestOSFileHandle(),
312       base::Bind(&FileIOResource::OnPluginMsgRequestOSFileHandleComplete, this,
313                  callback, handle));
314 
315   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
316   return PP_OK_COMPLETIONPENDING;
317 }
318 
FileHandleHolder(PP_FileHandle file_handle)319 FileIOResource::FileHandleHolder::FileHandleHolder(PP_FileHandle file_handle)
320     : raw_handle_(file_handle) {
321 }
322 
323 // static
IsValid(const scoped_refptr<FileIOResource::FileHandleHolder> & handle)324 bool FileIOResource::FileHandleHolder::IsValid(
325     const scoped_refptr<FileIOResource::FileHandleHolder>& handle) {
326   return handle && (handle->raw_handle() != base::kInvalidPlatformFileValue);
327 }
328 
~FileHandleHolder()329 FileIOResource::FileHandleHolder::~FileHandleHolder() {
330   if (raw_handle_ != base::kInvalidPlatformFileValue) {
331     base::TaskRunner* file_task_runner =
332         PpapiGlobals::Get()->GetFileTaskRunner();
333     file_task_runner->PostTask(FROM_HERE,
334                                base::Bind(&DoClose, raw_handle_));
335   }
336 }
337 
ReadValidated(int64_t offset,int32_t bytes_to_read,const PP_ArrayOutput & array_output,scoped_refptr<TrackedCallback> callback)338 int32_t FileIOResource::ReadValidated(int64_t offset,
339                                       int32_t bytes_to_read,
340                                       const PP_ArrayOutput& array_output,
341                                       scoped_refptr<TrackedCallback> callback) {
342   if (bytes_to_read < 0)
343     return PP_ERROR_FAILED;
344   if (!FileHandleHolder::IsValid(file_handle_))
345     return PP_ERROR_FAILED;
346 
347   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_READ);
348 
349   bytes_to_read = std::min(bytes_to_read, kMaxReadWriteSize);
350   if (callback->is_blocking()) {
351     char* buffer = static_cast<char*>(
352         array_output.GetDataBuffer(array_output.user_data, bytes_to_read, 1));
353     int32_t result = PP_ERROR_FAILED;
354     // The plugin could release its reference to this instance when we release
355     // the proxy lock below.
356     scoped_refptr<FileIOResource> protect(this);
357     if (buffer) {
358       // Release the proxy lock while making a potentially slow file call.
359       ProxyAutoUnlock unlock;
360       result = base::ReadPlatformFile(
361           file_handle_->raw_handle(), offset, buffer, bytes_to_read);
362       if (result < 0)
363         result = PP_ERROR_FAILED;
364     }
365     state_manager_.SetOperationFinished();
366     return result;
367   }
368 
369   // For the non-blocking case, post a task to the file thread.
370   scoped_refptr<ReadOp> read_op(
371       new ReadOp(file_handle_, offset, bytes_to_read));
372   base::PostTaskAndReplyWithResult(
373       PpapiGlobals::Get()->GetFileTaskRunner(),
374       FROM_HERE,
375       Bind(&FileIOResource::ReadOp::DoWork, read_op),
376       RunWhileLocked(Bind(&TrackedCallback::Run, callback)));
377   callback->set_completion_task(
378       Bind(&FileIOResource::OnReadComplete, this, read_op, array_output));
379 
380   return PP_OK_COMPLETIONPENDING;
381 }
382 
OnQueryComplete(scoped_refptr<QueryOp> query_op,PP_FileInfo * info,int32_t result)383 int32_t FileIOResource::OnQueryComplete(scoped_refptr<QueryOp> query_op,
384                                         PP_FileInfo* info,
385                                         int32_t result) {
386   DCHECK(state_manager_.get_pending_operation() ==
387          FileIOStateManager::OPERATION_EXCLUSIVE);
388 
389   if (result == PP_OK) {
390     // This writes the file info into the plugin's PP_FileInfo struct.
391     ppapi::PlatformFileInfoToPepperFileInfo(query_op->file_info(),
392                                             file_system_type_,
393                                             info);
394   }
395   state_manager_.SetOperationFinished();
396   return result;
397 }
398 
OnReadComplete(scoped_refptr<ReadOp> read_op,PP_ArrayOutput array_output,int32_t result)399 int32_t FileIOResource::OnReadComplete(scoped_refptr<ReadOp> read_op,
400                                        PP_ArrayOutput array_output,
401                                        int32_t result) {
402   DCHECK(state_manager_.get_pending_operation() ==
403          FileIOStateManager::OPERATION_READ);
404   if (result >= 0) {
405     ArrayWriter output;
406     output.set_pp_array_output(array_output);
407     if (output.is_valid())
408       output.StoreArray(read_op->buffer(), result);
409     else
410       result = PP_ERROR_FAILED;
411   } else {
412     // The read operation failed.
413     result = PP_ERROR_FAILED;
414   }
415   state_manager_.SetOperationFinished();
416   return result;
417 }
418 
OnPluginMsgGeneralComplete(scoped_refptr<TrackedCallback> callback,const ResourceMessageReplyParams & params)419 void FileIOResource::OnPluginMsgGeneralComplete(
420     scoped_refptr<TrackedCallback> callback,
421     const ResourceMessageReplyParams& params) {
422   DCHECK(state_manager_.get_pending_operation() ==
423          FileIOStateManager::OPERATION_EXCLUSIVE ||
424          state_manager_.get_pending_operation() ==
425          FileIOStateManager::OPERATION_WRITE);
426   // End this operation now, so the user's callback can execute another FileIO
427   // operation, assuming there are no other pending operations.
428   state_manager_.SetOperationFinished();
429   callback->Run(params.result());
430 }
431 
OnPluginMsgOpenFileComplete(scoped_refptr<TrackedCallback> callback,const ResourceMessageReplyParams & params)432 void FileIOResource::OnPluginMsgOpenFileComplete(
433     scoped_refptr<TrackedCallback> callback,
434     const ResourceMessageReplyParams& params) {
435   DCHECK(state_manager_.get_pending_operation() ==
436          FileIOStateManager::OPERATION_EXCLUSIVE);
437 
438   // Release the FileRef resource.
439   file_ref_ = NULL;
440   if (params.result() == PP_OK)
441     state_manager_.SetOpenSucceed();
442 
443   int32_t result = params.result();
444   IPC::PlatformFileForTransit transit_file;
445   if ((result == PP_OK) && params.TakeFileHandleAtIndex(0, &transit_file)) {
446     file_handle_ = new FileHandleHolder(
447         IPC::PlatformFileForTransitToPlatformFile(transit_file));
448   }
449   // End this operation now, so the user's callback can execute another FileIO
450   // operation, assuming there are no other pending operations.
451   state_manager_.SetOperationFinished();
452   callback->Run(result);
453 }
454 
OnPluginMsgRequestOSFileHandleComplete(scoped_refptr<TrackedCallback> callback,PP_FileHandle * output_handle,const ResourceMessageReplyParams & params)455 void FileIOResource::OnPluginMsgRequestOSFileHandleComplete(
456     scoped_refptr<TrackedCallback> callback,
457     PP_FileHandle* output_handle,
458     const ResourceMessageReplyParams& params) {
459   DCHECK(state_manager_.get_pending_operation() ==
460          FileIOStateManager::OPERATION_EXCLUSIVE);
461 
462   if (!TrackedCallback::IsPending(callback)) {
463     state_manager_.SetOperationFinished();
464     return;
465   }
466 
467   int32_t result = params.result();
468   IPC::PlatformFileForTransit transit_file;
469   if (!params.TakeFileHandleAtIndex(0, &transit_file))
470     result = PP_ERROR_FAILED;
471   *output_handle = IPC::PlatformFileForTransitToPlatformFile(transit_file);
472 
473   // End this operation now, so the user's callback can execute another FileIO
474   // operation, assuming there are no other pending operations.
475   state_manager_.SetOperationFinished();
476   callback->Run(result);
477 }
478 
479 }  // namespace proxy
480 }  // namespace ppapi
481