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 "extensions/browser/extension_prefs.h"
13 #include "extensions/browser/granted_file_entry.h"
14 #include "extensions/common/permissions/permissions_data.h"
15 #include "net/base/mime_util.h"
16 #include "storage/browser/fileapi/isolated_context.h"
17 #include "storage/common/fileapi/file_system_mount_option.h"
18 #include "storage/common/fileapi/file_system_types.h"
20 #if defined(OS_CHROMEOS)
21 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
24 namespace extensions
{
26 namespace app_file_handler_util
{
28 const char kInvalidParameters
[] = "Invalid parameters";
29 const char kSecurityError
[] = "Security error";
33 bool FileHandlerCanHandleFileWithExtension(
34 const FileHandlerInfo
& handler
,
35 const base::FilePath
& path
) {
36 for (std::set
<std::string
>::const_iterator extension
=
37 handler
.extensions
.begin();
38 extension
!= handler
.extensions
.end(); ++extension
) {
39 if (*extension
== "*")
42 // Accept files whose extension or combined extension (e.g. ".tar.gz")
43 // match the supported extensions of file handler.
44 base::FilePath::StringType
handler_extention(
45 base::FilePath::kExtensionSeparator
+
46 base::FilePath::FromUTF8Unsafe(*extension
).value());
47 if (base::FilePath::CompareEqualIgnoreCase(
48 handler_extention
, path
.Extension()) ||
49 base::FilePath::CompareEqualIgnoreCase(
50 handler_extention
, path
.FinalExtension())) {
54 // Also accept files with no extension for handlers that support an
55 // empty extension, i.e. both "foo" and "foo." match.
56 if (extension
->empty() &&
57 path
.MatchesExtension(base::FilePath::StringType())) {
64 bool FileHandlerCanHandleFileWithMimeType(
65 const FileHandlerInfo
& handler
,
66 const std::string
& mime_type
) {
67 for (std::set
<std::string
>::const_iterator type
= handler
.types
.begin();
68 type
!= handler
.types
.end(); ++type
) {
69 if (net::MatchesMimeType(*type
, mime_type
))
75 bool PrepareNativeLocalFileForWritableApp(const base::FilePath
& path
,
77 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
80 if (base::PathExists(path
) && base::IsLink(path
))
84 return base::DirectoryExists(path
);
86 // Create the file if it doesn't already exist.
87 int creation_flags
= base::File::FLAG_OPEN_ALWAYS
| base::File::FLAG_READ
;
88 base::File
file(path
, creation_flags
);
90 return file
.IsValid();
93 // Checks whether a list of paths are all OK for writing and calls a provided
94 // on_success or on_failure callback when done. A file is OK for writing if it
95 // is not a symlink, is not in a blacklisted path and can be opened for writing;
96 // files are created if they do not exist.
97 class WritableFileChecker
98 : public base::RefCountedThreadSafe
<WritableFileChecker
> {
101 const std::vector
<base::FilePath
>& paths
,
104 const base::Closure
& on_success
,
105 const base::Callback
<void(const base::FilePath
&)>& on_failure
);
110 friend class base::RefCountedThreadSafe
<WritableFileChecker
>;
111 virtual ~WritableFileChecker();
113 // Called when a work item is completed. If all work items are done, this
114 // calls the success or failure callback.
117 // Reports an error in completing a work item. This may be called more than
118 // once, but only the last message will be retained.
119 void Error(const base::FilePath
& error_path
);
121 void CheckLocalWritableFiles();
123 // Called when processing a file is completed with either a success or an
125 void OnPrepareFileDone(const base::FilePath
& path
, bool success
);
127 const std::vector
<base::FilePath
> paths_
;
129 const bool is_directory_
;
130 int outstanding_tasks_
;
131 base::FilePath error_path_
;
132 base::Closure on_success_
;
133 base::Callback
<void(const base::FilePath
&)> on_failure_
;
136 WritableFileChecker::WritableFileChecker(
137 const std::vector
<base::FilePath
>& paths
,
140 const base::Closure
& on_success
,
141 const base::Callback
<void(const base::FilePath
&)>& on_failure
)
144 is_directory_(is_directory
),
145 outstanding_tasks_(1),
146 on_success_(on_success
),
147 on_failure_(on_failure
) {}
149 void WritableFileChecker::Check() {
150 outstanding_tasks_
= paths_
.size();
151 for (const auto& path
: paths_
) {
152 #if defined(OS_CHROMEOS)
153 if (file_manager::util::IsUnderNonNativeLocalPath(profile_
, path
)) {
155 file_manager::util::IsNonNativeLocalPathDirectory(
157 base::Bind(&WritableFileChecker::OnPrepareFileDone
, this, path
));
159 file_manager::util::PrepareNonNativeLocalFileForWritableApp(
161 base::Bind(&WritableFileChecker::OnPrepareFileDone
, this, path
));
166 content::BrowserThread::PostTaskAndReplyWithResult(
167 content::BrowserThread::FILE, FROM_HERE
,
168 base::Bind(&PrepareNativeLocalFileForWritableApp
, path
,
170 base::Bind(&WritableFileChecker::OnPrepareFileDone
, this, path
));
174 WritableFileChecker::~WritableFileChecker() {}
176 void WritableFileChecker::TaskDone() {
177 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
178 if (--outstanding_tasks_
== 0) {
179 if (error_path_
.empty())
182 on_failure_
.Run(error_path_
);
186 // Reports an error in completing a work item. This may be called more than
187 // once, but only the last message will be retained.
188 void WritableFileChecker::Error(const base::FilePath
& error_path
) {
189 DCHECK(!error_path
.empty());
190 error_path_
= error_path
;
194 void WritableFileChecker::OnPrepareFileDone(const base::FilePath
& path
,
204 const FileHandlerInfo
* FileHandlerForId(const Extension
& app
,
205 const std::string
& handler_id
) {
206 const FileHandlersInfo
* file_handlers
= FileHandlers::GetFileHandlers(&app
);
210 for (FileHandlersInfo::const_iterator i
= file_handlers
->begin();
211 i
!= file_handlers
->end(); i
++) {
212 if (i
->id
== handler_id
)
218 const FileHandlerInfo
* FirstFileHandlerForFile(
219 const Extension
& app
,
220 const std::string
& mime_type
,
221 const base::FilePath
& path
) {
222 const FileHandlersInfo
* file_handlers
= FileHandlers::GetFileHandlers(&app
);
226 for (FileHandlersInfo::const_iterator i
= file_handlers
->begin();
227 i
!= file_handlers
->end(); i
++) {
228 if (FileHandlerCanHandleFile(*i
, mime_type
, path
))
234 std::vector
<const FileHandlerInfo
*> FindFileHandlersForFiles(
235 const Extension
& app
, const PathAndMimeTypeSet
& files
) {
236 std::vector
<const FileHandlerInfo
*> handlers
;
240 // Look for file handlers which can handle all the MIME types specified.
241 const FileHandlersInfo
* file_handlers
= FileHandlers::GetFileHandlers(&app
);
245 for (FileHandlersInfo::const_iterator data
= file_handlers
->begin();
246 data
!= file_handlers
->end(); ++data
) {
247 bool handles_all_types
= true;
248 for (PathAndMimeTypeSet::const_iterator it
= files
.begin();
249 it
!= files
.end(); ++it
) {
250 if (!FileHandlerCanHandleFile(*data
, it
->second
, it
->first
)) {
251 handles_all_types
= false;
255 if (handles_all_types
)
256 handlers
.push_back(&*data
);
261 bool FileHandlerCanHandleFile(
262 const FileHandlerInfo
& handler
,
263 const std::string
& mime_type
,
264 const base::FilePath
& path
) {
265 return FileHandlerCanHandleFileWithMimeType(handler
, mime_type
) ||
266 FileHandlerCanHandleFileWithExtension(handler
, path
);
269 GrantedFileEntry
CreateFileEntry(
271 const Extension
* extension
,
273 const base::FilePath
& path
,
275 GrantedFileEntry result
;
276 storage::IsolatedContext
* isolated_context
=
277 storage::IsolatedContext::GetInstance();
278 DCHECK(isolated_context
);
280 result
.filesystem_id
= isolated_context
->RegisterFileSystemForPath(
281 storage::kFileSystemTypeNativeForPlatformApp
,
284 &result
.registered_name
);
286 content::ChildProcessSecurityPolicy
* policy
=
287 content::ChildProcessSecurityPolicy::GetInstance();
288 policy
->GrantReadFileSystem(renderer_id
, result
.filesystem_id
);
289 if (HasFileSystemWritePermission(extension
)) {
291 policy
->GrantCreateReadWriteFileSystem(renderer_id
, result
.filesystem_id
);
293 policy
->GrantWriteFileSystem(renderer_id
, result
.filesystem_id
);
294 policy
->GrantDeleteFromFileSystem(renderer_id
, result
.filesystem_id
);
298 result
.id
= result
.filesystem_id
+ ":" + result
.registered_name
;
302 void PrepareFilesForWritableApp(
303 const std::vector
<base::FilePath
>& paths
,
306 const base::Closure
& on_success
,
307 const base::Callback
<void(const base::FilePath
&)>& on_failure
) {
308 scoped_refptr
<WritableFileChecker
> checker(new WritableFileChecker(
309 paths
, profile
, is_directory
, on_success
, on_failure
));
313 bool HasFileSystemWritePermission(const Extension
* extension
) {
314 return extension
->permissions_data()->HasAPIPermission(
315 APIPermission::kFileSystemWrite
);
318 bool ValidateFileEntryAndGetPath(const std::string
& filesystem_name
,
319 const std::string
& filesystem_path
,
320 int render_process_id
,
321 base::FilePath
* file_path
,
322 std::string
* error
) {
323 if (filesystem_path
.empty()) {
324 *error
= kInvalidParameters
;
328 std::string filesystem_id
;
329 if (!storage::CrackIsolatedFileSystemName(filesystem_name
, &filesystem_id
)) {
330 *error
= kInvalidParameters
;
334 // Only return the display path if the process has read access to the
336 content::ChildProcessSecurityPolicy
* policy
=
337 content::ChildProcessSecurityPolicy::GetInstance();
338 if (!policy
->CanReadFileSystem(render_process_id
, filesystem_id
)) {
339 *error
= kSecurityError
;
343 storage::IsolatedContext
* context
= storage::IsolatedContext::GetInstance();
344 base::FilePath relative_path
=
345 base::FilePath::FromUTF8Unsafe(filesystem_path
);
346 base::FilePath virtual_path
= context
->CreateVirtualRootPath(filesystem_id
)
347 .Append(relative_path
);
348 storage::FileSystemType type
;
349 storage::FileSystemMountOption mount_option
;
350 std::string cracked_id
;
351 if (!context
->CrackVirtualPath(
352 virtual_path
, &filesystem_id
, &type
, &cracked_id
, file_path
,
354 *error
= kInvalidParameters
;
358 // The file system API is only intended to operate on file entries that
359 // correspond to a native file, selected by the user so only allow file
360 // systems returned by the file system API or from a drag and drop operation.
361 if (type
!= storage::kFileSystemTypeNativeForPlatformApp
&&
362 type
!= storage::kFileSystemTypeDragged
) {
363 *error
= kInvalidParameters
;
370 } // namespace app_file_handler_util
372 } // namespace extensions