create_file_operation.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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/file_system/create_file_operation.h" 6 7#include <string> 8 9#include "base/file_util.h" 10#include "chrome/browser/chromeos/drive/drive.pb.h" 11#include "chrome/browser/chromeos/drive/file_cache.h" 12#include "chrome/browser/chromeos/drive/file_system/operation_observer.h" 13#include "chrome/browser/chromeos/drive/file_system_util.h" 14#include "chrome/browser/chromeos/drive/job_scheduler.h" 15#include "chrome/browser/chromeos/drive/resource_entry_conversion.h" 16#include "chrome/browser/chromeos/drive/resource_metadata.h" 17#include "content/public/browser/browser_thread.h" 18#include "net/base/mime_util.h" 19 20using content::BrowserThread; 21 22namespace drive { 23namespace file_system { 24 25namespace { 26 27const char kMimeTypeOctetStream[] = "application/octet-stream"; 28 29// Part of CreateFileOperation::CreateFile(), runs on |blocking_task_runner_| 30// of the operation, before server-side file creation. 31FileError CheckPreConditionForCreateFile(internal::ResourceMetadata* metadata, 32 const base::FilePath& file_path, 33 bool is_exclusive, 34 std::string* parent_resource_id, 35 std::string* mime_type) { 36 DCHECK(metadata); 37 DCHECK(parent_resource_id); 38 DCHECK(mime_type); 39 40 ResourceEntry entry; 41 FileError error = metadata->GetResourceEntryByPath(file_path, &entry); 42 if (error == FILE_ERROR_OK) { 43 // Error if an exclusive mode is requested, or the entry is not a file. 44 return (is_exclusive || 45 entry.file_info().is_directory() || 46 entry.file_specific_info().is_hosted_document()) ? 47 FILE_ERROR_EXISTS : FILE_ERROR_OK; 48 } 49 50 // If the file is not found, an actual request to create a new file will be 51 // sent to the server. 52 if (error == FILE_ERROR_NOT_FOUND) { 53 // If parent path is not a directory, it is an error. 54 ResourceEntry parent; 55 if (metadata->GetResourceEntryByPath( 56 file_path.DirName(), &parent) != FILE_ERROR_OK || 57 !parent.file_info().is_directory()) 58 return FILE_ERROR_NOT_A_DIRECTORY; 59 60 // In the request, parent_resource_id and mime_type are needed. 61 // Here, populate them. 62 *parent_resource_id = parent.resource_id(); 63 64 // If mime type is unsure, use octet stream by default. 65 if (!net::GetMimeTypeFromFile(file_path, mime_type)) 66 *mime_type = kMimeTypeOctetStream; 67 } 68 69 return error; 70} 71 72// Part of CreateFileOperation::CreateFile(), runs on |blocking_task_runner_| 73// of the operation, after server side file creation. 74FileError UpdateLocalStateForCreateFile( 75 internal::ResourceMetadata* metadata, 76 internal::FileCache* cache, 77 scoped_ptr<google_apis::ResourceEntry> resource_entry, 78 base::FilePath* file_path) { 79 DCHECK(metadata); 80 DCHECK(cache); 81 DCHECK(resource_entry); 82 DCHECK(file_path); 83 84 // Add the entry to the local resource metadata. 85 ResourceEntry entry = ConvertToResourceEntry(*resource_entry); 86 FileError error = metadata->AddEntry(entry); 87 88 // Depending on timing, the metadata may have inserted via change list feed 89 // already. So, FILE_ERROR_EXISTS is not an error. 90 if (error == FILE_ERROR_EXISTS) 91 error = FILE_ERROR_OK; 92 93 if (error == FILE_ERROR_OK) { 94 // At this point, upload to the server is fully succeeded. 95 // Populate the |file_path| which will be used to notify the observer. 96 *file_path = metadata->GetFilePath(entry.resource_id()); 97 98 // Also store an empty file to the cache. 99 // Here, failure is not a fatal error, so ignore the returned code. 100 FileError cache_store_error = FILE_ERROR_FAILED; 101 base::FilePath empty_file; 102 if (file_util::CreateTemporaryFile(&empty_file)) { 103 cache_store_error = cache->Store( 104 entry.resource_id(), 105 entry.file_specific_info().file_md5(), 106 empty_file, 107 internal::FileCache::FILE_OPERATION_MOVE); 108 } 109 DLOG_IF(WARNING, cache_store_error != FILE_ERROR_OK) 110 << "Failed to store a cache file: " 111 << FileErrorToString(cache_store_error) 112 << ", resource_id: " << entry.resource_id(); 113 } 114 115 return error; 116} 117 118} // namespace 119 120CreateFileOperation::CreateFileOperation( 121 base::SequencedTaskRunner* blocking_task_runner, 122 OperationObserver* observer, 123 JobScheduler* scheduler, 124 internal::ResourceMetadata* metadata, 125 internal::FileCache* cache) 126 : blocking_task_runner_(blocking_task_runner), 127 observer_(observer), 128 scheduler_(scheduler), 129 metadata_(metadata), 130 cache_(cache), 131 weak_ptr_factory_(this) { 132 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 133} 134 135CreateFileOperation::~CreateFileOperation() { 136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 137} 138 139void CreateFileOperation::CreateFile(const base::FilePath& file_path, 140 bool is_exclusive, 141 const FileOperationCallback& callback) { 142 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 143 DCHECK(!callback.is_null()); 144 145 std::string* parent_resource_id = new std::string; 146 std::string* mime_type = new std::string; 147 base::PostTaskAndReplyWithResult( 148 blocking_task_runner_, 149 FROM_HERE, 150 base::Bind(&CheckPreConditionForCreateFile, 151 metadata_, file_path, is_exclusive, 152 parent_resource_id, mime_type), 153 base::Bind(&CreateFileOperation::CreateFileAfterCheckPreCondition, 154 weak_ptr_factory_.GetWeakPtr(), 155 file_path, callback, 156 base::Owned(parent_resource_id), base::Owned(mime_type))); 157} 158 159void CreateFileOperation::CreateFileAfterCheckPreCondition( 160 const base::FilePath& file_path, 161 const FileOperationCallback& callback, 162 std::string* parent_resource_id, 163 std::string* mime_type, 164 FileError error) { 165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 166 DCHECK(!callback.is_null()); 167 DCHECK(parent_resource_id); 168 DCHECK(mime_type); 169 170 // If the file is found, or an error other than "not found" is found, 171 // runs callback and quit the operation. 172 if (error != FILE_ERROR_NOT_FOUND) { 173 callback.Run(error); 174 return; 175 } 176 177 scheduler_->CreateFile( 178 *parent_resource_id, 179 file_path, 180 file_path.BaseName().value(), 181 *mime_type, 182 ClientContext(USER_INITIATED), 183 base::Bind(&CreateFileOperation::CreateFileAfterUpload, 184 weak_ptr_factory_.GetWeakPtr(), 185 callback)); 186} 187 188void CreateFileOperation::CreateFileAfterUpload( 189 const FileOperationCallback& callback, 190 google_apis::GDataErrorCode gdata_error, 191 scoped_ptr<google_apis::ResourceEntry> resource_entry) { 192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 193 DCHECK(!callback.is_null()); 194 195 FileError error = util::GDataToFileError(gdata_error); 196 if (error != FILE_ERROR_OK) { 197 callback.Run(error); 198 return; 199 } 200 DCHECK(resource_entry); 201 202 base::FilePath* file_path = new base::FilePath; 203 base::PostTaskAndReplyWithResult( 204 blocking_task_runner_, 205 FROM_HERE, 206 base::Bind(&UpdateLocalStateForCreateFile, 207 metadata_, cache_, base::Passed(&resource_entry), file_path), 208 base::Bind(&CreateFileOperation::CreateFileAfterUpdateLocalState, 209 weak_ptr_factory_.GetWeakPtr(), 210 callback, base::Owned(file_path))); 211} 212 213void CreateFileOperation::CreateFileAfterUpdateLocalState( 214 const FileOperationCallback& callback, 215 base::FilePath* file_path, 216 FileError error) { 217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 218 DCHECK(!callback.is_null()); 219 DCHECK(file_path); 220 221 // Notify observer if the file creation process is successfully done. 222 if (error == FILE_ERROR_OK) 223 observer_->OnDirectoryChangedByOperation(file_path->DirName()); 224 225 callback.Run(error); 226} 227 228} // namespace file_system 229} // namespace drive 230