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