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.
5 #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
7 #include "base/files/file.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "content/public/browser/child_process_security_policy.h"
12 #include "content/public/browser/render_process_host.h"
13 #include "extensions/browser/extension_prefs.h"
14 #include "extensions/browser/granted_file_entry.h"
15 #include "extensions/common/permissions/permissions_data.h"
16 #include "net/base/mime_util.h"
17 #include "storage/browser/fileapi/isolated_context.h"
18 #include "storage/common/fileapi/file_system_mount_option.h"
19 #include "storage/common/fileapi/file_system_types.h"
21 #if defined(OS_CHROMEOS)
22 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
25 namespace extensions
{
27 namespace app_file_handler_util
{
29 const char kInvalidParameters
[] = "Invalid parameters";
30 const char kSecurityError
[] = "Security error";
34 bool FileHandlerCanHandleFileWithExtension(
35 const FileHandlerInfo
& handler
,
36 const base::FilePath
& path
) {
37 for (std::set
<std::string
>::const_iterator extension
=
38 handler
.extensions
.begin();
39 extension
!= handler
.extensions
.end(); ++extension
) {
40 if (*extension
== "*")
43 // Accept files whose extension or combined extension (e.g. ".tar.gz")
44 // match the supported extensions of file handler.
45 base::FilePath::StringType
handler_extention(
46 base::FilePath::kExtensionSeparator
+
47 base::FilePath::FromUTF8Unsafe(*extension
).value());
48 if (base::FilePath::CompareEqualIgnoreCase(
49 handler_extention
, path
.Extension()) ||
50 base::FilePath::CompareEqualIgnoreCase(
51 handler_extention
, path
.FinalExtension())) {
55 // Also accept files with no extension for handlers that support an
56 // empty extension, i.e. both "foo" and "foo." match.
57 if (extension
->empty() &&
58 path
.MatchesExtension(base::FilePath::StringType())) {
65 bool FileHandlerCanHandleFileWithMimeType(
66 const FileHandlerInfo
& handler
,
67 const std::string
& mime_type
) {
68 for (std::set
<std::string
>::const_iterator type
= handler
.types
.begin();
69 type
!= handler
.types
.end(); ++type
) {
70 if (net::MatchesMimeType(*type
, mime_type
))
76 bool PrepareNativeLocalFileForWritableApp(const base::FilePath
& path
,
78 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
81 if (base::PathExists(path
) && base::IsLink(path
))
85 return base::DirectoryExists(path
);
87 // Create the file if it doesn't already exist.
88 int creation_flags
= base::File::FLAG_OPEN_ALWAYS
| base::File::FLAG_READ
;
89 base::File
file(path
, creation_flags
);
91 return file
.IsValid();
94 // Checks whether a list of paths are all OK for writing and calls a provided
95 // on_success or on_failure callback when done. A file is OK for writing if it
96 // is not a symlink, is not in a blacklisted path and can be opened for writing;
97 // files are created if they do not exist.
98 class WritableFileChecker
99 : public base::RefCountedThreadSafe
<WritableFileChecker
> {
102 const std::vector
<base::FilePath
>& paths
,
105 const base::Closure
& on_success
,
106 const base::Callback
<void(const base::FilePath
&)>& on_failure
);
111 friend class base::RefCountedThreadSafe
<WritableFileChecker
>;
112 virtual ~WritableFileChecker();
114 // Called when a work item is completed. If all work items are done, this
115 // calls the success or failure callback.
118 // Reports an error in completing a work item. This may be called more than
119 // once, but only the last message will be retained.
120 void Error(const base::FilePath
& error_path
);
122 void CheckLocalWritableFiles();
124 // Called when processing a file is completed with either a success or an
126 void OnPrepareFileDone(const base::FilePath
& path
, bool success
);
128 const std::vector
<base::FilePath
> paths_
;
130 const bool is_directory_
;
131 int outstanding_tasks_
;
132 base::FilePath error_path_
;
133 base::Closure on_success_
;
134 base::Callback
<void(const base::FilePath
&)> on_failure_
;
137 WritableFileChecker::WritableFileChecker(
138 const std::vector
<base::FilePath
>& paths
,
141 const base::Closure
& on_success
,
142 const base::Callback
<void(const base::FilePath
&)>& on_failure
)
145 is_directory_(is_directory
),
146 outstanding_tasks_(1),
147 on_success_(on_success
),
148 on_failure_(on_failure
) {}
150 void WritableFileChecker::Check() {
151 outstanding_tasks_
= paths_
.size();
152 for (const auto& path
: paths_
) {
153 #if defined(OS_CHROMEOS)
154 if (file_manager::util::IsUnderNonNativeLocalPath(profile_
, path
)) {
156 file_manager::util::IsNonNativeLocalPathDirectory(
158 base::Bind(&WritableFileChecker::OnPrepareFileDone
, this, path
));
160 file_manager::util::PrepareNonNativeLocalFileForWritableApp(
162 base::Bind(&WritableFileChecker::OnPrepareFileDone
, this, path
));
167 content::BrowserThread::PostTaskAndReplyWithResult(
168 content::BrowserThread::FILE, FROM_HERE
,
169 base::Bind(&PrepareNativeLocalFileForWritableApp
, path
,
171 base::Bind(&WritableFileChecker::OnPrepareFileDone
, this, path
));
175 WritableFileChecker::~WritableFileChecker() {}
177 void WritableFileChecker::TaskDone() {
178 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
179 if (--outstanding_tasks_
== 0) {
180 if (error_path_
.empty())
183 on_failure_
.Run(error_path_
);
187 // Reports an error in completing a work item. This may be called more than
188 // once, but only the last message will be retained.
189 void WritableFileChecker::Error(const base::FilePath
& error_path
) {
190 DCHECK(!error_path
.empty());
191 error_path_
= error_path
;
195 void WritableFileChecker::OnPrepareFileDone(const base::FilePath
& path
,
205 const FileHandlerInfo
* FileHandlerForId(const Extension
& app
,
206 const std::string
& handler_id
) {
207 const FileHandlersInfo
* file_handlers
= FileHandlers::GetFileHandlers(&app
);
211 for (FileHandlersInfo::const_iterator i
= file_handlers
->begin();
212 i
!= file_handlers
->end(); i
++) {
213 if (i
->id
== handler_id
)
219 const FileHandlerInfo
* FirstFileHandlerForFile(
220 const Extension
& app
,
221 const std::string
& mime_type
,
222 const base::FilePath
& path
) {
223 const FileHandlersInfo
* file_handlers
= FileHandlers::GetFileHandlers(&app
);
227 for (FileHandlersInfo::const_iterator i
= file_handlers
->begin();
228 i
!= file_handlers
->end(); i
++) {
229 if (FileHandlerCanHandleFile(*i
, mime_type
, path
))
235 std::vector
<const FileHandlerInfo
*> FindFileHandlersForFiles(
236 const Extension
& app
, const PathAndMimeTypeSet
& files
) {
237 std::vector
<const FileHandlerInfo
*> handlers
;
241 // Look for file handlers which can handle all the MIME types specified.
242 const FileHandlersInfo
* file_handlers
= FileHandlers::GetFileHandlers(&app
);
246 for (FileHandlersInfo::const_iterator data
= file_handlers
->begin();
247 data
!= file_handlers
->end(); ++data
) {
248 bool handles_all_types
= true;
249 for (PathAndMimeTypeSet::const_iterator it
= files
.begin();
250 it
!= files
.end(); ++it
) {
251 if (!FileHandlerCanHandleFile(*data
, it
->second
, it
->first
)) {
252 handles_all_types
= false;
256 if (handles_all_types
)
257 handlers
.push_back(&*data
);
262 bool FileHandlerCanHandleFile(
263 const FileHandlerInfo
& handler
,
264 const std::string
& mime_type
,
265 const base::FilePath
& path
) {
266 return FileHandlerCanHandleFileWithMimeType(handler
, mime_type
) ||
267 FileHandlerCanHandleFileWithExtension(handler
, path
);
270 GrantedFileEntry
CreateFileEntry(
272 const Extension
* extension
,
274 const base::FilePath
& path
,
276 GrantedFileEntry result
;
277 storage::IsolatedContext
* isolated_context
=
278 storage::IsolatedContext::GetInstance();
279 DCHECK(isolated_context
);
281 result
.filesystem_id
= isolated_context
->RegisterFileSystemForPath(
282 storage::kFileSystemTypeNativeForPlatformApp
,
285 &result
.registered_name
);
287 content::ChildProcessSecurityPolicy
* policy
=
288 content::ChildProcessSecurityPolicy::GetInstance();
289 policy
->GrantReadFileSystem(renderer_id
, result
.filesystem_id
);
290 if (HasFileSystemWritePermission(extension
)) {
292 policy
->GrantCreateReadWriteFileSystem(renderer_id
, result
.filesystem_id
);
294 policy
->GrantWriteFileSystem(renderer_id
, result
.filesystem_id
);
295 policy
->GrantDeleteFromFileSystem(renderer_id
, result
.filesystem_id
);
299 result
.id
= result
.filesystem_id
+ ":" + result
.registered_name
;
303 void PrepareFilesForWritableApp(
304 const std::vector
<base::FilePath
>& paths
,
307 const base::Closure
& on_success
,
308 const base::Callback
<void(const base::FilePath
&)>& on_failure
) {
309 scoped_refptr
<WritableFileChecker
> checker(new WritableFileChecker(
310 paths
, profile
, is_directory
, on_success
, on_failure
));
314 bool HasFileSystemWritePermission(const Extension
* extension
) {
315 return extension
->permissions_data()->HasAPIPermission(
316 APIPermission::kFileSystemWrite
);
319 bool ValidateFileEntryAndGetPath(
320 const std::string
& filesystem_name
,
321 const std::string
& filesystem_path
,
322 const content::RenderViewHost
* render_view_host
,
323 base::FilePath
* file_path
,
324 std::string
* error
) {
325 if (filesystem_path
.empty()) {
326 *error
= kInvalidParameters
;
330 std::string filesystem_id
;
331 if (!storage::CrackIsolatedFileSystemName(filesystem_name
, &filesystem_id
)) {
332 *error
= kInvalidParameters
;
336 // Only return the display path if the process has read access to the
338 content::ChildProcessSecurityPolicy
* policy
=
339 content::ChildProcessSecurityPolicy::GetInstance();
340 if (!policy
->CanReadFileSystem(render_view_host
->GetProcess()->GetID(),
342 *error
= kSecurityError
;
346 storage::IsolatedContext
* context
= storage::IsolatedContext::GetInstance();
347 base::FilePath relative_path
=
348 base::FilePath::FromUTF8Unsafe(filesystem_path
);
349 base::FilePath virtual_path
= context
->CreateVirtualRootPath(filesystem_id
)
350 .Append(relative_path
);
351 storage::FileSystemType type
;
352 storage::FileSystemMountOption mount_option
;
353 std::string cracked_id
;
354 if (!context
->CrackVirtualPath(
355 virtual_path
, &filesystem_id
, &type
, &cracked_id
, file_path
,
357 *error
= kInvalidParameters
;
361 // The file system API is only intended to operate on file entries that
362 // correspond to a native file, selected by the user so only allow file
363 // systems returned by the file system API or from a drag and drop operation.
364 if (type
!= storage::kFileSystemTypeNativeForPlatformApp
&&
365 type
!= storage::kFileSystemTypeDragged
) {
366 *error
= kInvalidParameters
;
373 } // namespace app_file_handler_util
375 } // namespace extensions