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