fileapi_worker.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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/drive/fileapi/fileapi_worker.h"
6
7#include "base/files/file_path.h"
8#include "base/logging.h"
9#include "base/task_runner_util.h"
10#include "base/threading/sequenced_worker_pool.h"
11#include "chrome/browser/chromeos/drive/drive.pb.h"
12#include "chrome/browser/chromeos/drive/file_errors.h"
13#include "chrome/browser/chromeos/drive/file_system_interface.h"
14#include "chrome/browser/chromeos/drive/file_system_util.h"
15#include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
16#include "content/public/browser/browser_thread.h"
17#include "storage/browser/fileapi/file_system_url.h"
18#include "storage/common/fileapi/directory_entry.h"
19
20using content::BrowserThread;
21
22namespace drive {
23namespace fileapi_internal {
24namespace {
25
26// The summary of opening mode is:
27// - File::FLAG_OPEN: Open the existing file. Fail if not exists.
28// - File::FLAG_CREATE: Create the file if not exists. Fail if exists.
29// - File::FLAG_OPEN_ALWAYS: Open the existing file. Create a new file
30//     if not exists.
31// - File::FLAG_CREATE_ALWAYS: Create a new file if not exists. If exists
32//     open it with truncate.
33// - File::FLAG_OPEN_TRUNCATE: Open the existing file with truncate.
34//     Fail if not exists.
35OpenMode GetOpenMode(int file_flag) {
36  if (file_flag & (base::File::FLAG_OPEN | base::File::FLAG_OPEN_TRUNCATED))
37    return OPEN_FILE;
38
39  if (file_flag & base::File::FLAG_CREATE)
40    return CREATE_FILE;
41
42  DCHECK(file_flag &
43         (base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_CREATE_ALWAYS));
44  return OPEN_OR_CREATE_FILE;
45}
46
47// Runs |callback| with the File::Error converted from |error|.
48void RunStatusCallbackByFileError(const StatusCallback& callback,
49                                  FileError error) {
50  callback.Run(FileErrorToBaseFileError(error));
51}
52
53// Runs |callback| with arguments converted from |error| and |entry|.
54void RunGetFileInfoCallback(const GetFileInfoCallback& callback,
55                            FileError error,
56                            scoped_ptr<ResourceEntry> entry) {
57  if (error != FILE_ERROR_OK) {
58    callback.Run(FileErrorToBaseFileError(error), base::File::Info());
59    return;
60  }
61
62  DCHECK(entry);
63  base::File::Info file_info;
64  ConvertResourceEntryToFileInfo(*entry, &file_info);
65  callback.Run(base::File::FILE_OK, file_info);
66}
67
68// Runs |callback| with entries.
69void RunReadDirectoryCallbackWithEntries(
70    const ReadDirectoryCallback& callback,
71    scoped_ptr<ResourceEntryVector> resource_entries) {
72  DCHECK(resource_entries);
73
74  std::vector<storage::DirectoryEntry> entries;
75  // Convert drive files to File API's directory entry.
76  entries.reserve(resource_entries->size());
77  for (size_t i = 0; i < resource_entries->size(); ++i) {
78    const ResourceEntry& resource_entry = (*resource_entries)[i];
79    storage::DirectoryEntry entry;
80    entry.name = resource_entry.base_name();
81
82    const PlatformFileInfoProto& file_info = resource_entry.file_info();
83    entry.is_directory = file_info.is_directory();
84    entry.size = file_info.size();
85    entry.last_modified_time =
86        base::Time::FromInternalValue(file_info.last_modified());
87    entries.push_back(entry);
88  }
89
90  callback.Run(base::File::FILE_OK, entries, true /*has_more*/);
91}
92
93// Runs |callback| with |error|.
94void RunReadDirectoryCallbackOnCompletion(const ReadDirectoryCallback& callback,
95                                          FileError error) {
96  callback.Run(FileErrorToBaseFileError(error),
97               std::vector<storage::DirectoryEntry>(),
98               false /*has_more*/);
99}
100
101// Runs |callback| with arguments based on |error|, |local_path| and |entry|.
102void RunCreateSnapshotFileCallback(const CreateSnapshotFileCallback& callback,
103                                   FileError error,
104                                   const base::FilePath& local_path,
105                                   scoped_ptr<ResourceEntry> entry) {
106  if (error != FILE_ERROR_OK) {
107    callback.Run(FileErrorToBaseFileError(error),
108                 base::File::Info(),
109                 base::FilePath(),
110                 storage::ScopedFile::ScopeOutPolicy());
111    return;
112  }
113
114  DCHECK(entry);
115
116  // When reading file, last modified time specified in file info will be
117  // compared to the last modified time of the local version of the drive file.
118  // Since those two values don't generally match (last modification time on the
119  // drive server vs. last modification time of the local, downloaded file), so
120  // we have to opt out from this check. We do this by unsetting last_modified
121  // value in the file info passed to the CreateSnapshot caller.
122  base::File::Info file_info;
123  ConvertResourceEntryToFileInfo(*entry, &file_info);
124  file_info.last_modified = base::Time();
125
126  // If the file is a hosted document, a temporary JSON file is created to
127  // represent the document. The JSON file is not cached and its lifetime
128  // is managed by ShareableFileReference.
129  storage::ScopedFile::ScopeOutPolicy scope_out_policy =
130      entry->file_specific_info().is_hosted_document()
131          ? storage::ScopedFile::DELETE_ON_SCOPE_OUT
132          : storage::ScopedFile::DONT_DELETE_ON_SCOPE_OUT;
133
134  callback.Run(base::File::FILE_OK, file_info, local_path, scope_out_policy);
135}
136
137// Runs |callback| with arguments converted from |error| and |local_path|.
138void RunCreateWritableSnapshotFileCallback(
139    const CreateWritableSnapshotFileCallback& callback,
140    FileError error,
141    const base::FilePath& local_path,
142    const base::Closure& close_callback) {
143  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
144  callback.Run(FileErrorToBaseFileError(error), local_path, close_callback);
145}
146
147// Runs |callback| with |file|.
148void RunOpenFileCallback(const OpenFileCallback& callback,
149                         const base::Closure& close_callback,
150                         base::File file) {
151  callback.Run(file.Pass(), close_callback);
152}
153
154base::File OpenFile(const base::FilePath& path, int flags) {
155  return base::File(path, flags);
156}
157
158// Part of OpenFile(). Called after FileSystem::OpenFile().
159void OpenFileAfterFileSystemOpenFile(int file_flags,
160                                     const OpenFileCallback& callback,
161                                     FileError error,
162                                     const base::FilePath& local_path,
163                                     const base::Closure& close_callback) {
164  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
165
166  if (error != FILE_ERROR_OK) {
167    callback.Run(base::File(FileErrorToBaseFileError(error)), base::Closure());
168    return;
169  }
170
171  // Here, the file should be at |local_path|, but there may be timing issue.
172  // Because the file is managed by Drive file system, so, in order to avoid
173  // unexpected file creation, CREATE, OPEN_ALWAYS and CREATE_ALWAYS are
174  // translated into OPEN or OPEN_TRUNCATED, here. Keep OPEN and OPEN_TRUNCATED
175  // as is.
176  if (file_flags & (base::File::FLAG_CREATE |
177                    base::File::FLAG_OPEN_ALWAYS)) {
178    file_flags &= ~(base::File::FLAG_CREATE |
179                    base::File::FLAG_OPEN_ALWAYS);
180    file_flags |= base::File::FLAG_OPEN;
181  } else if (file_flags & base::File::FLAG_CREATE_ALWAYS) {
182    file_flags &= ~base::File::FLAG_CREATE_ALWAYS;
183    file_flags |= base::File::FLAG_OPEN_TRUNCATED;
184  }
185
186  // Cache file prepared for modification is available. Open it locally.
187  bool posted = base::PostTaskAndReplyWithResult(
188      BrowserThread::GetBlockingPool(), FROM_HERE,
189      base::Bind(&OpenFile, local_path, file_flags),
190      base::Bind(&RunOpenFileCallback, callback, close_callback));
191  DCHECK(posted);
192}
193
194}  // namespace
195
196FileSystemInterface* GetFileSystemFromUrl(const storage::FileSystemURL& url) {
197  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
198
199  Profile* profile = util::ExtractProfileFromPath(url.path());
200  return profile ? util::GetFileSystemByProfile(profile) : NULL;
201}
202
203void RunFileSystemCallback(
204    const FileSystemGetter& file_system_getter,
205    const base::Callback<void(FileSystemInterface*)>& callback,
206    const base::Closure& on_error_callback) {
207  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
208  FileSystemInterface* file_system = file_system_getter.Run();
209
210  if (!file_system) {
211    if (!on_error_callback.is_null())
212      on_error_callback.Run();
213    return;
214  }
215
216  callback.Run(file_system);
217}
218
219void GetFileInfo(const base::FilePath& file_path,
220                 const GetFileInfoCallback& callback,
221                 FileSystemInterface* file_system) {
222  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
223  file_system->GetResourceEntry(
224      file_path,
225      base::Bind(&RunGetFileInfoCallback, callback));
226}
227
228void Copy(const base::FilePath& src_file_path,
229          const base::FilePath& dest_file_path,
230          bool preserve_last_modified,
231          const StatusCallback& callback,
232          FileSystemInterface* file_system) {
233  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
234  file_system->Copy(src_file_path, dest_file_path, preserve_last_modified,
235                    base::Bind(&RunStatusCallbackByFileError, callback));
236}
237
238void Move(const base::FilePath& src_file_path,
239          const base::FilePath& dest_file_path,
240          const StatusCallback& callback,
241          FileSystemInterface* file_system) {
242  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
243  file_system->Move(src_file_path, dest_file_path,
244                    base::Bind(&RunStatusCallbackByFileError, callback));
245}
246
247void CopyInForeignFile(const base::FilePath& src_foreign_file_path,
248                       const base::FilePath& dest_file_path,
249                       const StatusCallback& callback,
250                       FileSystemInterface* file_system) {
251  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
252  file_system->TransferFileFromLocalToRemote(
253      src_foreign_file_path, dest_file_path,
254      base::Bind(&RunStatusCallbackByFileError, callback));
255}
256
257void ReadDirectory(const base::FilePath& file_path,
258                   const ReadDirectoryCallback& callback,
259                   FileSystemInterface* file_system) {
260  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
261  file_system->ReadDirectory(
262      file_path,
263      base::Bind(&RunReadDirectoryCallbackWithEntries, callback),
264      base::Bind(&RunReadDirectoryCallbackOnCompletion, callback));
265}
266
267void Remove(const base::FilePath& file_path,
268            bool is_recursive,
269            const StatusCallback& callback,
270            FileSystemInterface* file_system) {
271  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
272  file_system->Remove(file_path, is_recursive,
273                      base::Bind(&RunStatusCallbackByFileError, callback));
274}
275
276void CreateDirectory(const base::FilePath& file_path,
277                     bool is_exclusive,
278                     bool is_recursive,
279                     const StatusCallback& callback,
280                     FileSystemInterface* file_system) {
281  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
282  file_system->CreateDirectory(
283      file_path, is_exclusive, is_recursive,
284      base::Bind(&RunStatusCallbackByFileError, callback));
285}
286
287void CreateFile(const base::FilePath& file_path,
288                bool is_exclusive,
289                const StatusCallback& callback,
290                FileSystemInterface* file_system) {
291  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
292  file_system->CreateFile(file_path, is_exclusive,
293                          std::string(),  // no mime type; guess from file_path
294                          base::Bind(&RunStatusCallbackByFileError, callback));
295}
296
297void Truncate(const base::FilePath& file_path,
298              int64 length,
299              const StatusCallback& callback,
300              FileSystemInterface* file_system) {
301  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
302  file_system->TruncateFile(
303      file_path, length,
304      base::Bind(&RunStatusCallbackByFileError, callback));
305}
306
307void CreateSnapshotFile(const base::FilePath& file_path,
308                        const CreateSnapshotFileCallback& callback,
309                        FileSystemInterface* file_system) {
310  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
311  file_system->GetFile(file_path,
312                       base::Bind(&RunCreateSnapshotFileCallback, callback));
313}
314
315void CreateWritableSnapshotFile(
316    const base::FilePath& file_path,
317    const CreateWritableSnapshotFileCallback& callback,
318    FileSystemInterface* file_system) {
319  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
320  file_system->OpenFile(
321      file_path,
322      OPEN_FILE,
323      std::string(),  // no mime type; we never create a new file here.
324      base::Bind(&RunCreateWritableSnapshotFileCallback, callback));
325}
326
327void OpenFile(const base::FilePath& file_path,
328              int file_flags,
329              const OpenFileCallback& callback,
330              FileSystemInterface* file_system) {
331  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
332
333  // Returns an error if any unsupported flag is found.
334  if (file_flags & ~(base::File::FLAG_OPEN |
335                     base::File::FLAG_CREATE |
336                     base::File::FLAG_OPEN_ALWAYS |
337                     base::File::FLAG_CREATE_ALWAYS |
338                     base::File::FLAG_OPEN_TRUNCATED |
339                     base::File::FLAG_READ |
340                     base::File::FLAG_WRITE |
341                     base::File::FLAG_WRITE_ATTRIBUTES |
342                     base::File::FLAG_APPEND)) {
343    base::MessageLoopProxy::current()->PostTask(
344        FROM_HERE,
345        base::Bind(callback,
346                   Passed(base::File(base::File::FILE_ERROR_FAILED)),
347                   base::Closure()));
348    return;
349  }
350
351  file_system->OpenFile(
352      file_path, GetOpenMode(file_flags),
353      std::string(),  // no mime type; guess from file_path
354      base::Bind(&OpenFileAfterFileSystemOpenFile, file_flags, callback));
355}
356
357void TouchFile(const base::FilePath& file_path,
358               const base::Time& last_access_time,
359               const base::Time& last_modified_time,
360               const StatusCallback& callback,
361               FileSystemInterface* file_system) {
362  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
363  file_system->TouchFile(file_path, last_access_time, last_modified_time,
364                         base::Bind(&RunStatusCallbackByFileError, callback));
365
366}
367
368}  // namespace fileapi_internal
369}  // namespace drive
370