Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / drive / file_system / copy_operation.cc
index d438095..811e224 100644 (file)
@@ -6,7 +6,6 @@
 
 #include <string>
 
-#include "base/callback_helpers.h"
 #include "base/file_util.h"
 #include "base/task_runner_util.h"
 #include "chrome/browser/chromeos/drive/drive.pb.h"
@@ -26,38 +25,106 @@ using content::BrowserThread;
 namespace drive {
 namespace file_system {
 
+struct CopyOperation::CopyParams {
+  base::FilePath src_file_path;
+  base::FilePath dest_file_path;
+  bool preserve_last_modified;
+  FileOperationCallback callback;
+  ResourceEntry src_entry;
+  ResourceEntry parent_entry;
+};
+
 namespace {
 
-FileError PrepareCopy(internal::ResourceMetadata* metadata,
-                      const base::FilePath& src_path,
-                      const base::FilePath& dest_path,
-                      ResourceEntry* src_entry,
-                      std::string* parent_resource_id) {
-  FileError error = metadata->GetResourceEntryByPath(src_path, src_entry);
+FileError TryToCopyLocally(internal::ResourceMetadata* metadata,
+                           internal::FileCache* cache,
+                           CopyOperation::CopyParams* params,
+                           std::vector<std::string>* updated_local_ids,
+                           bool* directory_changed,
+                           bool* should_copy_on_server) {
+  FileError error = metadata->GetResourceEntryByPath(params->src_file_path,
+                                                     &params->src_entry);
   if (error != FILE_ERROR_OK)
     return error;
 
-  ResourceEntry parent_entry;
-  error = metadata->GetResourceEntryByPath(dest_path.DirName(), &parent_entry);
+  error = metadata->GetResourceEntryByPath(params->dest_file_path.DirName(),
+                                           &params->parent_entry);
   if (error != FILE_ERROR_OK)
     return error;
 
-  if (!parent_entry.file_info().is_directory())
+  if (!params->parent_entry.file_info().is_directory())
     return FILE_ERROR_NOT_A_DIRECTORY;
 
   // Drive File System doesn't support recursive copy.
-  if (src_entry->file_info().is_directory())
+  if (params->src_entry.file_info().is_directory())
     return FILE_ERROR_NOT_A_FILE;
 
-  *parent_resource_id = parent_entry.resource_id();
-  return FILE_ERROR_OK;
-}
+  // Check destination.
+  ResourceEntry dest_entry;
+  error = metadata->GetResourceEntryByPath(params->dest_file_path, &dest_entry);
+  switch (error) {
+    case FILE_ERROR_OK:
+      // File API spec says it is an error to try to "copy a file to a path
+      // occupied by a directory".
+      if (dest_entry.file_info().is_directory())
+        return FILE_ERROR_INVALID_OPERATION;
+
+      // Move the existing entry to the trash.
+      dest_entry.set_parent_local_id(util::kDriveTrashDirLocalId);
+      error = metadata->RefreshEntry(dest_entry);
+      if (error != FILE_ERROR_OK)
+        return error;
+      updated_local_ids->push_back(dest_entry.local_id());
+      *directory_changed = true;
+      break;
+    case FILE_ERROR_NOT_FOUND:
+      break;
+    default:
+      return error;
+  }
+
+  // If the cache file is not present and the entry exists on the server,
+  // server side copy should be used.
+  FileCacheEntry cache_entry;
+  cache->GetCacheEntry(params->src_entry.local_id(), &cache_entry);
+  if (!cache_entry.is_present() && !params->src_entry.resource_id().empty()) {
+    *should_copy_on_server = true;
+    return FILE_ERROR_OK;
+  }
+
+  // Copy locally.
+  ResourceEntry entry;
+  const int64 now = base::Time::Now().ToInternalValue();
+  entry.set_title(params->dest_file_path.BaseName().AsUTF8Unsafe());
+  entry.set_parent_local_id(params->parent_entry.local_id());
+  entry.mutable_file_specific_info()->set_content_mime_type(
+      params->src_entry.file_specific_info().content_mime_type());
+  entry.set_metadata_edit_state(ResourceEntry::DIRTY);
+  entry.mutable_file_info()->set_last_modified(
+      params->preserve_last_modified ?
+      params->src_entry.file_info().last_modified() : now);
+  entry.mutable_file_info()->set_last_accessed(now);
+
+  std::string local_id;
+  error = metadata->AddEntry(entry, &local_id);
+  if (error != FILE_ERROR_OK)
+    return error;
+  updated_local_ids->push_back(local_id);
+  *directory_changed = true;
 
-int64 GetFileSize(const base::FilePath& file_path) {
-  int64 file_size;
-  if (!base::GetFileSize(file_path, &file_size))
-    return -1;
-  return file_size;
+  if (!cache_entry.is_present()) {
+    DCHECK(params->src_entry.resource_id().empty());
+    // Locally created empty file may have no cache file.
+    return FILE_ERROR_OK;
+  }
+
+  base::FilePath cache_file_path;
+  error = cache->GetFile(params->src_entry.local_id(), &cache_file_path);
+  if (error != FILE_ERROR_OK)
+    return error;
+
+  return cache->Store(local_id, std::string(), cache_file_path,
+                      internal::FileCache::FILE_OPERATION_COPY);
 }
 
 // Stores the copied entry and returns its path.
@@ -110,18 +177,8 @@ FileError UpdateLocalStateForScheduleTransfer(
   if (error != FILE_ERROR_OK)
     return error;
 
-  error = cache->Store(
-      *local_id, entry.file_specific_info().md5(), local_src_path,
-      internal::FileCache::FILE_OPERATION_COPY);
-  if (error != FILE_ERROR_OK)
-    return error;
-
-  scoped_ptr<base::ScopedClosureRunner> file_closer;
-  error = cache->OpenForWrite(*local_id, &file_closer);
-  if (error != FILE_ERROR_OK)
-    return error;
-
-  return FILE_ERROR_OK;
+  return cache->Store(*local_id, std::string(), local_src_path,
+                      internal::FileCache::FILE_OPERATION_COPY);
 }
 
 // Gets the file size of the |local_path|, and the ResourceEntry for the parent
@@ -154,12 +211,6 @@ FileError PrepareTransferFileFromLocalToRemote(
 
 }  // namespace
 
-struct CopyOperation::CopyParams {
-  base::FilePath dest_file_path;
-  bool preserve_last_modified;
-  FileOperationCallback callback;
-};
-
 CopyOperation::CopyOperation(base::SequencedTaskRunner* blocking_task_runner,
                              OperationObserver* observer,
                              JobScheduler* scheduler,
@@ -174,9 +225,7 @@ CopyOperation::CopyOperation(base::SequencedTaskRunner* blocking_task_runner,
     id_canonicalizer_(id_canonicalizer),
     create_file_operation_(new CreateFileOperation(blocking_task_runner,
                                                    observer,
-                                                   scheduler,
-                                                   metadata,
-                                                   cache)),
+                                                   metadata)),
     weak_ptr_factory_(this) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 }
@@ -192,51 +241,61 @@ void CopyOperation::Copy(const base::FilePath& src_file_path,
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  CopyParams params;
-  params.dest_file_path = dest_file_path;
-  params.preserve_last_modified = preserve_last_modified;
-  params.callback = callback;
+  CopyParams* params = new CopyParams;
+  params->src_file_path = src_file_path;
+  params->dest_file_path = dest_file_path;
+  params->preserve_last_modified = preserve_last_modified;
+  params->callback = callback;
 
-  ResourceEntry* src_entry = new ResourceEntry;
-  std::string* parent_resource_id = new std::string;
+  std::vector<std::string>* updated_local_ids = new std::vector<std::string>;
+  bool* directory_changed = new bool(false);
+  bool* should_copy_on_server = new bool(false);
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(),
       FROM_HERE,
-      base::Bind(&PrepareCopy,
-                 metadata_, src_file_path, dest_file_path,
-                 src_entry, parent_resource_id),
-      base::Bind(&CopyOperation::CopyAfterPrepare,
-                 weak_ptr_factory_.GetWeakPtr(), params,
-                 base::Owned(src_entry), base::Owned(parent_resource_id)));
+      base::Bind(&TryToCopyLocally, metadata_, cache_, params,
+                 updated_local_ids, directory_changed, should_copy_on_server),
+      base::Bind(&CopyOperation::CopyAfterTryToCopyLocally,
+                 weak_ptr_factory_.GetWeakPtr(), base::Owned(params),
+                 base::Owned(updated_local_ids), base::Owned(directory_changed),
+                 base::Owned(should_copy_on_server)));
 }
 
-void CopyOperation::CopyAfterPrepare(const CopyParams& params,
-                                     ResourceEntry* src_entry,
-                                     std::string* parent_resource_id,
-                                     FileError error) {
+void CopyOperation::CopyAfterTryToCopyLocally(
+    const CopyParams* params,
+    const std::vector<std::string>* updated_local_ids,
+    const bool* directory_changed,
+    const bool* should_copy_on_server,
+    FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(!params.callback.is_null());
+  DCHECK(!params->callback.is_null());
 
-  if (error != FILE_ERROR_OK) {
-    params.callback.Run(error);
+  for (size_t i = 0; i < updated_local_ids->size(); ++i)
+    observer_->OnEntryUpdatedByOperation((*updated_local_ids)[i]);
+
+  if (*directory_changed)
+    observer_->OnDirectoryChangedByOperation(params->dest_file_path.DirName());
+
+  if (error != FILE_ERROR_OK || !*should_copy_on_server) {
+    params->callback.Run(error);
     return;
   }
 
-  base::FilePath new_title = params.dest_file_path.BaseName();
-  if (src_entry->file_specific_info().is_hosted_document()) {
+  base::FilePath new_title = params->dest_file_path.BaseName();
+  if (params->src_entry.file_specific_info().is_hosted_document()) {
     // Drop the document extension, which should not be in the title.
     // TODO(yoshiki): Remove this code with crbug.com/223304.
     new_title = new_title.RemoveExtension();
   }
 
   base::Time last_modified =
-      params.preserve_last_modified ?
-      base::Time::FromInternalValue(src_entry->file_info().last_modified()) :
-      base::Time();
+      params->preserve_last_modified ?
+      base::Time::FromInternalValue(
+          params->src_entry.file_info().last_modified()) : base::Time();
 
   CopyResourceOnServer(
-      src_entry->resource_id(), *parent_resource_id,
-      new_title.AsUTF8Unsafe(), last_modified, params.callback);
+      params->src_entry.resource_id(), params->parent_entry.resource_id(),
+      new_title.AsUTF8Unsafe(), last_modified, params->callback);
 }
 
 void CopyOperation::TransferFileFromLocalToRemote(
@@ -360,64 +419,9 @@ void CopyOperation::ScheduleTransferRegularFile(
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  base::PostTaskAndReplyWithResult(
-      blocking_task_runner_.get(),
-      FROM_HERE,
-      base::Bind(&GetFileSize, local_src_path),
-      base::Bind(&CopyOperation::ScheduleTransferRegularFileAfterGetFileSize,
-                 weak_ptr_factory_.GetWeakPtr(),
-                 local_src_path, remote_dest_path, callback));
-}
-
-void CopyOperation::ScheduleTransferRegularFileAfterGetFileSize(
-    const base::FilePath& local_src_path,
-    const base::FilePath& remote_dest_path,
-    const FileOperationCallback& callback,
-    int64 local_file_size) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(!callback.is_null());
-
-  if (local_file_size < 0) {
-    callback.Run(FILE_ERROR_NOT_FOUND);
-    return;
-  }
-
-  // For regular files, check the server-side quota whether sufficient space
-  // is available for the file to be uploaded.
-  scheduler_->GetAboutResource(
-      base::Bind(
-          &CopyOperation::ScheduleTransferRegularFileAfterGetAboutResource,
-          weak_ptr_factory_.GetWeakPtr(),
-          local_src_path, remote_dest_path, callback, local_file_size));
-}
-
-void CopyOperation::ScheduleTransferRegularFileAfterGetAboutResource(
-    const base::FilePath& local_src_path,
-    const base::FilePath& remote_dest_path,
-    const FileOperationCallback& callback,
-    int64 local_file_size,
-    google_apis::GDataErrorCode status,
-    scoped_ptr<google_apis::AboutResource> about_resource) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(!callback.is_null());
-
-  FileError error = GDataToFileError(status);
-  if (error != FILE_ERROR_OK) {
-    callback.Run(error);
-    return;
-  }
-
-  DCHECK(about_resource);
-  const int64 space =
-      about_resource->quota_bytes_total() - about_resource->quota_bytes_used();
-  if (space < local_file_size) {
-    callback.Run(FILE_ERROR_NO_SERVER_SPACE);
-    return;
-  }
-
   create_file_operation_->CreateFile(
       remote_dest_path,
-      true,  // Exclusive (i.e. fail if a file already exists).
+      false,  // Not exclusive (OK even if a file already exists).
       std::string(),  // no specific mime type; CreateFile should guess it.
       base::Bind(&CopyOperation::ScheduleTransferRegularFileAfterCreate,
                  weak_ptr_factory_.GetWeakPtr(),
@@ -446,18 +450,22 @@ void CopyOperation::ScheduleTransferRegularFileAfterCreate(
           metadata_, cache_, local_src_path, remote_dest_path, local_id),
       base::Bind(
           &CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState,
-          weak_ptr_factory_.GetWeakPtr(), callback, base::Owned(local_id)));
+          weak_ptr_factory_.GetWeakPtr(), callback, remote_dest_path,
+          base::Owned(local_id)));
 }
 
 void CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState(
     const FileOperationCallback& callback,
+    const base::FilePath& remote_dest_path,
     std::string* local_id,
     FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  if (error == FILE_ERROR_OK)
+  if (error == FILE_ERROR_OK) {
+    observer_->OnDirectoryChangedByOperation(remote_dest_path.DirName());
     observer_->OnEntryUpdatedByOperation(*local_id);
+  }
   callback.Run(error);
 }