Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / pepper / pepper_file_io_host.cc
1 // Copyright 2013 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 "content/browser/renderer_host/pepper/pepper_file_io_host.h"
6
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/callback_helpers.h"
10 #include "base/files/file_util_proxy.h"
11 #include "base/memory/weak_ptr.h"
12 #include "content/browser/renderer_host/pepper/pepper_file_ref_host.h"
13 #include "content/browser/renderer_host/pepper/pepper_file_system_browser_host.h"
14 #include "content/browser/renderer_host/pepper/pepper_security_helper.h"
15 #include "content/common/fileapi/file_system_messages.h"
16 #include "content/common/sandbox_util.h"
17 #include "content/common/view_messages.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/content_browser_client.h"
20 #include "content/public/browser/render_process_host.h"
21 #include "content/public/browser/storage_partition.h"
22 #include "content/public/common/content_client.h"
23 #include "ppapi/c/pp_errors.h"
24 #include "ppapi/c/ppb_file_io.h"
25 #include "ppapi/host/dispatch_host_message.h"
26 #include "ppapi/host/ppapi_host.h"
27 #include "ppapi/proxy/ppapi_messages.h"
28 #include "ppapi/shared_impl/file_system_util.h"
29 #include "ppapi/shared_impl/file_type_conversion.h"
30 #include "ppapi/shared_impl/time_conversion.h"
31 #include "webkit/browser/fileapi/file_observers.h"
32 #include "webkit/browser/fileapi/file_system_context.h"
33 #include "webkit/browser/fileapi/file_system_operation_runner.h"
34 #include "webkit/browser/fileapi/task_runner_bound_observer_list.h"
35 #include "webkit/common/fileapi/file_system_util.h"
36
37 namespace content {
38
39 using ppapi::FileIOStateManager;
40 using ppapi::PPTimeToTime;
41
42 namespace {
43
44 PepperFileIOHost::UIThreadStuff GetUIThreadStuffForInternalFileSystems(
45     int render_process_id) {
46   PepperFileIOHost::UIThreadStuff stuff;
47   DCHECK_CURRENTLY_ON(BrowserThread::UI);
48   RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
49   if (host) {
50     stuff.resolved_render_process_id = base::GetProcId(host->GetHandle());
51     StoragePartition* storage_partition = host->GetStoragePartition();
52     if (storage_partition)
53       stuff.file_system_context = storage_partition->GetFileSystemContext();
54   }
55   return stuff;
56 }
57
58 base::ProcessId GetResolvedRenderProcessId(int render_process_id) {
59   DCHECK_CURRENTLY_ON(BrowserThread::UI);
60   RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
61   if (!host)
62     return base::kNullProcessId;
63   return base::GetProcId(host->GetHandle());
64 }
65
66 bool GetPluginAllowedToCallRequestOSFileHandle(int render_process_id,
67                                                const GURL& document_url) {
68   DCHECK_CURRENTLY_ON(BrowserThread::UI);
69   ContentBrowserClient* client = GetContentClient()->browser();
70   RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
71   if (!host)
72     return false;
73   return client->IsPluginAllowedToCallRequestOSFileHandle(
74       host->GetBrowserContext(), document_url);
75 }
76
77 bool FileOpenForWrite(int32_t open_flags) {
78   return (open_flags & (PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_APPEND)) != 0;
79 }
80
81 }  // namespace
82
83 PepperFileIOHost::PepperFileIOHost(BrowserPpapiHostImpl* host,
84                                    PP_Instance instance,
85                                    PP_Resource resource)
86     : ResourceHost(host->GetPpapiHost(), instance, resource),
87       browser_ppapi_host_(host),
88       render_process_host_(NULL),
89       file_(base::kInvalidPlatformFileValue),
90       open_flags_(0),
91       file_system_type_(PP_FILESYSTEMTYPE_INVALID),
92       max_written_offset_(0),
93       check_quota_(false),
94       weak_factory_(this) {
95   int unused;
96   if (!host->GetRenderFrameIDsForInstance(
97           instance, &render_process_id_, &unused)) {
98     render_process_id_ = -1;
99   }
100   file_message_loop_ =
101       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE);
102 }
103
104 PepperFileIOHost::~PepperFileIOHost() {}
105
106 int32_t PepperFileIOHost::OnResourceMessageReceived(
107     const IPC::Message& msg,
108     ppapi::host::HostMessageContext* context) {
109   IPC_BEGIN_MESSAGE_MAP(PepperFileIOHost, msg)
110   PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Open, OnHostMsgOpen)
111   PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Touch, OnHostMsgTouch)
112   PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_SetLength,
113                                     OnHostMsgSetLength)
114   PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_Flush, OnHostMsgFlush)
115   PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Close, OnHostMsgClose)
116   PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_RequestOSFileHandle,
117                                       OnHostMsgRequestOSFileHandle)
118   IPC_END_MESSAGE_MAP()
119   return PP_ERROR_FAILED;
120 }
121
122 PepperFileIOHost::UIThreadStuff::UIThreadStuff() {
123   resolved_render_process_id = base::kNullProcessId;
124 }
125
126 PepperFileIOHost::UIThreadStuff::~UIThreadStuff() {}
127
128 int32_t PepperFileIOHost::OnHostMsgOpen(
129     ppapi::host::HostMessageContext* context,
130     PP_Resource file_ref_resource,
131     int32_t open_flags) {
132   int32_t rv = state_manager_.CheckOperationState(
133       FileIOStateManager::OPERATION_EXCLUSIVE, false);
134   if (rv != PP_OK)
135     return rv;
136
137   int platform_file_flags = 0;
138   if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(open_flags,
139                                                      &platform_file_flags))
140     return PP_ERROR_BADARGUMENT;
141
142   ppapi::host::ResourceHost* resource_host =
143       host()->GetResourceHost(file_ref_resource);
144   if (!resource_host || !resource_host->IsFileRefHost())
145     return PP_ERROR_BADRESOURCE;
146   PepperFileRefHost* file_ref_host =
147       static_cast<PepperFileRefHost*>(resource_host);
148   if (file_ref_host->GetFileSystemType() == PP_FILESYSTEMTYPE_INVALID)
149     return PP_ERROR_FAILED;
150
151   file_system_host_ = file_ref_host->GetFileSystemHost();
152
153   open_flags_ = open_flags;
154   file_system_type_ = file_ref_host->GetFileSystemType();
155   file_system_url_ = file_ref_host->GetFileSystemURL();
156
157   // For external file systems, if there is a valid FileSystemURL, then treat
158   // it like internal file systems and access it via the FileSystemURL.
159   bool is_internal_type = (file_system_type_ != PP_FILESYSTEMTYPE_EXTERNAL) ||
160                           file_system_url_.is_valid();
161
162   if (is_internal_type) {
163     if (!file_system_url_.is_valid())
164       return PP_ERROR_BADARGUMENT;
165
166     // Not all external file systems are fully supported yet.
167     // Whitelist the supported ones.
168     if (file_system_url_.mount_type() == fileapi::kFileSystemTypeExternal) {
169       switch (file_system_url_.type()) {
170         case fileapi::kFileSystemTypeNativeMedia:
171         case fileapi::kFileSystemTypeDeviceMedia:
172         case fileapi::kFileSystemTypePicasa:
173         case fileapi::kFileSystemTypeItunes:
174         case fileapi::kFileSystemTypeIphoto:
175           break;
176         default:
177           return PP_ERROR_NOACCESS;
178       }
179     }
180     if (!CanOpenFileSystemURLWithPepperFlags(
181             open_flags, render_process_id_, file_system_url_))
182       return PP_ERROR_NOACCESS;
183     BrowserThread::PostTaskAndReplyWithResult(
184         BrowserThread::UI,
185         FROM_HERE,
186         base::Bind(&GetUIThreadStuffForInternalFileSystems, render_process_id_),
187         base::Bind(&PepperFileIOHost::GotUIThreadStuffForInternalFileSystems,
188                    weak_factory_.GetWeakPtr(),
189                    context->MakeReplyMessageContext(),
190                    platform_file_flags));
191   } else {
192     base::FilePath path = file_ref_host->GetExternalFilePath();
193     if (!CanOpenWithPepperFlags(open_flags, render_process_id_, path))
194       return PP_ERROR_NOACCESS;
195     BrowserThread::PostTaskAndReplyWithResult(
196         BrowserThread::UI,
197         FROM_HERE,
198         base::Bind(&GetResolvedRenderProcessId, render_process_id_),
199         base::Bind(&PepperFileIOHost::GotResolvedRenderProcessId,
200                    weak_factory_.GetWeakPtr(),
201                    context->MakeReplyMessageContext(),
202                    path,
203                    platform_file_flags));
204   }
205   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
206   return PP_OK_COMPLETIONPENDING;
207 }
208
209 void PepperFileIOHost::GotUIThreadStuffForInternalFileSystems(
210     ppapi::host::ReplyMessageContext reply_context,
211     int platform_file_flags,
212     UIThreadStuff ui_thread_stuff) {
213   DCHECK_CURRENTLY_ON(BrowserThread::IO);
214   file_system_context_ = ui_thread_stuff.file_system_context;
215   resolved_render_process_id_ = ui_thread_stuff.resolved_render_process_id;
216   if (resolved_render_process_id_ == base::kNullProcessId ||
217       !file_system_context_.get()) {
218     reply_context.params.set_result(PP_ERROR_FAILED);
219     SendOpenErrorReply(reply_context);
220     return;
221   }
222
223   if (!file_system_context_->GetFileSystemBackend(file_system_url_.type())) {
224     reply_context.params.set_result(PP_ERROR_FAILED);
225     SendOpenErrorReply(reply_context);
226     return;
227   }
228
229   DCHECK(file_system_host_.get());
230   DCHECK(file_system_host_->GetFileSystemOperationRunner());
231   file_system_host_->GetFileSystemOperationRunner()->OpenFile(
232       file_system_url_,
233       platform_file_flags,
234       base::Bind(&PepperFileIOHost::DidOpenInternalFile,
235                  weak_factory_.GetWeakPtr(),
236                  reply_context));
237 }
238
239 void PepperFileIOHost::DidOpenInternalFile(
240     ppapi::host::ReplyMessageContext reply_context,
241     base::File::Error result,
242     base::PlatformFile file,
243     const base::Closure& on_close_callback) {
244   if (result == base::File::FILE_OK) {
245     on_close_callback_ = on_close_callback;
246
247     if (FileOpenForWrite(open_flags_) && file_system_host_->ChecksQuota()) {
248       check_quota_ = true;
249       file_system_host_->OpenQuotaFile(
250           this,
251           file_system_url_,
252           base::Bind(&PepperFileIOHost::DidOpenQuotaFile,
253                      weak_factory_.GetWeakPtr(),
254                      reply_context,
255                      file));
256       return;
257     }
258   }
259
260   ExecutePlatformOpenFileCallback(
261       reply_context, result, base::PassPlatformFile(&file), true);
262 }
263
264 void PepperFileIOHost::GotResolvedRenderProcessId(
265     ppapi::host::ReplyMessageContext reply_context,
266     base::FilePath path,
267     int platform_file_flags,
268     base::ProcessId resolved_render_process_id) {
269   DCHECK_CURRENTLY_ON(BrowserThread::IO);
270   resolved_render_process_id_ = resolved_render_process_id;
271   base::FileUtilProxy::CreateOrOpen(
272       file_message_loop_,
273       path,
274       platform_file_flags,
275       base::Bind(&PepperFileIOHost::ExecutePlatformOpenFileCallback,
276                  weak_factory_.GetWeakPtr(),
277                  reply_context));
278 }
279
280 int32_t PepperFileIOHost::OnHostMsgTouch(
281     ppapi::host::HostMessageContext* context,
282     PP_Time last_access_time,
283     PP_Time last_modified_time) {
284   int32_t rv = state_manager_.CheckOperationState(
285       FileIOStateManager::OPERATION_EXCLUSIVE, true);
286   if (rv != PP_OK)
287     return rv;
288
289   if (!base::FileUtilProxy::Touch(
290           file_message_loop_,
291           file_,
292           PPTimeToTime(last_access_time),
293           PPTimeToTime(last_modified_time),
294           base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
295                      weak_factory_.GetWeakPtr(),
296                      context->MakeReplyMessageContext())))
297     return PP_ERROR_FAILED;
298
299   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
300   return PP_OK_COMPLETIONPENDING;
301 }
302
303 int32_t PepperFileIOHost::OnHostMsgSetLength(
304     ppapi::host::HostMessageContext* context,
305     int64_t length) {
306   int32_t rv = state_manager_.CheckOperationState(
307       FileIOStateManager::OPERATION_EXCLUSIVE, true);
308   if (rv != PP_OK)
309     return rv;
310   if (length < 0)
311     return PP_ERROR_BADARGUMENT;
312
313   // Quota checks are performed on the plugin side, in order to use the same
314   // quota reservation and request system as Write.
315
316   if (!base::FileUtilProxy::Truncate(
317           file_message_loop_,
318           file_,
319           length,
320           base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
321                      weak_factory_.GetWeakPtr(),
322                      context->MakeReplyMessageContext())))
323     return PP_ERROR_FAILED;
324
325   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
326   return PP_OK_COMPLETIONPENDING;
327 }
328
329 int32_t PepperFileIOHost::OnHostMsgFlush(
330     ppapi::host::HostMessageContext* context) {
331   int32_t rv = state_manager_.CheckOperationState(
332       FileIOStateManager::OPERATION_EXCLUSIVE, true);
333   if (rv != PP_OK)
334     return rv;
335
336   if (!base::FileUtilProxy::Flush(
337           file_message_loop_,
338           file_,
339           base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback,
340                      weak_factory_.GetWeakPtr(),
341                      context->MakeReplyMessageContext())))
342     return PP_ERROR_FAILED;
343
344   state_manager_.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE);
345   return PP_OK_COMPLETIONPENDING;
346 }
347
348 int32_t PepperFileIOHost::OnHostMsgClose(
349     ppapi::host::HostMessageContext* context,
350     const ppapi::FileGrowth& file_growth) {
351   if (check_quota_) {
352     file_system_host_->CloseQuotaFile(this, file_growth);
353     check_quota_ = false;
354   }
355
356   if (file_ != base::kInvalidPlatformFileValue) {
357     base::FileUtilProxy::Close(file_message_loop_,
358                                file_,
359                                base::Bind(&PepperFileIOHost::DidCloseFile,
360                                           weak_factory_.GetWeakPtr()));
361     file_ = base::kInvalidPlatformFileValue;
362   }
363   return PP_OK;
364 }
365
366 void PepperFileIOHost::DidOpenQuotaFile(
367     ppapi::host::ReplyMessageContext reply_context,
368     base::PlatformFile file,
369     int64_t max_written_offset) {
370   max_written_offset_ = max_written_offset;
371
372   ExecutePlatformOpenFileCallback(
373       reply_context, base::File::FILE_OK, base::PassPlatformFile(&file), true);
374 }
375
376 void PepperFileIOHost::DidCloseFile(base::File::Error /*error*/) {
377   // Silently ignore if we fail to close the file.
378   if (!on_close_callback_.is_null()) {
379     on_close_callback_.Run();
380     on_close_callback_.Reset();
381   }
382 }
383
384 int32_t PepperFileIOHost::OnHostMsgRequestOSFileHandle(
385     ppapi::host::HostMessageContext* context) {
386   if (open_flags_ != PP_FILEOPENFLAG_READ && file_system_host_->ChecksQuota())
387     return PP_ERROR_FAILED;
388
389   GURL document_url =
390       browser_ppapi_host_->GetDocumentURLForInstance(pp_instance());
391   BrowserThread::PostTaskAndReplyWithResult(
392       BrowserThread::UI,
393       FROM_HERE,
394       base::Bind(&GetPluginAllowedToCallRequestOSFileHandle,
395                  render_process_id_,
396                  document_url),
397       base::Bind(&PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle,
398                  weak_factory_.GetWeakPtr(),
399                  context->MakeReplyMessageContext()));
400   return PP_OK_COMPLETIONPENDING;
401 }
402
403 void PepperFileIOHost::GotPluginAllowedToCallRequestOSFileHandle(
404     ppapi::host::ReplyMessageContext reply_context,
405     bool plugin_allowed) {
406   DCHECK_CURRENTLY_ON(BrowserThread::IO);
407   if (!browser_ppapi_host_->external_plugin() ||
408       host()->permissions().HasPermission(ppapi::PERMISSION_PRIVATE) ||
409       plugin_allowed) {
410     if (!AddFileToReplyContext(open_flags_, &reply_context))
411       reply_context.params.set_result(PP_ERROR_FAILED);
412   } else {
413     reply_context.params.set_result(PP_ERROR_NOACCESS);
414   }
415   host()->SendReply(reply_context,
416                     PpapiPluginMsg_FileIO_RequestOSFileHandleReply());
417 }
418
419 void PepperFileIOHost::ExecutePlatformGeneralCallback(
420     ppapi::host::ReplyMessageContext reply_context,
421     base::File::Error error_code) {
422   reply_context.params.set_result(ppapi::FileErrorToPepperError(error_code));
423   host()->SendReply(reply_context, PpapiPluginMsg_FileIO_GeneralReply());
424   state_manager_.SetOperationFinished();
425 }
426
427 void PepperFileIOHost::ExecutePlatformOpenFileCallback(
428     ppapi::host::ReplyMessageContext reply_context,
429     base::File::Error error_code,
430     base::PassPlatformFile file,
431     bool unused_created) {
432   int32_t pp_error = ppapi::FileErrorToPepperError(error_code);
433   DCHECK(file_ == base::kInvalidPlatformFileValue);
434   file_ = file.ReleaseValue();
435
436   if (file_ != base::kInvalidPlatformFileValue &&
437       !AddFileToReplyContext(open_flags_, &reply_context))
438     pp_error = PP_ERROR_FAILED;
439
440   PP_Resource quota_file_system = 0;
441   if (pp_error == PP_OK) {
442     state_manager_.SetOpenSucceed();
443     // A non-zero resource id signals the plugin side to check quota.
444     if (check_quota_)
445       quota_file_system = file_system_host_->pp_resource();
446   }
447
448   reply_context.params.set_result(pp_error);
449   host()->SendReply(
450       reply_context,
451       PpapiPluginMsg_FileIO_OpenReply(quota_file_system, max_written_offset_));
452   state_manager_.SetOperationFinished();
453 }
454
455 void PepperFileIOHost::SendOpenErrorReply(
456     ppapi::host::ReplyMessageContext reply_context) {
457   host()->SendReply(reply_context, PpapiPluginMsg_FileIO_OpenReply(0, 0));
458 }
459
460 bool PepperFileIOHost::AddFileToReplyContext(
461     int32_t open_flags,
462     ppapi::host::ReplyMessageContext* reply_context) const {
463   base::ProcessId plugin_process_id =
464       base::GetProcId(browser_ppapi_host_->GetPluginProcessHandle());
465   if (plugin_process_id == base::kNullProcessId)
466     plugin_process_id = resolved_render_process_id_;
467
468   IPC::PlatformFileForTransit transit_file =
469       BrokerGetFileHandleForProcess(file_, plugin_process_id, false);
470   if (transit_file == IPC::InvalidPlatformFileForTransit())
471     return false;
472
473   ppapi::proxy::SerializedHandle file_handle;
474   // A non-zero resource id signals NaClIPCAdapter to create a NaClQuotaDesc.
475   PP_Resource quota_file_io = check_quota_ ? pp_resource() : 0;
476   file_handle.set_file_handle(transit_file, open_flags, quota_file_io);
477   reply_context->params.AppendHandle(file_handle);
478   return true;
479 }
480
481 }  // namespace content