file_io_resource.cc revision fb250657ef40d7500f20882d5c9909c1013367d3
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/files/file_util_proxy.h"
9#include "ipc/ipc_message.h"
10#include "ppapi/c/pp_errors.h"
11#include "ppapi/proxy/plugin_globals.h"
12#include "ppapi/proxy/ppapi_messages.h"
13#include "ppapi/shared_impl/array_writer.h"
14#include "ppapi/shared_impl/file_type_conversion.h"
15#include "ppapi/shared_impl/ppapi_globals.h"
16#include "ppapi/shared_impl/proxy_lock.h"
17#include "ppapi/shared_impl/resource_tracker.h"
18#include "ppapi/thunk/enter.h"
19#include "ppapi/thunk/ppb_file_ref_api.h"
20
21using ppapi::thunk::EnterResourceNoLock;
22using ppapi::thunk::PPB_FileIO_API;
23using ppapi::thunk::PPB_FileRef_API;
24
25namespace {
26
27// An adapter to let Read() share the same implementation with ReadToArray().
28void* DummyGetDataBuffer(void* user_data, uint32_t count, uint32_t size) {
29  return user_data;
30}
31
32// Dummy close callback allows us to call CloseFileHandle in the destructor.
33void DummyCloseCallback(base::PlatformFileError error_code) {
34}
35
36}  // namespace
37
38namespace ppapi {
39namespace proxy {
40
41FileIOResource::FileIOResource(Connection connection, PP_Instance instance)
42    : PluginResource(connection, instance),
43      file_handle_(PP_kInvalidFileHandle),
44      file_system_type_(PP_FILESYSTEMTYPE_INVALID) {
45  SendCreate(RENDERER, PpapiHostMsg_FileIO_Create());
46}
47
48FileIOResource::~FileIOResource() {
49  CloseFileHandle();
50}
51
52PPB_FileIO_API* FileIOResource::AsPPB_FileIO_API() {
53  return this;
54}
55
56int32_t FileIOResource::Open(PP_Resource file_ref,
57                             int32_t open_flags,
58                             scoped_refptr<TrackedCallback> callback) {
59  EnterResourceNoLock<PPB_FileRef_API> enter(file_ref, true);
60  if (enter.failed())
61    return PP_ERROR_BADRESOURCE;
62
63  PPB_FileRef_API* file_ref_api = enter.object();
64  PP_FileSystemType type = file_ref_api->GetFileSystemType();
65  if (type != PP_FILESYSTEMTYPE_LOCALPERSISTENT &&
66      type != PP_FILESYSTEMTYPE_LOCALTEMPORARY &&
67      type != PP_FILESYSTEMTYPE_EXTERNAL &&
68      type != PP_FILESYSTEMTYPE_ISOLATED) {
69    NOTREACHED();
70    return PP_ERROR_FAILED;
71  }
72  file_system_type_ = type;
73
74  int32_t rv = state_manager_.CheckOperationState(
75      FileIOStateManager::OPERATION_EXCLUSIVE, false);
76  if (rv != PP_OK)
77    return rv;
78
79  Call<PpapiPluginMsg_FileIO_OpenReply>(RENDERER,
80      PpapiHostMsg_FileIO_Open(
81          enter.resource()->host_resource().host_resource(),
82          open_flags),
83      base::Bind(&FileIOResource::OnPluginMsgOpenFileComplete, this,
84                 callback));
85
86  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
87  return PP_OK_COMPLETIONPENDING;
88}
89
90int32_t FileIOResource::Query(PP_FileInfo* info,
91                              scoped_refptr<TrackedCallback> callback) {
92  int32_t rv = state_manager_.CheckOperationState(
93      FileIOStateManager::OPERATION_EXCLUSIVE, true);
94  if (rv != PP_OK)
95    return rv;
96
97  if (file_handle_ == base::kInvalidPlatformFileValue)
98    return PP_ERROR_FAILED;
99
100  if (!base::FileUtilProxy::GetFileInfoFromPlatformFile(
101          PpapiGlobals::Get()->GetFileTaskRunner(pp_instance()),
102          file_handle_,
103          RunWhileLocked(base::Bind(&FileIOResource::OnQueryComplete, this,
104                                    callback, info)))) {
105    return PP_ERROR_FAILED;
106  }
107
108  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
109  return PP_OK_COMPLETIONPENDING;
110}
111
112int32_t FileIOResource::Touch(PP_Time last_access_time,
113                              PP_Time last_modified_time,
114                              scoped_refptr<TrackedCallback> callback) {
115  int32_t rv = state_manager_.CheckOperationState(
116      FileIOStateManager::OPERATION_EXCLUSIVE, true);
117  if (rv != PP_OK)
118    return rv;
119
120  Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER,
121      PpapiHostMsg_FileIO_Touch(last_access_time, last_modified_time),
122      base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
123                 callback));
124
125  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
126  return PP_OK_COMPLETIONPENDING;
127}
128
129int32_t FileIOResource::Read(int64_t offset,
130                             char* buffer,
131                             int32_t bytes_to_read,
132                             scoped_refptr<TrackedCallback> callback) {
133  int32_t rv = state_manager_.CheckOperationState(
134      FileIOStateManager::OPERATION_READ, true);
135  if (rv != PP_OK)
136    return rv;
137
138  PP_ArrayOutput output_adapter;
139  output_adapter.GetDataBuffer = &DummyGetDataBuffer;
140  output_adapter.user_data = buffer;
141  return ReadValidated(offset, bytes_to_read, output_adapter, callback);
142}
143
144int32_t FileIOResource::ReadToArray(int64_t offset,
145                                    int32_t max_read_length,
146                                    PP_ArrayOutput* array_output,
147                                    scoped_refptr<TrackedCallback> callback) {
148  DCHECK(array_output);
149  int32_t rv = state_manager_.CheckOperationState(
150      FileIOStateManager::OPERATION_READ, true);
151  if (rv != PP_OK)
152    return rv;
153
154  return ReadValidated(offset, max_read_length, *array_output, callback);
155}
156
157int32_t FileIOResource::Write(int64_t offset,
158                              const char* buffer,
159                              int32_t bytes_to_write,
160                              scoped_refptr<TrackedCallback> callback) {
161  int32_t rv = state_manager_.CheckOperationState(
162      FileIOStateManager::OPERATION_WRITE, true);
163  if (rv != PP_OK)
164    return rv;
165
166  // TODO(brettw) it would be nice to use a shared memory buffer for large
167  // writes rather than having to copy to a string (which will involve a number
168  // of extra copies to serialize over IPC).
169  Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER,
170      PpapiHostMsg_FileIO_Write(offset, std::string(buffer, bytes_to_write)),
171      base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
172                 callback));
173
174  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_WRITE);
175  return PP_OK_COMPLETIONPENDING;
176}
177
178int32_t FileIOResource::SetLength(int64_t length,
179                                  scoped_refptr<TrackedCallback> callback) {
180  int32_t rv = state_manager_.CheckOperationState(
181      FileIOStateManager::OPERATION_EXCLUSIVE, true);
182  if (rv != PP_OK)
183    return rv;
184
185  Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER,
186      PpapiHostMsg_FileIO_SetLength(length),
187      base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
188                 callback));
189
190  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
191  return PP_OK_COMPLETIONPENDING;
192}
193
194int32_t FileIOResource::Flush(scoped_refptr<TrackedCallback> callback) {
195  int32_t rv = state_manager_.CheckOperationState(
196      FileIOStateManager::OPERATION_EXCLUSIVE, true);
197  if (rv != PP_OK)
198    return rv;
199
200  Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER,
201      PpapiHostMsg_FileIO_Flush(),
202      base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
203                 callback));
204
205  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
206  return PP_OK_COMPLETIONPENDING;
207}
208
209void FileIOResource::Close() {
210  CloseFileHandle();
211  Post(RENDERER, PpapiHostMsg_FileIO_Close());
212}
213
214int32_t FileIOResource::GetOSFileDescriptor() {
215  int32_t file_descriptor;
216  // Only available when running in process.
217  SyncCall<PpapiPluginMsg_FileIO_GetOSFileDescriptorReply>(
218      RENDERER, PpapiHostMsg_FileIO_GetOSFileDescriptor(), &file_descriptor);
219  return file_descriptor;
220}
221
222int32_t FileIOResource::RequestOSFileHandle(
223    PP_FileHandle* handle,
224    scoped_refptr<TrackedCallback> callback) {
225  int32_t rv = state_manager_.CheckOperationState(
226      FileIOStateManager::OPERATION_EXCLUSIVE, true);
227  if (rv != PP_OK)
228    return rv;
229
230  Call<PpapiPluginMsg_FileIO_RequestOSFileHandleReply>(RENDERER,
231      PpapiHostMsg_FileIO_RequestOSFileHandle(),
232      base::Bind(&FileIOResource::OnPluginMsgRequestOSFileHandleComplete, this,
233                 callback, handle));
234
235  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
236  return PP_OK_COMPLETIONPENDING;
237}
238
239int32_t FileIOResource::WillWrite(int64_t offset,
240                                  int32_t bytes_to_write,
241                                  scoped_refptr<TrackedCallback> callback) {
242  Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER,
243      PpapiHostMsg_FileIO_WillWrite(offset, bytes_to_write),
244      base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
245                 callback));
246
247  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
248  return PP_OK_COMPLETIONPENDING;
249}
250
251int32_t FileIOResource::WillSetLength(int64_t length,
252                                      scoped_refptr<TrackedCallback> callback) {
253  Call<PpapiPluginMsg_FileIO_GeneralReply>(RENDERER,
254      PpapiHostMsg_FileIO_WillSetLength(length),
255      base::Bind(&FileIOResource::OnPluginMsgGeneralComplete, this,
256                 callback));
257
258  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
259  return PP_OK_COMPLETIONPENDING;
260}
261
262int32_t FileIOResource::ReadValidated(int64_t offset,
263                                      int32_t bytes_to_read,
264                                      const PP_ArrayOutput& array_output,
265                                      scoped_refptr<TrackedCallback> callback) {
266  if (file_handle_ == base::kInvalidPlatformFileValue)
267    return PP_ERROR_FAILED;
268
269  if (!base::FileUtilProxy::Read(
270          PpapiGlobals::Get()->GetFileTaskRunner(pp_instance()),
271          file_handle_,
272          offset,
273          bytes_to_read,
274          RunWhileLocked(base::Bind(&FileIOResource::OnReadComplete, this,
275                                    callback, array_output)))) {
276    return PP_ERROR_FAILED;
277  }
278
279  state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_READ);
280  return PP_OK_COMPLETIONPENDING;
281}
282
283void FileIOResource::CloseFileHandle() {
284  if (file_handle_ != base::kInvalidPlatformFileValue) {
285    base::FileUtilProxy::Close(
286        PpapiGlobals::Get()->GetFileTaskRunner(pp_instance()),
287        file_handle_,
288        base::Bind(&DummyCloseCallback));
289    file_handle_ = base::kInvalidPlatformFileValue;
290  }
291}
292
293void FileIOResource::OnQueryComplete(
294    scoped_refptr<TrackedCallback> callback,
295    PP_FileInfo* output_info,
296    base::PlatformFileError error_code,
297    const base::PlatformFileInfo& file_info) {
298  DCHECK(state_manager_.get_pending_operation() ==
299         FileIOStateManager::OPERATION_EXCLUSIVE);
300
301  if (!TrackedCallback::IsPending(callback)) {
302    state_manager_.SetOperationFinished();
303    return;
304  }
305
306  int32_t result = ::ppapi::PlatformFileErrorToPepperError(error_code);
307  if (result == PP_OK) {
308    ppapi::PlatformFileInfoToPepperFileInfo(file_info, file_system_type_,
309                                            output_info);
310  }
311
312  // End this operation now, so the user's callback can execute another FileIO
313  // operation, assuming there are no other pending operations.
314  state_manager_.SetOperationFinished();
315  callback->Run(result);
316}
317
318void FileIOResource::OnReadComplete(
319    scoped_refptr<TrackedCallback> callback,
320    PP_ArrayOutput array_output,
321    base::PlatformFileError error_code,
322    const char* data, int bytes_read) {
323  DCHECK(state_manager_.get_pending_operation() ==
324         FileIOStateManager::OPERATION_READ);
325
326  if (!TrackedCallback::IsPending(callback)) {
327    state_manager_.SetOperationFinished();
328    return;
329  }
330
331  int32_t result = ::ppapi::PlatformFileErrorToPepperError(error_code);
332  if (result == PP_OK) {
333    result = std::max(0, bytes_read);
334    ArrayWriter output;
335    output.set_pp_array_output(array_output);
336    if (output.is_valid())
337      output.StoreArray(data, result);
338    else
339      result = PP_ERROR_FAILED;
340  }
341
342  // End this operation now, so the user's callback can execute another FileIO
343  // operation, assuming there are no other pending operations.
344  state_manager_.SetOperationFinished();
345  callback->Run(result);
346}
347
348void FileIOResource::OnPluginMsgGeneralComplete(
349    scoped_refptr<TrackedCallback> callback,
350    const ResourceMessageReplyParams& params) {
351  DCHECK(state_manager_.get_pending_operation() ==
352         FileIOStateManager::OPERATION_EXCLUSIVE ||
353         state_manager_.get_pending_operation() ==
354         FileIOStateManager::OPERATION_WRITE);
355  // End this operation now, so the user's callback can execute another FileIO
356  // operation, assuming there are no other pending operations.
357  state_manager_.SetOperationFinished();
358  callback->Run(params.result());
359}
360
361void FileIOResource::OnPluginMsgOpenFileComplete(
362    scoped_refptr<TrackedCallback> callback,
363    const ResourceMessageReplyParams& params) {
364  DCHECK(state_manager_.get_pending_operation() ==
365         FileIOStateManager::OPERATION_EXCLUSIVE);
366  if (params.result() == PP_OK)
367    state_manager_.SetOpenSucceed();
368
369  int32_t result = params.result();
370  IPC::PlatformFileForTransit transit_file;
371  if (result == PP_OK && !params.TakeFileHandleAtIndex(0, &transit_file))
372    result = PP_ERROR_FAILED;
373  file_handle_ = IPC::PlatformFileForTransitToPlatformFile(transit_file);
374
375  // End this operation now, so the user's callback can execute another FileIO
376  // operation, assuming there are no other pending operations.
377  state_manager_.SetOperationFinished();
378  callback->Run(params.result());
379}
380
381void FileIOResource::OnPluginMsgRequestOSFileHandleComplete(
382    scoped_refptr<TrackedCallback> callback,
383    PP_FileHandle* output_handle,
384    const ResourceMessageReplyParams& params) {
385  DCHECK(state_manager_.get_pending_operation() ==
386         FileIOStateManager::OPERATION_EXCLUSIVE);
387
388  if (!TrackedCallback::IsPending(callback)) {
389    state_manager_.SetOperationFinished();
390    return;
391  }
392
393  int32_t result = params.result();
394  IPC::PlatformFileForTransit transit_file;
395  if (!params.TakeFileHandleAtIndex(0, &transit_file))
396    result = PP_ERROR_FAILED;
397  *output_handle = IPC::PlatformFileForTransitToPlatformFile(transit_file);
398
399  // End this operation now, so the user's callback can execute another FileIO
400  // operation, assuming there are no other pending operations.
401  state_manager_.SetOperationFinished();
402  callback->Run(result);
403}
404
405}  // namespace proxy
406}  // namespace ppapi
407