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.
5 #include "chrome/browser/chromeos/drive/fileapi_worker.h"
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"
19 using content::BrowserThread
;
22 namespace fileapi_internal
{
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
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.
34 OpenMode
GetOpenMode(int file_flag
) {
35 if (file_flag
& (base::PLATFORM_FILE_OPEN
|
36 base::PLATFORM_FILE_OPEN_TRUNCATED
))
39 if (file_flag
& base::PLATFORM_FILE_CREATE
)
42 DCHECK(file_flag
& (base::PLATFORM_FILE_OPEN_ALWAYS
|
43 base::PLATFORM_FILE_CREATE_ALWAYS
));
44 return OPEN_OR_CREATE_FILE
;
47 // Runs |callback| with the PlatformFileError converted from |error|.
48 void RunStatusCallbackByFileError(const StatusCallback
& callback
,
50 callback
.Run(FileErrorToPlatformError(error
));
53 // Runs |callback| with arguments converted from |error| and |entry|.
54 void RunGetFileInfoCallback(const GetFileInfoCallback
& callback
,
56 scoped_ptr
<ResourceEntry
> entry
) {
57 if (error
!= FILE_ERROR_OK
) {
58 callback
.Run(FileErrorToPlatformError(error
), base::PlatformFileInfo());
63 base::PlatformFileInfo file_info
;
64 ConvertResourceEntryToPlatformFileInfo(*entry
, &file_info
);
65 callback
.Run(base::PLATFORM_FILE_OK
, file_info
);
68 // Runs |callback| with arguments converted from |error| and |resource_entries|.
69 void RunReadDirectoryCallback(
70 const ReadDirectoryCallback
& callback
,
72 scoped_ptr
<ResourceEntryVector
> resource_entries
) {
73 if (error
!= FILE_ERROR_OK
) {
74 callback
.Run(FileErrorToPlatformError(error
),
75 std::vector
<fileapi::DirectoryEntry
>(), false);
79 DCHECK(resource_entries
);
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();
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
);
97 callback
.Run(base::PLATFORM_FILE_OK
, entries
, false);
100 // Runs |callback| with arguments based on |error|, |local_path| and |entry|.
101 void RunCreateSnapshotFileCallback(const CreateSnapshotFileCallback
& callback
,
103 const base::FilePath
& local_path
,
104 scoped_ptr
<ResourceEntry
> entry
) {
105 if (error
!= FILE_ERROR_OK
) {
107 FileErrorToPlatformError(error
),
108 base::PlatformFileInfo(), base::FilePath(),
109 webkit_blob::ScopedFile::ScopeOutPolicy());
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();
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
;
133 callback
.Run(base::PLATFORM_FILE_OK
, file_info
, local_path
, scope_out_policy
);
136 // Runs |callback| with arguments converted from |error| and |local_path|.
137 void RunCreateWritableSnapshotFileCallback(
138 const CreateWritableSnapshotFileCallback
& callback
,
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
);
146 // Runs |callback| with |error| and |platform_file|.
147 void 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
);
154 // Part of OpenFile(). Called after FileSystem::OpenFile().
155 void OpenFileAfterFileSystemOpenFile(int file_flags
,
156 const OpenFileCallback
& callback
,
158 const base::FilePath
& local_path
,
159 const base::Closure
& close_callback
) {
160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
162 if (error
!= FILE_ERROR_OK
) {
163 callback
.Run(FileErrorToPlatformError(error
),
164 base::kInvalidPlatformFileValue
,
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
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
;
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
)));
198 void 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();
206 if (!on_error_callback
.is_null())
207 on_error_callback
.Run();
211 callback
.Run(file_system
);
214 void GetFileInfo(const base::FilePath
& file_path
,
215 const GetFileInfoCallback
& callback
,
216 FileSystemInterface
* file_system
) {
217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
218 file_system
->GetResourceEntry(
220 base::Bind(&RunGetFileInfoCallback
, callback
));
223 void Copy(const base::FilePath
& src_file_path
,
224 const base::FilePath
& dest_file_path
,
225 bool preserve_last_modified
,
226 const StatusCallback
& callback
,
227 FileSystemInterface
* file_system
) {
228 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
229 file_system
->Copy(src_file_path
, dest_file_path
, preserve_last_modified
,
230 base::Bind(&RunStatusCallbackByFileError
, callback
));
233 void Move(const base::FilePath
& src_file_path
,
234 const base::FilePath
& dest_file_path
,
235 bool preserve_last_modified
,
236 const StatusCallback
& callback
,
237 FileSystemInterface
* file_system
) {
238 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
239 file_system
->Move(src_file_path
, dest_file_path
, preserve_last_modified
,
240 base::Bind(&RunStatusCallbackByFileError
, callback
));
243 void CopyInForeignFile(const base::FilePath
& src_foreign_file_path
,
244 const base::FilePath
& dest_file_path
,
245 const StatusCallback
& callback
,
246 FileSystemInterface
* file_system
) {
247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
248 file_system
->TransferFileFromLocalToRemote(
249 src_foreign_file_path
, dest_file_path
,
250 base::Bind(&RunStatusCallbackByFileError
, callback
));
253 void ReadDirectory(const base::FilePath
& file_path
,
254 const ReadDirectoryCallback
& callback
,
255 FileSystemInterface
* file_system
) {
256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
257 file_system
->ReadDirectory(file_path
,
258 base::Bind(&RunReadDirectoryCallback
, callback
));
261 void Remove(const base::FilePath
& file_path
,
263 const StatusCallback
& callback
,
264 FileSystemInterface
* file_system
) {
265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
266 file_system
->Remove(file_path
, is_recursive
,
267 base::Bind(&RunStatusCallbackByFileError
, callback
));
270 void CreateDirectory(const base::FilePath
& file_path
,
273 const StatusCallback
& callback
,
274 FileSystemInterface
* file_system
) {
275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
276 file_system
->CreateDirectory(
277 file_path
, is_exclusive
, is_recursive
,
278 base::Bind(&RunStatusCallbackByFileError
, callback
));
281 void CreateFile(const base::FilePath
& file_path
,
283 const StatusCallback
& callback
,
284 FileSystemInterface
* file_system
) {
285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
286 file_system
->CreateFile(file_path
, is_exclusive
,
287 std::string(), // no mime type; guess from file_path
288 base::Bind(&RunStatusCallbackByFileError
, callback
));
291 void Truncate(const base::FilePath
& file_path
,
293 const StatusCallback
& callback
,
294 FileSystemInterface
* file_system
) {
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
296 file_system
->TruncateFile(
298 base::Bind(&RunStatusCallbackByFileError
, callback
));
301 void CreateSnapshotFile(const base::FilePath
& file_path
,
302 const CreateSnapshotFileCallback
& callback
,
303 FileSystemInterface
* file_system
) {
304 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
305 file_system
->GetFile(file_path
,
306 base::Bind(&RunCreateSnapshotFileCallback
, callback
));
309 void CreateWritableSnapshotFile(
310 const base::FilePath
& file_path
,
311 const CreateWritableSnapshotFileCallback
& callback
,
312 FileSystemInterface
* file_system
) {
313 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
314 file_system
->OpenFile(
317 std::string(), // no mime type; we never create a new file here.
318 base::Bind(&RunCreateWritableSnapshotFileCallback
, callback
));
321 void OpenFile(const base::FilePath
& file_path
,
323 const OpenFileCallback
& callback
,
324 FileSystemInterface
* file_system
) {
325 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
327 // Returns an error if any unsupported flag is found.
328 if (file_flags
& ~(base::PLATFORM_FILE_OPEN
|
329 base::PLATFORM_FILE_CREATE
|
330 base::PLATFORM_FILE_OPEN_ALWAYS
|
331 base::PLATFORM_FILE_CREATE_ALWAYS
|
332 base::PLATFORM_FILE_OPEN_TRUNCATED
|
333 base::PLATFORM_FILE_READ
|
334 base::PLATFORM_FILE_WRITE
|
335 base::PLATFORM_FILE_WRITE_ATTRIBUTES
|
336 base::PLATFORM_FILE_APPEND
)) {
337 base::MessageLoopProxy::current()->PostTask(
340 base::PLATFORM_FILE_ERROR_FAILED
,
341 base::kInvalidPlatformFileValue
,
346 file_system
->OpenFile(
347 file_path
, GetOpenMode(file_flags
),
348 std::string(), // no mime type; guess from file_path
349 base::Bind(&OpenFileAfterFileSystemOpenFile
, file_flags
, callback
));
352 void TouchFile(const base::FilePath
& file_path
,
353 const base::Time
& last_access_time
,
354 const base::Time
& last_modified_time
,
355 const StatusCallback
& callback
,
356 FileSystemInterface
* file_system
) {
357 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
358 file_system
->TouchFile(file_path
, last_access_time
, last_modified_time
,
359 base::Bind(&RunStatusCallbackByFileError
, callback
));
363 } // namespace fileapi_internal