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/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"
20 using content::BrowserThread
;
23 namespace fileapi_internal
{
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
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.
35 OpenMode
GetOpenMode(int file_flag
) {
36 if (file_flag
& (base::File::FLAG_OPEN
| base::File::FLAG_OPEN_TRUNCATED
))
39 if (file_flag
& base::File::FLAG_CREATE
)
43 (base::File::FLAG_OPEN_ALWAYS
| base::File::FLAG_CREATE_ALWAYS
));
44 return OPEN_OR_CREATE_FILE
;
47 // Runs |callback| with the File::Error converted from |error|.
48 void RunStatusCallbackByFileError(const StatusCallback
& callback
,
50 callback
.Run(FileErrorToBaseFileError(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(FileErrorToBaseFileError(error
), base::File::Info());
63 base::File::Info file_info
;
64 ConvertResourceEntryToFileInfo(*entry
, &file_info
);
65 callback
.Run(base::File::FILE_OK
, file_info
);
68 // Runs |callback| with entries.
69 void RunReadDirectoryCallbackWithEntries(
70 const ReadDirectoryCallback
& callback
,
71 scoped_ptr
<ResourceEntryVector
> resource_entries
) {
72 DCHECK(resource_entries
);
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();
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
);
90 callback
.Run(base::File::FILE_OK
, entries
, true /*has_more*/);
93 // Runs |callback| with |error|.
94 void RunReadDirectoryCallbackOnCompletion(const ReadDirectoryCallback
& callback
,
96 callback
.Run(FileErrorToBaseFileError(error
),
97 std::vector
<storage::DirectoryEntry
>(),
101 // Runs |callback| with arguments based on |error|, |local_path| and |entry|.
102 void RunCreateSnapshotFileCallback(const CreateSnapshotFileCallback
& callback
,
104 const base::FilePath
& local_path
,
105 scoped_ptr
<ResourceEntry
> entry
) {
106 if (error
!= FILE_ERROR_OK
) {
107 callback
.Run(FileErrorToBaseFileError(error
),
110 storage::ScopedFile::ScopeOutPolicy());
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();
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
;
134 callback
.Run(base::File::FILE_OK
, file_info
, local_path
, scope_out_policy
);
137 // Runs |callback| with arguments converted from |error| and |local_path|.
138 void RunCreateWritableSnapshotFileCallback(
139 const CreateWritableSnapshotFileCallback
& callback
,
141 const base::FilePath
& local_path
,
142 const base::Closure
& close_callback
) {
143 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
144 callback
.Run(FileErrorToBaseFileError(error
), local_path
, close_callback
);
147 // Runs |callback| with |file|.
148 void RunOpenFileCallback(const OpenFileCallback
& callback
,
149 const base::Closure
& close_callback
,
151 callback
.Run(file
.Pass(), close_callback
);
154 base::File
OpenFile(const base::FilePath
& path
, int flags
) {
155 return base::File(path
, flags
);
158 // Part of OpenFile(). Called after FileSystem::OpenFile().
159 void OpenFileAfterFileSystemOpenFile(int file_flags
,
160 const OpenFileCallback
& callback
,
162 const base::FilePath
& local_path
,
163 const base::Closure
& close_callback
) {
164 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
166 if (error
!= FILE_ERROR_OK
) {
167 callback
.Run(base::File(FileErrorToBaseFileError(error
)), base::Closure());
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
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
;
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
));
196 FileSystemInterface
* GetFileSystemFromUrl(const storage::FileSystemURL
& url
) {
197 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
199 Profile
* profile
= util::ExtractProfileFromPath(url
.path());
200 return profile
? util::GetFileSystemByProfile(profile
) : NULL
;
203 void RunFileSystemCallback(
204 const FileSystemGetter
& file_system_getter
,
205 const base::Callback
<void(FileSystemInterface
*)>& callback
,
206 const base::Closure
& on_error_callback
) {
207 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
208 FileSystemInterface
* file_system
= file_system_getter
.Run();
211 if (!on_error_callback
.is_null())
212 on_error_callback
.Run();
216 callback
.Run(file_system
);
219 void GetFileInfo(const base::FilePath
& file_path
,
220 const GetFileInfoCallback
& callback
,
221 FileSystemInterface
* file_system
) {
222 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
223 file_system
->GetResourceEntry(
225 base::Bind(&RunGetFileInfoCallback
, callback
));
228 void 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_CURRENTLY_ON(BrowserThread::UI
);
234 file_system
->Copy(src_file_path
, dest_file_path
, preserve_last_modified
,
235 base::Bind(&RunStatusCallbackByFileError
, callback
));
238 void Move(const base::FilePath
& src_file_path
,
239 const base::FilePath
& dest_file_path
,
240 const StatusCallback
& callback
,
241 FileSystemInterface
* file_system
) {
242 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
243 file_system
->Move(src_file_path
, dest_file_path
,
244 base::Bind(&RunStatusCallbackByFileError
, callback
));
247 void 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_CURRENTLY_ON(BrowserThread::UI
);
252 file_system
->TransferFileFromLocalToRemote(
253 src_foreign_file_path
, dest_file_path
,
254 base::Bind(&RunStatusCallbackByFileError
, callback
));
257 void ReadDirectory(const base::FilePath
& file_path
,
258 const ReadDirectoryCallback
& callback
,
259 FileSystemInterface
* file_system
) {
260 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
261 file_system
->ReadDirectory(
263 base::Bind(&RunReadDirectoryCallbackWithEntries
, callback
),
264 base::Bind(&RunReadDirectoryCallbackOnCompletion
, callback
));
267 void Remove(const base::FilePath
& file_path
,
269 const StatusCallback
& callback
,
270 FileSystemInterface
* file_system
) {
271 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
272 file_system
->Remove(file_path
, is_recursive
,
273 base::Bind(&RunStatusCallbackByFileError
, callback
));
276 void CreateDirectory(const base::FilePath
& file_path
,
279 const StatusCallback
& callback
,
280 FileSystemInterface
* file_system
) {
281 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
282 file_system
->CreateDirectory(
283 file_path
, is_exclusive
, is_recursive
,
284 base::Bind(&RunStatusCallbackByFileError
, callback
));
287 void CreateFile(const base::FilePath
& file_path
,
289 const StatusCallback
& callback
,
290 FileSystemInterface
* file_system
) {
291 DCHECK_CURRENTLY_ON(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
));
297 void Truncate(const base::FilePath
& file_path
,
299 const StatusCallback
& callback
,
300 FileSystemInterface
* file_system
) {
301 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
302 file_system
->TruncateFile(
304 base::Bind(&RunStatusCallbackByFileError
, callback
));
307 void CreateSnapshotFile(const base::FilePath
& file_path
,
308 const CreateSnapshotFileCallback
& callback
,
309 FileSystemInterface
* file_system
) {
310 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
311 file_system
->GetFile(file_path
,
312 base::Bind(&RunCreateSnapshotFileCallback
, callback
));
315 void CreateWritableSnapshotFile(
316 const base::FilePath
& file_path
,
317 const CreateWritableSnapshotFileCallback
& callback
,
318 FileSystemInterface
* file_system
) {
319 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
320 file_system
->OpenFile(
323 std::string(), // no mime type; we never create a new file here.
324 base::Bind(&RunCreateWritableSnapshotFileCallback
, callback
));
327 void OpenFile(const base::FilePath
& file_path
,
329 const OpenFileCallback
& callback
,
330 FileSystemInterface
* file_system
) {
331 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
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(
346 Passed(base::File(base::File::FILE_ERROR_FAILED
)),
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
));
357 void 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_CURRENTLY_ON(BrowserThread::UI
);
363 file_system
->TouchFile(file_path
, last_access_time
, last_modified_time
,
364 base::Bind(&RunStatusCallbackByFileError
, callback
));
368 } // namespace fileapi_internal