1 // Copyright 2014 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 "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
6
7 #include "base/callback.h"
8 #include "base/files/file_path.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "chrome/browser/chromeos/drive/file_errors.h"
11 #include "chrome/browser/chromeos/drive/file_system_interface.h"
12 #include "chrome/browser/chromeos/drive/file_system_util.h"
13 #include "chrome/browser/chromeos/file_manager/app_id.h"
14 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
15 #include "chrome/browser/extensions/extension_util.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/storage_partition.h"
19 #include "google_apis/drive/task_util.h"
20 #include "webkit/browser/fileapi/file_system_context.h"
21
22 namespace file_manager {
23 namespace util {
24
25 namespace {
26
27 // Helper function used to implement GetNonNativeLocalPathMimeType. It extracts
28 // the mime type from the passed Drive resource entry.
GetMimeTypeAfterGetResourceEntry(const base::Callback<void (bool,const std::string &)> & callback,drive::FileError error,scoped_ptr<drive::ResourceEntry> entry)29 void GetMimeTypeAfterGetResourceEntry(
30 const base::Callback<void(bool, const std::string&)>& callback,
31 drive::FileError error,
32 scoped_ptr<drive::ResourceEntry> entry) {
33 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
34
35 if (error != drive::FILE_ERROR_OK || !entry->has_file_specific_info()) {
36 callback.Run(false, std::string());
37 return;
38 }
39 callback.Run(true, entry->file_specific_info().content_mime_type());
40 }
41
42 // Helper function to converts a callback that takes boolean value to that takes
43 // File::Error, by regarding FILE_OK as the only successful value.
BoolCallbackAsFileErrorCallback(const base::Callback<void (bool)> & callback,base::File::Error error)44 void BoolCallbackAsFileErrorCallback(
45 const base::Callback<void(bool)>& callback,
46 base::File::Error error) {
47 return callback.Run(error == base::File::FILE_OK);
48 }
49
50 // Part of PrepareFileOnIOThread. It tries to create a new file if the given
51 // |url| is not already inhabited.
PrepareFileAfterCheckExistOnIOThread(scoped_refptr<fileapi::FileSystemContext> file_system_context,const fileapi::FileSystemURL & url,const fileapi::FileSystemOperation::StatusCallback & callback,base::File::Error error)52 void PrepareFileAfterCheckExistOnIOThread(
53 scoped_refptr<fileapi::FileSystemContext> file_system_context,
54 const fileapi::FileSystemURL& url,
55 const fileapi::FileSystemOperation::StatusCallback& callback,
56 base::File::Error error) {
57 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
58
59 if (error != base::File::FILE_ERROR_NOT_FOUND) {
60 callback.Run(error);
61 return;
62 }
63
64 // Call with the second argument |exclusive| set to false, meaning that it
65 // is not an error even if the file already exists (it can happen if the file
66 // is created after the previous FileExists call and before this CreateFile.)
67 //
68 // Note that the preceding call to FileExists is necessary for handling
69 // read only filesystems that blindly rejects handling CreateFile().
70 file_system_context->operation_runner()->CreateFile(url, false, callback);
71 }
72
73 // Checks whether a file exists at the given |url|, and try creating it if it
74 // is not already there.
PrepareFileOnIOThread(scoped_refptr<fileapi::FileSystemContext> file_system_context,const fileapi::FileSystemURL & url,const base::Callback<void (bool)> & callback)75 void PrepareFileOnIOThread(
76 scoped_refptr<fileapi::FileSystemContext> file_system_context,
77 const fileapi::FileSystemURL& url,
78 const base::Callback<void(bool)>& callback) {
79 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
80
81 file_system_context->operation_runner()->FileExists(
82 url,
83 base::Bind(&PrepareFileAfterCheckExistOnIOThread,
84 file_system_context,
85 url,
86 base::Bind(&BoolCallbackAsFileErrorCallback, callback)));
87 }
88
89 } // namespace
90
IsUnderNonNativeLocalPath(Profile * profile,const base::FilePath & path)91 bool IsUnderNonNativeLocalPath(Profile* profile,
92 const base::FilePath& path) {
93 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
94
95 GURL url;
96 if (!util::ConvertAbsoluteFilePathToFileSystemUrl(
97 profile, path, kFileManagerAppId, &url)) {
98 return false;
99 }
100
101 fileapi::FileSystemURL filesystem_url =
102 GetFileSystemContextForExtensionId(profile,
103 kFileManagerAppId)->CrackURL(url);
104 if (!filesystem_url.is_valid())
105 return false;
106
107 switch (filesystem_url.type()) {
108 case fileapi::kFileSystemTypeNativeLocal:
109 case fileapi::kFileSystemTypeRestrictedNativeLocal:
110 return false;
111 default:
112 // The path indeed corresponds to a mount point not associated with a
113 // native local path.
114 return true;
115 }
116 }
117
GetNonNativeLocalPathMimeType(Profile * profile,const base::FilePath & path,const base::Callback<void (bool,const std::string &)> & callback)118 void GetNonNativeLocalPathMimeType(
119 Profile* profile,
120 const base::FilePath& path,
121 const base::Callback<void(bool, const std::string&)>& callback) {
122 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
123 DCHECK(IsUnderNonNativeLocalPath(profile, path));
124
125 if (!drive::util::IsUnderDriveMountPoint(path)) {
126 // Non-drive mount point does not have mime types as metadata. Just return
127 // success with empty mime type value.
128 content::BrowserThread::PostTask(
129 content::BrowserThread::UI,
130 FROM_HERE,
131 base::Bind(callback, true /* success */, std::string()));
132 return;
133 }
134
135 drive::FileSystemInterface* file_system =
136 drive::util::GetFileSystemByProfile(profile);
137 if (!file_system) {
138 content::BrowserThread::PostTask(
139 content::BrowserThread::UI,
140 FROM_HERE,
141 base::Bind(callback, false, std::string()));
142 return;
143 }
144
145 file_system->GetResourceEntry(
146 drive::util::ExtractDrivePath(path),
147 base::Bind(&GetMimeTypeAfterGetResourceEntry, callback));
148 }
149
IsNonNativeLocalPathDirectory(Profile * profile,const base::FilePath & path,const base::Callback<void (bool)> & callback)150 void IsNonNativeLocalPathDirectory(
151 Profile* profile,
152 const base::FilePath& path,
153 const base::Callback<void(bool)>& callback) {
154 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
155 DCHECK(IsUnderNonNativeLocalPath(profile, path));
156
157 GURL url;
158 if (!util::ConvertAbsoluteFilePathToFileSystemUrl(
159 profile, path, kFileManagerAppId, &url)) {
160 // Posting to the current thread, so that we always call back asynchronously
161 // independent from whether or not the operation succeeds.
162 content::BrowserThread::PostTask(content::BrowserThread::UI,
163 FROM_HERE,
164 base::Bind(callback, false));
165 return;
166 }
167
168 util::CheckIfDirectoryExists(
169 GetFileSystemContextForExtensionId(profile, kFileManagerAppId),
170 url,
171 base::Bind(&BoolCallbackAsFileErrorCallback, callback));
172 }
173
PrepareNonNativeLocalFileForWritableApp(Profile * profile,const base::FilePath & path,const base::Callback<void (bool)> & callback)174 void PrepareNonNativeLocalFileForWritableApp(
175 Profile* profile,
176 const base::FilePath& path,
177 const base::Callback<void(bool)>& callback) {
178 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
179 DCHECK(IsUnderNonNativeLocalPath(profile, path));
180
181 GURL url;
182 if (!util::ConvertAbsoluteFilePathToFileSystemUrl(
183 profile, path, kFileManagerAppId, &url)) {
184 // Posting to the current thread, so that we always call back asynchronously
185 // independent from whether or not the operation succeeds.
186 content::BrowserThread::PostTask(content::BrowserThread::UI,
187 FROM_HERE,
188 base::Bind(callback, false));
189 return;
190 }
191
192 fileapi::FileSystemContext* const context =
193 GetFileSystemContextForExtensionId(profile, kFileManagerAppId);
194 DCHECK(context);
195
196 // Check the existence of a file using file system API implementation on
197 // behalf of the file manager app. We need to grant access beforehand.
198 context->external_backend()->GrantFullAccessToExtension(kFileManagerAppId);
199
200 content::BrowserThread::PostTask(
201 content::BrowserThread::IO,
202 FROM_HERE,
203 base::Bind(&PrepareFileOnIOThread,
204 make_scoped_refptr(context),
205 context->CrackURL(url),
206 google_apis::CreateRelayCallback(callback)));
207 }
208
209 } // namespace util
210 } // namespace file_manager
211