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.
5 #include "chrome/browser/chromeos/drive/fileapi/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/thread_task_runner_handle.h"
11 #include "base/threading/sequenced_worker_pool.h"
12 #include "chrome/browser/chromeos/drive/file_system_interface.h"
13 #include "chrome/browser/chromeos/drive/file_system_util.h"
14 #include "components/drive/drive.pb.h"
15 #include "components/drive/file_errors.h"
16 #include "components/drive/resource_entry_conversion.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "storage/browser/fileapi/file_system_url.h"
19 #include "storage/common/fileapi/directory_entry.h"
21 using content::BrowserThread
;
24 namespace fileapi_internal
{
27 // The summary of opening mode is:
28 // - File::FLAG_OPEN: Open the existing file. Fail if not exists.
29 // - File::FLAG_CREATE: Create the file if not exists. Fail if exists.
30 // - File::FLAG_OPEN_ALWAYS: Open the existing file. Create a new file
32 // - File::FLAG_CREATE_ALWAYS: Create a new file if not exists. If exists
33 // open it with truncate.
34 // - File::FLAG_OPEN_TRUNCATE: Open the existing file with truncate.
35 // Fail if not exists.
36 OpenMode
GetOpenMode(int file_flag
) {
37 if (file_flag
& (base::File::FLAG_OPEN
| base::File::FLAG_OPEN_TRUNCATED
))
40 if (file_flag
& base::File::FLAG_CREATE
)
44 (base::File::FLAG_OPEN_ALWAYS
| base::File::FLAG_CREATE_ALWAYS
));
45 return OPEN_OR_CREATE_FILE
;
48 // Runs |callback| with the File::Error converted from |error|.
49 void RunStatusCallbackByFileError(const StatusCallback
& callback
,
51 callback
.Run(FileErrorToBaseFileError(error
));
54 // Runs |callback| with arguments converted from |error| and |entry|.
55 void RunGetFileInfoCallback(const GetFileInfoCallback
& callback
,
57 scoped_ptr
<ResourceEntry
> entry
) {
58 if (error
!= FILE_ERROR_OK
) {
59 callback
.Run(FileErrorToBaseFileError(error
), base::File::Info());
64 base::File::Info file_info
;
65 ConvertResourceEntryToFileInfo(*entry
, &file_info
);
66 callback
.Run(base::File::FILE_OK
, file_info
);
69 // Runs |callback| with entries.
70 void RunReadDirectoryCallbackWithEntries(
71 const ReadDirectoryCallback
& callback
,
72 scoped_ptr
<ResourceEntryVector
> resource_entries
) {
73 DCHECK(resource_entries
);
75 std::vector
<storage::DirectoryEntry
> entries
;
76 // Convert drive files to File API's directory entry.
77 entries
.reserve(resource_entries
->size());
78 for (size_t i
= 0; i
< resource_entries
->size(); ++i
) {
79 const ResourceEntry
& resource_entry
= (*resource_entries
)[i
];
80 storage::DirectoryEntry entry
;
81 entry
.name
= resource_entry
.base_name();
83 const PlatformFileInfoProto
& file_info
= resource_entry
.file_info();
84 entry
.is_directory
= file_info
.is_directory();
85 entry
.size
= file_info
.size();
86 entry
.last_modified_time
=
87 base::Time::FromInternalValue(file_info
.last_modified());
88 entries
.push_back(entry
);
91 callback
.Run(base::File::FILE_OK
, entries
, true /*has_more*/);
94 // Runs |callback| with |error|.
95 void RunReadDirectoryCallbackOnCompletion(const ReadDirectoryCallback
& callback
,
97 callback
.Run(FileErrorToBaseFileError(error
),
98 std::vector
<storage::DirectoryEntry
>(),
102 // Runs |callback| with arguments based on |error|, |local_path| and |entry|.
103 void RunCreateSnapshotFileCallback(const CreateSnapshotFileCallback
& callback
,
105 const base::FilePath
& local_path
,
106 scoped_ptr
<ResourceEntry
> entry
) {
107 if (error
!= FILE_ERROR_OK
) {
108 callback
.Run(FileErrorToBaseFileError(error
),
111 storage::ScopedFile::ScopeOutPolicy());
117 // When reading file, last modified time specified in file info will be
118 // compared to the last modified time of the local version of the drive file.
119 // Since those two values don't generally match (last modification time on the
120 // drive server vs. last modification time of the local, downloaded file), so
121 // we have to opt out from this check. We do this by unsetting last_modified
122 // value in the file info passed to the CreateSnapshot caller.
123 base::File::Info file_info
;
124 ConvertResourceEntryToFileInfo(*entry
, &file_info
);
125 file_info
.last_modified
= base::Time();
127 // If the file is a hosted document, a temporary JSON file is created to
128 // represent the document. The JSON file is not cached and its lifetime
129 // is managed by ShareableFileReference.
130 storage::ScopedFile::ScopeOutPolicy scope_out_policy
=
131 entry
->file_specific_info().is_hosted_document()
132 ? storage::ScopedFile::DELETE_ON_SCOPE_OUT
133 : storage::ScopedFile::DONT_DELETE_ON_SCOPE_OUT
;
135 callback
.Run(base::File::FILE_OK
, file_info
, local_path
, scope_out_policy
);
138 // Runs |callback| with arguments converted from |error| and |local_path|.
139 void RunCreateWritableSnapshotFileCallback(
140 const CreateWritableSnapshotFileCallback
& callback
,
142 const base::FilePath
& local_path
,
143 const base::Closure
& close_callback
) {
144 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
145 callback
.Run(FileErrorToBaseFileError(error
), local_path
, close_callback
);
148 // Runs |callback| with |file|.
149 void RunOpenFileCallback(const OpenFileCallback
& callback
,
150 const base::Closure
& close_callback
,
152 callback
.Run(file
.Pass(), close_callback
);
155 base::File
OpenFile(const base::FilePath
& path
, int flags
) {
156 return base::File(path
, flags
);
159 // Part of OpenFile(). Called after FileSystem::OpenFile().
160 void OpenFileAfterFileSystemOpenFile(int file_flags
,
161 const OpenFileCallback
& callback
,
163 const base::FilePath
& local_path
,
164 const base::Closure
& close_callback
) {
165 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
167 if (error
!= FILE_ERROR_OK
) {
168 callback
.Run(base::File(FileErrorToBaseFileError(error
)), base::Closure());
172 // Here, the file should be at |local_path|, but there may be timing issue.
173 // Because the file is managed by Drive file system, so, in order to avoid
174 // unexpected file creation, CREATE, OPEN_ALWAYS and CREATE_ALWAYS are
175 // translated into OPEN or OPEN_TRUNCATED, here. Keep OPEN and OPEN_TRUNCATED
177 if (file_flags
& (base::File::FLAG_CREATE
|
178 base::File::FLAG_OPEN_ALWAYS
)) {
179 file_flags
&= ~(base::File::FLAG_CREATE
|
180 base::File::FLAG_OPEN_ALWAYS
);
181 file_flags
|= base::File::FLAG_OPEN
;
182 } else if (file_flags
& base::File::FLAG_CREATE_ALWAYS
) {
183 file_flags
&= ~base::File::FLAG_CREATE_ALWAYS
;
184 file_flags
|= base::File::FLAG_OPEN_TRUNCATED
;
187 // Cache file prepared for modification is available. Open it locally.
188 bool posted
= base::PostTaskAndReplyWithResult(
189 BrowserThread::GetBlockingPool(), FROM_HERE
,
190 base::Bind(&OpenFile
, local_path
, file_flags
),
191 base::Bind(&RunOpenFileCallback
, callback
, close_callback
));
197 FileSystemInterface
* GetFileSystemFromUrl(const storage::FileSystemURL
& url
) {
198 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
200 Profile
* profile
= util::ExtractProfileFromPath(url
.path());
201 return profile
? util::GetFileSystemByProfile(profile
) : NULL
;
204 void RunFileSystemCallback(
205 const FileSystemGetter
& file_system_getter
,
206 const base::Callback
<void(FileSystemInterface
*)>& callback
,
207 const base::Closure
& on_error_callback
) {
208 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
209 FileSystemInterface
* file_system
= file_system_getter
.Run();
212 if (!on_error_callback
.is_null())
213 on_error_callback
.Run();
217 callback
.Run(file_system
);
220 void GetFileInfo(const base::FilePath
& file_path
,
221 const GetFileInfoCallback
& callback
,
222 FileSystemInterface
* file_system
) {
223 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
224 file_system
->GetResourceEntry(
226 base::Bind(&RunGetFileInfoCallback
, callback
));
229 void Copy(const base::FilePath
& src_file_path
,
230 const base::FilePath
& dest_file_path
,
231 bool preserve_last_modified
,
232 const StatusCallback
& callback
,
233 FileSystemInterface
* file_system
) {
234 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
235 file_system
->Copy(src_file_path
, dest_file_path
, preserve_last_modified
,
236 base::Bind(&RunStatusCallbackByFileError
, callback
));
239 void Move(const base::FilePath
& src_file_path
,
240 const base::FilePath
& dest_file_path
,
241 const StatusCallback
& callback
,
242 FileSystemInterface
* file_system
) {
243 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
244 file_system
->Move(src_file_path
, dest_file_path
,
245 base::Bind(&RunStatusCallbackByFileError
, callback
));
248 void CopyInForeignFile(const base::FilePath
& src_foreign_file_path
,
249 const base::FilePath
& dest_file_path
,
250 const StatusCallback
& callback
,
251 FileSystemInterface
* file_system
) {
252 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
253 file_system
->TransferFileFromLocalToRemote(
254 src_foreign_file_path
, dest_file_path
,
255 base::Bind(&RunStatusCallbackByFileError
, callback
));
258 void ReadDirectory(const base::FilePath
& file_path
,
259 const ReadDirectoryCallback
& callback
,
260 FileSystemInterface
* file_system
) {
261 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
262 file_system
->ReadDirectory(
264 base::Bind(&RunReadDirectoryCallbackWithEntries
, callback
),
265 base::Bind(&RunReadDirectoryCallbackOnCompletion
, callback
));
268 void Remove(const base::FilePath
& file_path
,
270 const StatusCallback
& callback
,
271 FileSystemInterface
* file_system
) {
272 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
273 file_system
->Remove(file_path
, is_recursive
,
274 base::Bind(&RunStatusCallbackByFileError
, callback
));
277 void CreateDirectory(const base::FilePath
& file_path
,
280 const StatusCallback
& callback
,
281 FileSystemInterface
* file_system
) {
282 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
283 file_system
->CreateDirectory(
284 file_path
, is_exclusive
, is_recursive
,
285 base::Bind(&RunStatusCallbackByFileError
, callback
));
288 void CreateFile(const base::FilePath
& file_path
,
290 const StatusCallback
& callback
,
291 FileSystemInterface
* file_system
) {
292 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
293 file_system
->CreateFile(file_path
, is_exclusive
,
294 std::string(), // no mime type; guess from file_path
295 base::Bind(&RunStatusCallbackByFileError
, callback
));
298 void Truncate(const base::FilePath
& file_path
,
300 const StatusCallback
& callback
,
301 FileSystemInterface
* file_system
) {
302 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
303 file_system
->TruncateFile(
305 base::Bind(&RunStatusCallbackByFileError
, callback
));
308 void CreateSnapshotFile(const base::FilePath
& file_path
,
309 const CreateSnapshotFileCallback
& callback
,
310 FileSystemInterface
* file_system
) {
311 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
312 file_system
->GetFile(file_path
,
313 base::Bind(&RunCreateSnapshotFileCallback
, callback
));
316 void CreateWritableSnapshotFile(
317 const base::FilePath
& file_path
,
318 const CreateWritableSnapshotFileCallback
& callback
,
319 FileSystemInterface
* file_system
) {
320 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
321 file_system
->OpenFile(
324 std::string(), // no mime type; we never create a new file here.
325 base::Bind(&RunCreateWritableSnapshotFileCallback
, callback
));
328 void OpenFile(const base::FilePath
& file_path
,
330 const OpenFileCallback
& callback
,
331 FileSystemInterface
* file_system
) {
332 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
334 // Returns an error if any unsupported flag is found.
335 if (file_flags
& ~(base::File::FLAG_OPEN
|
336 base::File::FLAG_CREATE
|
337 base::File::FLAG_OPEN_ALWAYS
|
338 base::File::FLAG_CREATE_ALWAYS
|
339 base::File::FLAG_OPEN_TRUNCATED
|
340 base::File::FLAG_READ
|
341 base::File::FLAG_WRITE
|
342 base::File::FLAG_WRITE_ATTRIBUTES
|
343 base::File::FLAG_APPEND
)) {
344 base::ThreadTaskRunnerHandle::Get()->PostTask(
347 Passed(base::File(base::File::FILE_ERROR_FAILED
)),
352 file_system
->OpenFile(
353 file_path
, GetOpenMode(file_flags
),
354 std::string(), // no mime type; guess from file_path
355 base::Bind(&OpenFileAfterFileSystemOpenFile
, file_flags
, callback
));
358 void TouchFile(const base::FilePath
& file_path
,
359 const base::Time
& last_access_time
,
360 const base::Time
& last_modified_time
,
361 const StatusCallback
& callback
,
362 FileSystemInterface
* file_system
) {
363 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
364 file_system
->TouchFile(file_path
, last_access_time
, last_modified_time
,
365 base::Bind(&RunStatusCallbackByFileError
, callback
));
369 } // namespace fileapi_internal