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