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 20 using content::BrowserThread; 21 22 namespace drive { 23 namespace file_system { 24 25 namespace { 26 27 const 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. 31 FileError 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. 74 FileError 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 FileError error = FILE_ERROR_NOT_A_FILE; 86 ResourceEntry entry; 87 if (ConvertToResourceEntry(*resource_entry, &entry)) 88 error = metadata->AddEntry(entry); 89 90 // Depending on timing, the metadata may have inserted via change list 91 // already. So, FILE_ERROR_EXISTS is not an error. 92 if (error == FILE_ERROR_EXISTS) 93 error = FILE_ERROR_OK; 94 95 if (error == FILE_ERROR_OK) { 96 // At this point, upload to the server is fully succeeded. 97 // Populate the |file_path| which will be used to notify the observer. 98 *file_path = metadata->GetFilePath(entry.resource_id()); 99 100 // Also store an empty file to the cache. 101 // Here, failure is not a fatal error, so ignore the returned code. 102 FileError cache_store_error = FILE_ERROR_FAILED; 103 base::FilePath empty_file; 104 if (file_util::CreateTemporaryFile(&empty_file)) { 105 cache_store_error = cache->Store( 106 entry.resource_id(), 107 entry.file_specific_info().md5(), 108 empty_file, 109 internal::FileCache::FILE_OPERATION_MOVE); 110 } 111 DLOG_IF(WARNING, cache_store_error != FILE_ERROR_OK) 112 << "Failed to store a cache file: " 113 << FileErrorToString(cache_store_error) 114 << ", resource_id: " << entry.resource_id(); 115 } 116 117 return error; 118 } 119 120 } // namespace 121 122 CreateFileOperation::CreateFileOperation( 123 base::SequencedTaskRunner* blocking_task_runner, 124 OperationObserver* observer, 125 JobScheduler* scheduler, 126 internal::ResourceMetadata* metadata, 127 internal::FileCache* cache) 128 : blocking_task_runner_(blocking_task_runner), 129 observer_(observer), 130 scheduler_(scheduler), 131 metadata_(metadata), 132 cache_(cache), 133 weak_ptr_factory_(this) { 134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 135 } 136 137 CreateFileOperation::~CreateFileOperation() { 138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 139 } 140 141 void CreateFileOperation::CreateFile(const base::FilePath& file_path, 142 bool is_exclusive, 143 const FileOperationCallback& callback) { 144 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 145 DCHECK(!callback.is_null()); 146 147 std::string* parent_resource_id = new std::string; 148 std::string* mime_type = new std::string; 149 base::PostTaskAndReplyWithResult( 150 blocking_task_runner_.get(), 151 FROM_HERE, 152 base::Bind(&CheckPreConditionForCreateFile, 153 metadata_, 154 file_path, 155 is_exclusive, 156 parent_resource_id, 157 mime_type), 158 base::Bind(&CreateFileOperation::CreateFileAfterCheckPreCondition, 159 weak_ptr_factory_.GetWeakPtr(), 160 file_path, 161 callback, 162 base::Owned(parent_resource_id), 163 base::Owned(mime_type))); 164 } 165 166 void CreateFileOperation::CreateFileAfterCheckPreCondition( 167 const base::FilePath& file_path, 168 const FileOperationCallback& callback, 169 std::string* parent_resource_id, 170 std::string* mime_type, 171 FileError error) { 172 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 173 DCHECK(!callback.is_null()); 174 DCHECK(parent_resource_id); 175 DCHECK(mime_type); 176 177 // If the file is found, or an error other than "not found" is found, 178 // runs callback and quit the operation. 179 if (error != FILE_ERROR_NOT_FOUND) { 180 callback.Run(error); 181 return; 182 } 183 184 scheduler_->CreateFile( 185 *parent_resource_id, 186 file_path, 187 file_path.BaseName().value(), 188 *mime_type, 189 ClientContext(USER_INITIATED), 190 base::Bind(&CreateFileOperation::CreateFileAfterUpload, 191 weak_ptr_factory_.GetWeakPtr(), 192 callback)); 193 } 194 195 void CreateFileOperation::CreateFileAfterUpload( 196 const FileOperationCallback& callback, 197 google_apis::GDataErrorCode gdata_error, 198 scoped_ptr<google_apis::ResourceEntry> resource_entry) { 199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 200 DCHECK(!callback.is_null()); 201 202 FileError error = GDataToFileError(gdata_error); 203 if (error != FILE_ERROR_OK) { 204 callback.Run(error); 205 return; 206 } 207 DCHECK(resource_entry); 208 209 base::FilePath* file_path = new base::FilePath; 210 base::PostTaskAndReplyWithResult( 211 blocking_task_runner_.get(), 212 FROM_HERE, 213 base::Bind(&UpdateLocalStateForCreateFile, 214 metadata_, 215 cache_, 216 base::Passed(&resource_entry), 217 file_path), 218 base::Bind(&CreateFileOperation::CreateFileAfterUpdateLocalState, 219 weak_ptr_factory_.GetWeakPtr(), 220 callback, 221 base::Owned(file_path))); 222 } 223 224 void CreateFileOperation::CreateFileAfterUpdateLocalState( 225 const FileOperationCallback& callback, 226 base::FilePath* file_path, 227 FileError error) { 228 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 229 DCHECK(!callback.is_null()); 230 DCHECK(file_path); 231 232 // Notify observer if the file creation process is successfully done. 233 if (error == FILE_ERROR_OK) 234 observer_->OnDirectoryChangedByOperation(file_path->DirName()); 235 236 callback.Run(error); 237 } 238 239 } // namespace file_system 240 } // namespace drive 241