Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / chromeos / drive / file_system / copy_operation.cc
blob82e9885895fd089189d07222b59508dd21c649b0
1 // Copyright (c) 2012 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/file_system/copy_operation.h"
7 #include <string>
9 #include "base/callback_helpers.h"
10 #include "base/file_util.h"
11 #include "base/task_runner_util.h"
12 #include "chrome/browser/chromeos/drive/drive.pb.h"
13 #include "chrome/browser/chromeos/drive/file_cache.h"
14 #include "chrome/browser/chromeos/drive/file_system/create_file_operation.h"
15 #include "chrome/browser/chromeos/drive/file_system/operation_observer.h"
16 #include "chrome/browser/chromeos/drive/file_system_util.h"
17 #include "chrome/browser/chromeos/drive/job_scheduler.h"
18 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
19 #include "chrome/browser/chromeos/drive/resource_metadata.h"
20 #include "chrome/browser/drive/drive_api_util.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "google_apis/drive/drive_api_parser.h"
24 using content::BrowserThread;
26 namespace drive {
27 namespace file_system {
29 namespace {
31 FileError PrepareCopy(internal::ResourceMetadata* metadata,
32 const base::FilePath& src_path,
33 const base::FilePath& dest_path,
34 ResourceEntry* src_entry,
35 std::string* parent_resource_id) {
36 FileError error = metadata->GetResourceEntryByPath(src_path, src_entry);
37 if (error != FILE_ERROR_OK)
38 return error;
40 ResourceEntry parent_entry;
41 error = metadata->GetResourceEntryByPath(dest_path.DirName(), &parent_entry);
42 if (error != FILE_ERROR_OK)
43 return error;
45 if (!parent_entry.file_info().is_directory())
46 return FILE_ERROR_NOT_A_DIRECTORY;
48 // Drive File System doesn't support recursive copy.
49 if (src_entry->file_info().is_directory())
50 return FILE_ERROR_NOT_A_FILE;
52 *parent_resource_id = parent_entry.resource_id();
53 return FILE_ERROR_OK;
56 int64 GetFileSize(const base::FilePath& file_path) {
57 int64 file_size;
58 if (!base::GetFileSize(file_path, &file_size))
59 return -1;
60 return file_size;
63 // Stores the copied entry and returns its path.
64 FileError UpdateLocalStateForServerSideCopy(
65 internal::ResourceMetadata* metadata,
66 scoped_ptr<google_apis::ResourceEntry> resource_entry,
67 base::FilePath* file_path) {
68 DCHECK(resource_entry);
70 ResourceEntry entry;
71 std::string parent_resource_id;
72 if (!ConvertToResourceEntry(*resource_entry, &entry, &parent_resource_id) ||
73 parent_resource_id.empty())
74 return FILE_ERROR_NOT_A_FILE;
76 std::string parent_local_id;
77 FileError error = metadata->GetIdByResourceId(parent_resource_id,
78 &parent_local_id);
79 if (error != FILE_ERROR_OK)
80 return error;
81 entry.set_parent_local_id(parent_local_id);
83 std::string local_id;
84 error = metadata->AddEntry(entry, &local_id);
85 // Depending on timing, the metadata may have inserted via change list
86 // already. So, FILE_ERROR_EXISTS is not an error.
87 if (error == FILE_ERROR_EXISTS)
88 error = metadata->GetIdByResourceId(entry.resource_id(), &local_id);
90 if (error == FILE_ERROR_OK)
91 *file_path = metadata->GetFilePath(local_id);
93 return error;
96 // Stores the file at |local_file_path| to the cache as a content of entry at
97 // |remote_dest_path|, and marks it dirty.
98 FileError UpdateLocalStateForScheduleTransfer(
99 internal::ResourceMetadata* metadata,
100 internal::FileCache* cache,
101 const base::FilePath& local_src_path,
102 const base::FilePath& remote_dest_path,
103 std::string* local_id) {
104 FileError error = metadata->GetIdByPath(remote_dest_path, local_id);
105 if (error != FILE_ERROR_OK)
106 return error;
108 ResourceEntry entry;
109 error = metadata->GetResourceEntryById(*local_id, &entry);
110 if (error != FILE_ERROR_OK)
111 return error;
113 error = cache->Store(
114 *local_id, entry.file_specific_info().md5(), local_src_path,
115 internal::FileCache::FILE_OPERATION_COPY);
116 if (error != FILE_ERROR_OK)
117 return error;
119 scoped_ptr<base::ScopedClosureRunner> file_closer;
120 error = cache->OpenForWrite(*local_id, &file_closer);
121 if (error != FILE_ERROR_OK)
122 return error;
124 return FILE_ERROR_OK;
127 // Gets the file size of the |local_path|, and the ResourceEntry for the parent
128 // of |remote_path| to prepare the necessary information for transfer.
129 FileError PrepareTransferFileFromLocalToRemote(
130 internal::ResourceMetadata* metadata,
131 const base::FilePath& local_src_path,
132 const base::FilePath& remote_dest_path,
133 std::string* gdoc_resource_id,
134 std::string* parent_resource_id) {
135 ResourceEntry parent_entry;
136 FileError error = metadata->GetResourceEntryByPath(
137 remote_dest_path.DirName(), &parent_entry);
138 if (error != FILE_ERROR_OK)
139 return error;
141 // The destination's parent must be a directory.
142 if (!parent_entry.file_info().is_directory())
143 return FILE_ERROR_NOT_A_DIRECTORY;
145 // Try to parse GDoc File and extract the resource id, if necessary.
146 // Failing isn't problem. It'd be handled as a regular file, then.
147 if (util::HasGDocFileExtension(local_src_path)) {
148 *gdoc_resource_id = util::ReadResourceIdFromGDocFile(local_src_path);
149 *parent_resource_id = parent_entry.resource_id();
152 return FILE_ERROR_OK;
155 } // namespace
157 struct CopyOperation::CopyParams {
158 base::FilePath dest_file_path;
159 bool preserve_last_modified;
160 FileOperationCallback callback;
163 CopyOperation::CopyOperation(base::SequencedTaskRunner* blocking_task_runner,
164 OperationObserver* observer,
165 JobScheduler* scheduler,
166 internal::ResourceMetadata* metadata,
167 internal::FileCache* cache,
168 const ResourceIdCanonicalizer& id_canonicalizer)
169 : blocking_task_runner_(blocking_task_runner),
170 observer_(observer),
171 scheduler_(scheduler),
172 metadata_(metadata),
173 cache_(cache),
174 id_canonicalizer_(id_canonicalizer),
175 create_file_operation_(new CreateFileOperation(blocking_task_runner,
176 observer,
177 scheduler,
178 metadata,
179 cache)),
180 weak_ptr_factory_(this) {
181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
184 CopyOperation::~CopyOperation() {
185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
188 void CopyOperation::Copy(const base::FilePath& src_file_path,
189 const base::FilePath& dest_file_path,
190 bool preserve_last_modified,
191 const FileOperationCallback& callback) {
192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
193 DCHECK(!callback.is_null());
195 CopyParams params;
196 params.dest_file_path = dest_file_path;
197 params.preserve_last_modified = preserve_last_modified;
198 params.callback = callback;
200 ResourceEntry* src_entry = new ResourceEntry;
201 std::string* parent_resource_id = new std::string;
202 base::PostTaskAndReplyWithResult(
203 blocking_task_runner_.get(),
204 FROM_HERE,
205 base::Bind(&PrepareCopy,
206 metadata_, src_file_path, dest_file_path,
207 src_entry, parent_resource_id),
208 base::Bind(&CopyOperation::CopyAfterPrepare,
209 weak_ptr_factory_.GetWeakPtr(), params,
210 base::Owned(src_entry), base::Owned(parent_resource_id)));
213 void CopyOperation::CopyAfterPrepare(const CopyParams& params,
214 ResourceEntry* src_entry,
215 std::string* parent_resource_id,
216 FileError error) {
217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
218 DCHECK(!params.callback.is_null());
220 if (error != FILE_ERROR_OK) {
221 params.callback.Run(error);
222 return;
225 base::FilePath new_title = params.dest_file_path.BaseName();
226 if (src_entry->file_specific_info().is_hosted_document()) {
227 // Drop the document extension, which should not be in the title.
228 // TODO(yoshiki): Remove this code with crbug.com/223304.
229 new_title = new_title.RemoveExtension();
232 base::Time last_modified =
233 params.preserve_last_modified ?
234 base::Time::FromInternalValue(src_entry->file_info().last_modified()) :
235 base::Time();
237 CopyResourceOnServer(
238 src_entry->resource_id(), *parent_resource_id,
239 new_title.AsUTF8Unsafe(), last_modified, params.callback);
242 void CopyOperation::TransferFileFromLocalToRemote(
243 const base::FilePath& local_src_path,
244 const base::FilePath& remote_dest_path,
245 const FileOperationCallback& callback) {
246 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
247 DCHECK(!callback.is_null());
249 std::string* gdoc_resource_id = new std::string;
250 std::string* parent_resource_id = new std::string;
251 base::PostTaskAndReplyWithResult(
252 blocking_task_runner_.get(),
253 FROM_HERE,
254 base::Bind(
255 &PrepareTransferFileFromLocalToRemote,
256 metadata_, local_src_path, remote_dest_path,
257 gdoc_resource_id, parent_resource_id),
258 base::Bind(
259 &CopyOperation::TransferFileFromLocalToRemoteAfterPrepare,
260 weak_ptr_factory_.GetWeakPtr(),
261 local_src_path, remote_dest_path, callback,
262 base::Owned(gdoc_resource_id), base::Owned(parent_resource_id)));
265 void CopyOperation::TransferFileFromLocalToRemoteAfterPrepare(
266 const base::FilePath& local_src_path,
267 const base::FilePath& remote_dest_path,
268 const FileOperationCallback& callback,
269 std::string* gdoc_resource_id,
270 std::string* parent_resource_id,
271 FileError error) {
272 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
273 DCHECK(!callback.is_null());
275 if (error != FILE_ERROR_OK) {
276 callback.Run(error);
277 return;
280 // For regular files, schedule the transfer.
281 if (gdoc_resource_id->empty()) {
282 ScheduleTransferRegularFile(local_src_path, remote_dest_path, callback);
283 return;
286 // This is uploading a JSON file representing a hosted document.
287 // Copy the document on the Drive server.
289 // GDoc file may contain a resource ID in the old format.
290 const std::string canonicalized_resource_id =
291 id_canonicalizer_.Run(*gdoc_resource_id);
293 CopyResourceOnServer(
294 canonicalized_resource_id, *parent_resource_id,
295 // Drop the document extension, which should not be in the title.
296 // TODO(yoshiki): Remove this code with crbug.com/223304.
297 remote_dest_path.BaseName().RemoveExtension().AsUTF8Unsafe(),
298 base::Time(),
299 callback);
302 void CopyOperation::CopyResourceOnServer(
303 const std::string& resource_id,
304 const std::string& parent_resource_id,
305 const std::string& new_title,
306 const base::Time& last_modified,
307 const FileOperationCallback& callback) {
308 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
309 DCHECK(!callback.is_null());
311 scheduler_->CopyResource(
312 resource_id, parent_resource_id, new_title, last_modified,
313 base::Bind(&CopyOperation::CopyResourceOnServerAfterServerSideCopy,
314 weak_ptr_factory_.GetWeakPtr(),
315 callback));
318 void CopyOperation::CopyResourceOnServerAfterServerSideCopy(
319 const FileOperationCallback& callback,
320 google_apis::GDataErrorCode status,
321 scoped_ptr<google_apis::ResourceEntry> resource_entry) {
322 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
323 DCHECK(!callback.is_null());
325 FileError error = GDataToFileError(status);
326 if (error != FILE_ERROR_OK) {
327 callback.Run(error);
328 return;
331 // The copy on the server side is completed successfully. Update the local
332 // metadata.
333 base::FilePath* file_path = new base::FilePath;
334 base::PostTaskAndReplyWithResult(
335 blocking_task_runner_.get(),
336 FROM_HERE,
337 base::Bind(&UpdateLocalStateForServerSideCopy,
338 metadata_, base::Passed(&resource_entry), file_path),
339 base::Bind(&CopyOperation::CopyResourceOnServerAfterUpdateLocalState,
340 weak_ptr_factory_.GetWeakPtr(),
341 callback, base::Owned(file_path)));
344 void CopyOperation::CopyResourceOnServerAfterUpdateLocalState(
345 const FileOperationCallback& callback,
346 base::FilePath* file_path,
347 FileError error) {
348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
349 DCHECK(!callback.is_null());
351 if (error == FILE_ERROR_OK)
352 observer_->OnDirectoryChangedByOperation(file_path->DirName());
353 callback.Run(error);
356 void CopyOperation::ScheduleTransferRegularFile(
357 const base::FilePath& local_src_path,
358 const base::FilePath& remote_dest_path,
359 const FileOperationCallback& callback) {
360 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
361 DCHECK(!callback.is_null());
363 base::PostTaskAndReplyWithResult(
364 blocking_task_runner_.get(),
365 FROM_HERE,
366 base::Bind(&GetFileSize, local_src_path),
367 base::Bind(&CopyOperation::ScheduleTransferRegularFileAfterGetFileSize,
368 weak_ptr_factory_.GetWeakPtr(),
369 local_src_path, remote_dest_path, callback));
372 void CopyOperation::ScheduleTransferRegularFileAfterGetFileSize(
373 const base::FilePath& local_src_path,
374 const base::FilePath& remote_dest_path,
375 const FileOperationCallback& callback,
376 int64 local_file_size) {
377 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
378 DCHECK(!callback.is_null());
380 if (local_file_size < 0) {
381 callback.Run(FILE_ERROR_NOT_FOUND);
382 return;
385 // For regular files, check the server-side quota whether sufficient space
386 // is available for the file to be uploaded.
387 scheduler_->GetAboutResource(
388 base::Bind(
389 &CopyOperation::ScheduleTransferRegularFileAfterGetAboutResource,
390 weak_ptr_factory_.GetWeakPtr(),
391 local_src_path, remote_dest_path, callback, local_file_size));
394 void CopyOperation::ScheduleTransferRegularFileAfterGetAboutResource(
395 const base::FilePath& local_src_path,
396 const base::FilePath& remote_dest_path,
397 const FileOperationCallback& callback,
398 int64 local_file_size,
399 google_apis::GDataErrorCode status,
400 scoped_ptr<google_apis::AboutResource> about_resource) {
401 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
402 DCHECK(!callback.is_null());
404 FileError error = GDataToFileError(status);
405 if (error != FILE_ERROR_OK) {
406 callback.Run(error);
407 return;
410 DCHECK(about_resource);
411 const int64 space =
412 about_resource->quota_bytes_total() - about_resource->quota_bytes_used();
413 if (space < local_file_size) {
414 callback.Run(FILE_ERROR_NO_SERVER_SPACE);
415 return;
418 create_file_operation_->CreateFile(
419 remote_dest_path,
420 true, // Exclusive (i.e. fail if a file already exists).
421 std::string(), // no specific mime type; CreateFile should guess it.
422 base::Bind(&CopyOperation::ScheduleTransferRegularFileAfterCreate,
423 weak_ptr_factory_.GetWeakPtr(),
424 local_src_path, remote_dest_path, callback));
427 void CopyOperation::ScheduleTransferRegularFileAfterCreate(
428 const base::FilePath& local_src_path,
429 const base::FilePath& remote_dest_path,
430 const FileOperationCallback& callback,
431 FileError error) {
432 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
433 DCHECK(!callback.is_null());
435 if (error != FILE_ERROR_OK) {
436 callback.Run(error);
437 return;
440 std::string* local_id = new std::string;
441 base::PostTaskAndReplyWithResult(
442 blocking_task_runner_.get(),
443 FROM_HERE,
444 base::Bind(
445 &UpdateLocalStateForScheduleTransfer,
446 metadata_, cache_, local_src_path, remote_dest_path, local_id),
447 base::Bind(
448 &CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState,
449 weak_ptr_factory_.GetWeakPtr(), callback, base::Owned(local_id)));
452 void CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState(
453 const FileOperationCallback& callback,
454 std::string* local_id,
455 FileError error) {
456 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
457 DCHECK(!callback.is_null());
459 if (error == FILE_ERROR_OK)
460 observer_->OnCacheFileUploadNeededByOperation(*local_id);
461 callback.Run(error);
464 } // namespace file_system
465 } // namespace drive