OLD | NEW |
| (Empty) |
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 | |
19 using content::BrowserThread; | |
20 | |
21 namespace drive { | |
22 namespace fileapi_internal { | |
23 namespace { | |
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. | |
34 OpenMode 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 File::Error converted from |error|. | |
48 void RunStatusCallbackByFileError(const StatusCallback& callback, | |
49 FileError error) { | |
50 callback.Run(FileErrorToBaseFileError(error)); | |
51 } | |
52 | |
53 // Runs |callback| with arguments converted from |error| and |entry|. | |
54 void 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 arguments converted from |error| and |resource_entries|. | |
69 void RunReadDirectoryCallback( | |
70 const ReadDirectoryCallback& callback, | |
71 FileError error, | |
72 scoped_ptr<ResourceEntryVector> resource_entries) { | |
73 if (error != FILE_ERROR_OK) { | |
74 callback.Run(FileErrorToBaseFileError(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::File::FILE_OK, entries, false); | |
98 } | |
99 | |
100 // Runs |callback| with arguments based on |error|, |local_path| and |entry|. | |
101 void 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 FileErrorToBaseFileError(error), | |
108 base::File::Info(), 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::File::Info file_info; | |
122 ConvertResourceEntryToFileInfo(*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::File::FILE_OK, file_info, local_path, scope_out_policy); | |
134 } | |
135 | |
136 // Runs |callback| with arguments converted from |error| and |local_path|. | |
137 void 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(FileErrorToBaseFileError(error), local_path, close_callback); | |
144 } | |
145 | |
146 // Runs |callback| with |error| and |platform_file|. | |
147 void RunOpenFileCallback(const OpenFileCallback& callback, | |
148 const base::Closure& close_callback, | |
149 base::File::Error* error, | |
150 base::PlatformFile platform_file) { | |
151 callback.Run(*error, platform_file, close_callback); | |
152 } | |
153 | |
154 // Part of OpenFile(). Called after FileSystem::OpenFile(). | |
155 void 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(FileErrorToBaseFileError(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 // TODO(rvargas): Convert this to base::File. | |
186 base::File::Error* result = | |
187 new base::File::Error(base::File::FILE_ERROR_FAILED); | |
188 bool posted = base::PostTaskAndReplyWithResult( | |
189 BrowserThread::GetBlockingPool(), FROM_HERE, | |
190 base::Bind(&base::CreatePlatformFile, | |
191 local_path, file_flags, static_cast<bool*>(NULL), | |
192 reinterpret_cast<base::PlatformFileError*>(result)), | |
193 base::Bind(&RunOpenFileCallback, | |
194 callback, close_callback, base::Owned(result))); | |
195 DCHECK(posted); | |
196 } | |
197 | |
198 } // namespace | |
199 | |
200 void RunFileSystemCallback( | |
201 const FileSystemGetter& file_system_getter, | |
202 const base::Callback<void(FileSystemInterface*)>& callback, | |
203 const base::Closure& on_error_callback) { | |
204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
205 FileSystemInterface* file_system = file_system_getter.Run(); | |
206 | |
207 if (!file_system) { | |
208 if (!on_error_callback.is_null()) | |
209 on_error_callback.Run(); | |
210 return; | |
211 } | |
212 | |
213 callback.Run(file_system); | |
214 } | |
215 | |
216 void GetFileInfo(const base::FilePath& file_path, | |
217 const GetFileInfoCallback& callback, | |
218 FileSystemInterface* file_system) { | |
219 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
220 file_system->GetResourceEntry( | |
221 file_path, | |
222 base::Bind(&RunGetFileInfoCallback, callback)); | |
223 } | |
224 | |
225 void Copy(const base::FilePath& src_file_path, | |
226 const base::FilePath& dest_file_path, | |
227 bool preserve_last_modified, | |
228 const StatusCallback& callback, | |
229 FileSystemInterface* file_system) { | |
230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
231 file_system->Copy(src_file_path, dest_file_path, preserve_last_modified, | |
232 base::Bind(&RunStatusCallbackByFileError, callback)); | |
233 } | |
234 | |
235 void Move(const base::FilePath& src_file_path, | |
236 const base::FilePath& dest_file_path, | |
237 bool preserve_last_modified, | |
238 const StatusCallback& callback, | |
239 FileSystemInterface* file_system) { | |
240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
241 file_system->Move(src_file_path, dest_file_path, preserve_last_modified, | |
242 base::Bind(&RunStatusCallbackByFileError, callback)); | |
243 } | |
244 | |
245 void CopyInForeignFile(const base::FilePath& src_foreign_file_path, | |
246 const base::FilePath& dest_file_path, | |
247 const StatusCallback& callback, | |
248 FileSystemInterface* file_system) { | |
249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
250 file_system->TransferFileFromLocalToRemote( | |
251 src_foreign_file_path, dest_file_path, | |
252 base::Bind(&RunStatusCallbackByFileError, callback)); | |
253 } | |
254 | |
255 void ReadDirectory(const base::FilePath& file_path, | |
256 const ReadDirectoryCallback& callback, | |
257 FileSystemInterface* file_system) { | |
258 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
259 file_system->ReadDirectory(file_path, | |
260 base::Bind(&RunReadDirectoryCallback, callback)); | |
261 } | |
262 | |
263 void Remove(const base::FilePath& file_path, | |
264 bool is_recursive, | |
265 const StatusCallback& callback, | |
266 FileSystemInterface* file_system) { | |
267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
268 file_system->Remove(file_path, is_recursive, | |
269 base::Bind(&RunStatusCallbackByFileError, callback)); | |
270 } | |
271 | |
272 void CreateDirectory(const base::FilePath& file_path, | |
273 bool is_exclusive, | |
274 bool is_recursive, | |
275 const StatusCallback& callback, | |
276 FileSystemInterface* file_system) { | |
277 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
278 file_system->CreateDirectory( | |
279 file_path, is_exclusive, is_recursive, | |
280 base::Bind(&RunStatusCallbackByFileError, callback)); | |
281 } | |
282 | |
283 void CreateFile(const base::FilePath& file_path, | |
284 bool is_exclusive, | |
285 const StatusCallback& callback, | |
286 FileSystemInterface* file_system) { | |
287 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
288 file_system->CreateFile(file_path, is_exclusive, | |
289 std::string(), // no mime type; guess from file_path | |
290 base::Bind(&RunStatusCallbackByFileError, callback)); | |
291 } | |
292 | |
293 void Truncate(const base::FilePath& file_path, | |
294 int64 length, | |
295 const StatusCallback& callback, | |
296 FileSystemInterface* file_system) { | |
297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
298 file_system->TruncateFile( | |
299 file_path, length, | |
300 base::Bind(&RunStatusCallbackByFileError, callback)); | |
301 } | |
302 | |
303 void CreateSnapshotFile(const base::FilePath& file_path, | |
304 const CreateSnapshotFileCallback& callback, | |
305 FileSystemInterface* file_system) { | |
306 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
307 file_system->GetFile(file_path, | |
308 base::Bind(&RunCreateSnapshotFileCallback, callback)); | |
309 } | |
310 | |
311 void CreateWritableSnapshotFile( | |
312 const base::FilePath& file_path, | |
313 const CreateWritableSnapshotFileCallback& callback, | |
314 FileSystemInterface* file_system) { | |
315 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
316 file_system->OpenFile( | |
317 file_path, | |
318 OPEN_FILE, | |
319 std::string(), // no mime type; we never create a new file here. | |
320 base::Bind(&RunCreateWritableSnapshotFileCallback, callback)); | |
321 } | |
322 | |
323 void OpenFile(const base::FilePath& file_path, | |
324 int file_flags, | |
325 const OpenFileCallback& callback, | |
326 FileSystemInterface* file_system) { | |
327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
328 | |
329 // Returns an error if any unsupported flag is found. | |
330 if (file_flags & ~(base::PLATFORM_FILE_OPEN | | |
331 base::PLATFORM_FILE_CREATE | | |
332 base::PLATFORM_FILE_OPEN_ALWAYS | | |
333 base::PLATFORM_FILE_CREATE_ALWAYS | | |
334 base::PLATFORM_FILE_OPEN_TRUNCATED | | |
335 base::PLATFORM_FILE_READ | | |
336 base::PLATFORM_FILE_WRITE | | |
337 base::PLATFORM_FILE_WRITE_ATTRIBUTES | | |
338 base::PLATFORM_FILE_APPEND)) { | |
339 base::MessageLoopProxy::current()->PostTask( | |
340 FROM_HERE, | |
341 base::Bind(callback, | |
342 base::File::FILE_ERROR_FAILED, | |
343 base::kInvalidPlatformFileValue, | |
344 base::Closure())); | |
345 return; | |
346 } | |
347 | |
348 file_system->OpenFile( | |
349 file_path, GetOpenMode(file_flags), | |
350 std::string(), // no mime type; guess from file_path | |
351 base::Bind(&OpenFileAfterFileSystemOpenFile, file_flags, callback)); | |
352 } | |
353 | |
354 void TouchFile(const base::FilePath& file_path, | |
355 const base::Time& last_access_time, | |
356 const base::Time& last_modified_time, | |
357 const StatusCallback& callback, | |
358 FileSystemInterface* file_system) { | |
359 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
360 file_system->TouchFile(file_path, last_access_time, last_modified_time, | |
361 base::Bind(&RunStatusCallbackByFileError, callback)); | |
362 | |
363 } | |
364 | |
365 } // namespace fileapi_internal | |
366 } // namespace drive | |
OLD | NEW |