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_internal_file_ref_backend.h"
6
7#include <string>
8
9#include "base/callback.h"
10#include "base/files/file_util.h"
11#include "base/files/file_util_proxy.h"
12#include "content/browser/child_process_security_policy_impl.h"
13#include "content/browser/fileapi/browser_file_system_helper.h"
14#include "content/browser/renderer_host/pepper/pepper_file_system_browser_host.h"
15#include "content/public/browser/browser_context.h"
16#include "content/public/browser/render_process_host.h"
17#include "content/public/browser/storage_partition.h"
18#include "net/base/escape.h"
19#include "ppapi/c/pp_errors.h"
20#include "ppapi/c/pp_file_info.h"
21#include "ppapi/c/pp_instance.h"
22#include "ppapi/c/pp_resource.h"
23#include "ppapi/c/ppb_file_ref.h"
24#include "ppapi/host/dispatch_host_message.h"
25#include "ppapi/host/ppapi_host.h"
26#include "ppapi/proxy/ppapi_messages.h"
27#include "ppapi/shared_impl/file_ref_create_info.h"
28#include "ppapi/shared_impl/file_ref_util.h"
29#include "ppapi/shared_impl/file_type_conversion.h"
30#include "ppapi/shared_impl/scoped_pp_var.h"
31#include "ppapi/shared_impl/time_conversion.h"
32#include "ppapi/shared_impl/var.h"
33#include "ppapi/thunk/enter.h"
34#include "ppapi/thunk/ppb_file_ref_api.h"
35#include "ppapi/thunk/ppb_file_system_api.h"
36#include "storage/browser/fileapi/file_system_operation.h"
37#include "storage/browser/fileapi/file_system_operation_runner.h"
38#include "storage/browser/fileapi/file_system_url.h"
39#include "storage/common/fileapi/file_system_util.h"
40
41using ppapi::host::PpapiHost;
42using ppapi::host::ResourceHost;
43
44namespace content {
45
46PepperInternalFileRefBackend::PepperInternalFileRefBackend(
47    PpapiHost* host,
48    int render_process_id,
49    base::WeakPtr<PepperFileSystemBrowserHost> fs_host,
50    const std::string& path)
51    : host_(host),
52      render_process_id_(render_process_id),
53      fs_host_(fs_host),
54      fs_type_(fs_host->GetType()),
55      path_(path),
56      weak_factory_(this) {
57  ppapi::NormalizeInternalPath(&path_);
58}
59
60PepperInternalFileRefBackend::~PepperInternalFileRefBackend() {}
61
62storage::FileSystemURL PepperInternalFileRefBackend::GetFileSystemURL() const {
63  if (!fs_url_.is_valid() && fs_host_.get() && fs_host_->IsOpened()) {
64    GURL fs_path =
65        fs_host_->GetRootUrl().Resolve(net::EscapePath(path_.substr(1)));
66    scoped_refptr<storage::FileSystemContext> fs_context =
67        GetFileSystemContext();
68    if (fs_context.get())
69      fs_url_ = fs_context->CrackURL(fs_path);
70  }
71  return fs_url_;
72}
73
74base::FilePath PepperInternalFileRefBackend::GetExternalFilePath() const {
75  return base::FilePath();
76}
77
78scoped_refptr<storage::FileSystemContext>
79PepperInternalFileRefBackend::GetFileSystemContext() const {
80  if (!fs_host_.get())
81    return NULL;
82  return fs_host_->GetFileSystemContext();
83}
84
85void PepperInternalFileRefBackend::DidFinish(
86    ppapi::host::ReplyMessageContext context,
87    const IPC::Message& msg,
88    base::File::Error error) {
89  context.params.set_result(ppapi::FileErrorToPepperError(error));
90  host_->SendReply(context, msg);
91}
92
93int32_t PepperInternalFileRefBackend::MakeDirectory(
94    ppapi::host::ReplyMessageContext reply_context,
95    int32_t make_directory_flags) {
96  if (!GetFileSystemURL().is_valid())
97    return PP_ERROR_FAILED;
98
99  GetFileSystemContext()->operation_runner()->CreateDirectory(
100      GetFileSystemURL(),
101      !!(make_directory_flags & PP_MAKEDIRECTORYFLAG_EXCLUSIVE),
102      !!(make_directory_flags & PP_MAKEDIRECTORYFLAG_WITH_ANCESTORS),
103      base::Bind(&PepperInternalFileRefBackend::DidFinish,
104                 weak_factory_.GetWeakPtr(),
105                 reply_context,
106                 PpapiPluginMsg_FileRef_MakeDirectoryReply()));
107  return PP_OK_COMPLETIONPENDING;
108}
109
110int32_t PepperInternalFileRefBackend::Touch(
111    ppapi::host::ReplyMessageContext reply_context,
112    PP_Time last_access_time,
113    PP_Time last_modified_time) {
114  if (!GetFileSystemURL().is_valid())
115    return PP_ERROR_FAILED;
116
117  GetFileSystemContext()->operation_runner()->TouchFile(
118      GetFileSystemURL(),
119      ppapi::PPTimeToTime(last_access_time),
120      ppapi::PPTimeToTime(last_modified_time),
121      base::Bind(&PepperInternalFileRefBackend::DidFinish,
122                 weak_factory_.GetWeakPtr(),
123                 reply_context,
124                 PpapiPluginMsg_FileRef_TouchReply()));
125  return PP_OK_COMPLETIONPENDING;
126}
127
128int32_t PepperInternalFileRefBackend::Delete(
129    ppapi::host::ReplyMessageContext reply_context) {
130  if (!GetFileSystemURL().is_valid())
131    return PP_ERROR_FAILED;
132
133  GetFileSystemContext()->operation_runner()->Remove(
134      GetFileSystemURL(),
135      false,
136      base::Bind(&PepperInternalFileRefBackend::DidFinish,
137                 weak_factory_.GetWeakPtr(),
138                 reply_context,
139                 PpapiPluginMsg_FileRef_DeleteReply()));
140  return PP_OK_COMPLETIONPENDING;
141}
142
143int32_t PepperInternalFileRefBackend::Rename(
144    ppapi::host::ReplyMessageContext reply_context,
145    PepperFileRefHost* new_file_ref) {
146  if (!GetFileSystemURL().is_valid())
147    return PP_ERROR_FAILED;
148
149  storage::FileSystemURL new_url = new_file_ref->GetFileSystemURL();
150  if (!new_url.is_valid())
151    return PP_ERROR_FAILED;
152  if (!new_url.IsInSameFileSystem(GetFileSystemURL()))
153    return PP_ERROR_FAILED;
154
155  GetFileSystemContext()->operation_runner()->Move(
156      GetFileSystemURL(),
157      new_url,
158      storage::FileSystemOperation::OPTION_NONE,
159      base::Bind(&PepperInternalFileRefBackend::DidFinish,
160                 weak_factory_.GetWeakPtr(),
161                 reply_context,
162                 PpapiPluginMsg_FileRef_RenameReply()));
163  return PP_OK_COMPLETIONPENDING;
164}
165
166int32_t PepperInternalFileRefBackend::Query(
167    ppapi::host::ReplyMessageContext reply_context) {
168  if (!GetFileSystemURL().is_valid())
169    return PP_ERROR_FAILED;
170
171  GetFileSystemContext()->operation_runner()->GetMetadata(
172      GetFileSystemURL(),
173      base::Bind(&PepperInternalFileRefBackend::GetMetadataComplete,
174                 weak_factory_.GetWeakPtr(),
175                 reply_context));
176  return PP_OK_COMPLETIONPENDING;
177}
178
179void PepperInternalFileRefBackend::GetMetadataComplete(
180    ppapi::host::ReplyMessageContext reply_context,
181    base::File::Error error,
182    const base::File::Info& file_info) {
183  reply_context.params.set_result(ppapi::FileErrorToPepperError(error));
184
185  PP_FileInfo pp_file_info;
186  if (error == base::File::FILE_OK)
187    ppapi::FileInfoToPepperFileInfo(file_info, fs_type_, &pp_file_info);
188  else
189    memset(&pp_file_info, 0, sizeof(pp_file_info));
190
191  host_->SendReply(reply_context,
192                   PpapiPluginMsg_FileRef_QueryReply(pp_file_info));
193}
194
195int32_t PepperInternalFileRefBackend::ReadDirectoryEntries(
196    ppapi::host::ReplyMessageContext reply_context) {
197  if (!GetFileSystemURL().is_valid())
198    return PP_ERROR_FAILED;
199
200  storage::FileSystemOperation::FileEntryList* accumulated_file_list =
201      new storage::FileSystemOperation::FileEntryList;
202  GetFileSystemContext()->operation_runner()->ReadDirectory(
203      GetFileSystemURL(),
204      base::Bind(&PepperInternalFileRefBackend::ReadDirectoryComplete,
205                 weak_factory_.GetWeakPtr(),
206                 reply_context,
207                 base::Owned(accumulated_file_list)));
208  return PP_OK_COMPLETIONPENDING;
209}
210
211void PepperInternalFileRefBackend::ReadDirectoryComplete(
212    ppapi::host::ReplyMessageContext context,
213    storage::FileSystemOperation::FileEntryList* accumulated_file_list,
214    base::File::Error error,
215    const storage::FileSystemOperation::FileEntryList& file_list,
216    bool has_more) {
217  accumulated_file_list->insert(
218      accumulated_file_list->end(), file_list.begin(), file_list.end());
219  if (has_more)
220    return;
221
222  context.params.set_result(ppapi::FileErrorToPepperError(error));
223
224  std::vector<ppapi::FileRefCreateInfo> infos;
225  std::vector<PP_FileType> file_types;
226  if (error == base::File::FILE_OK && fs_host_.get()) {
227    std::string dir_path = path_;
228    if (dir_path.empty() || dir_path[dir_path.size() - 1] != '/')
229      dir_path += '/';
230
231    for (storage::FileSystemOperation::FileEntryList::const_iterator it =
232             accumulated_file_list->begin();
233         it != accumulated_file_list->end();
234         ++it) {
235      if (it->is_directory)
236        file_types.push_back(PP_FILETYPE_DIRECTORY);
237      else
238        file_types.push_back(PP_FILETYPE_REGULAR);
239
240      ppapi::FileRefCreateInfo info;
241      info.file_system_type = fs_type_;
242      info.file_system_plugin_resource = fs_host_->pp_resource();
243      std::string path =
244          dir_path + storage::FilePathToString(base::FilePath(it->name));
245      info.internal_path = path;
246      info.display_name = ppapi::GetNameForInternalFilePath(path);
247      infos.push_back(info);
248    }
249  }
250
251  host_->SendReply(
252      context,
253      PpapiPluginMsg_FileRef_ReadDirectoryEntriesReply(infos, file_types));
254}
255
256int32_t PepperInternalFileRefBackend::GetAbsolutePath(
257    ppapi::host::ReplyMessageContext reply_context) {
258  host_->SendReply(reply_context,
259                   PpapiPluginMsg_FileRef_GetAbsolutePathReply(path_));
260  return PP_OK_COMPLETIONPENDING;
261}
262
263int32_t PepperInternalFileRefBackend::CanRead() const {
264  storage::FileSystemURL url = GetFileSystemURL();
265  if (!FileSystemURLIsValid(GetFileSystemContext().get(), url))
266    return PP_ERROR_FAILED;
267  if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFileSystemFile(
268          render_process_id_, url)) {
269    return PP_ERROR_NOACCESS;
270  }
271  return PP_OK;
272}
273
274int32_t PepperInternalFileRefBackend::CanWrite() const {
275  storage::FileSystemURL url = GetFileSystemURL();
276  if (!FileSystemURLIsValid(GetFileSystemContext().get(), url))
277    return PP_ERROR_FAILED;
278  if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanWriteFileSystemFile(
279          render_process_id_, url)) {
280    return PP_ERROR_NOACCESS;
281  }
282  return PP_OK;
283}
284
285int32_t PepperInternalFileRefBackend::CanCreate() const {
286  storage::FileSystemURL url = GetFileSystemURL();
287  if (!FileSystemURLIsValid(GetFileSystemContext().get(), url))
288    return PP_ERROR_FAILED;
289  if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanCreateFileSystemFile(
290          render_process_id_, url)) {
291    return PP_ERROR_NOACCESS;
292  }
293  return PP_OK;
294}
295
296int32_t PepperInternalFileRefBackend::CanReadWrite() const {
297  storage::FileSystemURL url = GetFileSystemURL();
298  if (!FileSystemURLIsValid(GetFileSystemContext().get(), url))
299    return PP_ERROR_FAILED;
300  ChildProcessSecurityPolicyImpl* policy =
301      ChildProcessSecurityPolicyImpl::GetInstance();
302  if (!policy->CanReadFileSystemFile(render_process_id_, url) ||
303      !policy->CanWriteFileSystemFile(render_process_id_, url)) {
304    return PP_ERROR_NOACCESS;
305  }
306  return PP_OK;
307}
308
309}  // namespace content
310