15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/chromeos/drive/sync/entry_update_performer.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#include "base/callback_helpers.h" 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/files/file_util.h" 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/chromeos/drive/change_list_loader.h" 10424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#include "chrome/browser/chromeos/drive/drive.pb.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/chromeos/drive/file_cache.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/chromeos/drive/file_change.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/chromeos/drive/file_system/operation_delegate.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/chromeos/drive/file_system_util.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/chromeos/drive/job_scheduler.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/chromeos/drive/resource_metadata.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/chromeos/drive/sync/entry_revert_performer.h" 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/chromeos/drive/sync/remove_performer.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h" 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "google_apis/drive/drive_api_parser.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread; 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace drive { 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace internal { 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct EntryUpdatePerformer::LocalState { 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LocalState() : should_content_update(false) { 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ResourceEntry entry; 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ResourceEntry parent_entry; 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::FilePath drive_file_path; 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::FilePath cache_file_path; 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool should_content_update; 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 37424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 38424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)namespace { 39424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 40424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// Looks up ResourceEntry for source entry and its parent. 41424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)FileError PrepareUpdate(ResourceMetadata* metadata, 42424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) FileCache* cache, 43424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const std::string& local_id, 44424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) EntryUpdatePerformer::LocalState* local_state) { 45424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) FileError error = metadata->GetResourceEntryById(local_id, 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &local_state->entry); 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (error != FILE_ERROR_OK) 482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return error; 492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) error = metadata->GetResourceEntryById(local_state->entry.parent_local_id(), 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &local_state->parent_entry); 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (error != FILE_ERROR_OK) 5358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) return error; 5458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 5558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) error = metadata->GetFilePath(local_id, &local_state->drive_file_path); 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (error != FILE_ERROR_OK) 5758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) return error; 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!local_state->entry.file_info().is_directory() && 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) !local_state->entry.file_specific_info().cache_state().is_present() && 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) local_state->entry.resource_id().empty()) { 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Locally created file with no cache file, store an empty file. 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::FilePath empty_file; 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!base::CreateTemporaryFile(&empty_file)) 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return FILE_ERROR_FAILED; 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error = cache->Store(local_id, std::string(), empty_file, 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FileCache::FILE_OPERATION_MOVE); 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (error != FILE_ERROR_OK) 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return error; 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error = metadata->GetResourceEntryById(local_id, &local_state->entry); 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (error != FILE_ERROR_OK) 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return error; 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Check if content update is needed or not. 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (local_state->entry.file_specific_info().cache_state().is_dirty() && 772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) !cache->IsOpenedForWrite(local_id)) { 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Update cache entry's MD5 if needed. 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (local_state->entry.file_specific_info().cache_state().md5().empty()) { 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error = cache->UpdateMd5(local_id); 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (error != FILE_ERROR_OK) 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return error; 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error = metadata->GetResourceEntryById(local_id, &local_state->entry); 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (error != FILE_ERROR_OK) 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return error; 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (local_state->entry.file_specific_info().cache_state().md5() == 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) local_state->entry.file_specific_info().md5()) { 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error = cache->ClearDirty(local_id); 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (error != FILE_ERROR_OK) 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return error; 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error = cache->GetFile(local_id, &local_state->cache_file_path); 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (error != FILE_ERROR_OK) 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return error; 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) local_state->should_content_update = true; 99 } 100 } 101 102 // Update metadata_edit_state. 103 switch (local_state->entry.metadata_edit_state()) { 104 case ResourceEntry::CLEAN: // Nothing to do. 105 case ResourceEntry::SYNCING: // Error during the last update. Go ahead. 106 break; 107 108 case ResourceEntry::DIRTY: 109 local_state->entry.set_metadata_edit_state(ResourceEntry::SYNCING); 110 error = metadata->RefreshEntry(local_state->entry); 111 if (error != FILE_ERROR_OK) 112 return error; 113 break; 114 } 115 return FILE_ERROR_OK; 116} 117 118FileError FinishUpdate(ResourceMetadata* metadata, 119 FileCache* cache, 120 const std::string& local_id, 121 scoped_ptr<google_apis::FileResource> file_resource, 122 FileChange* changed_files) { 123 ResourceEntry entry; 124 FileError error = metadata->GetResourceEntryById(local_id, &entry); 125 if (error != FILE_ERROR_OK) 126 return error; 127 128 // When creating new entries, update check may add a new entry with the same 129 // resource ID before us. If such an entry exists, remove it. 130 std::string existing_local_id; 131 error = 132 metadata->GetIdByResourceId(file_resource->file_id(), &existing_local_id); 133 134 switch (error) { 135 case FILE_ERROR_OK: 136 if (existing_local_id != local_id) { 137 base::FilePath existing_entry_path; 138 error = metadata->GetFilePath(existing_local_id, &existing_entry_path); 139 if (error != FILE_ERROR_OK) 140 return error; 141 error = metadata->RemoveEntry(existing_local_id); 142 if (error != FILE_ERROR_OK) 143 return error; 144 changed_files->Update(existing_entry_path, entry, FileChange::DELETE); 145 } 146 break; 147 case FILE_ERROR_NOT_FOUND: 148 break; 149 default: 150 return error; 151 } 152 153 // Update metadata_edit_state and MD5. 154 switch (entry.metadata_edit_state()) { 155 case ResourceEntry::CLEAN: // Nothing to do. 156 case ResourceEntry::DIRTY: // Entry was edited again during the update. 157 break; 158 159 case ResourceEntry::SYNCING: 160 entry.set_metadata_edit_state(ResourceEntry::CLEAN); 161 break; 162 } 163 if (!entry.file_info().is_directory()) 164 entry.mutable_file_specific_info()->set_md5(file_resource->md5_checksum()); 165 entry.set_resource_id(file_resource->file_id()); 166 error = metadata->RefreshEntry(entry); 167 if (error != FILE_ERROR_OK) 168 return error; 169 base::FilePath entry_path; 170 error = metadata->GetFilePath(local_id, &entry_path); 171 if (error != FILE_ERROR_OK) 172 return error; 173 changed_files->Update(entry_path, entry, FileChange::ADD_OR_UPDATE); 174 175 // Clear dirty bit unless the file has been edited during update. 176 if (entry.file_specific_info().cache_state().is_dirty() && 177 entry.file_specific_info().cache_state().md5() == 178 entry.file_specific_info().md5()) { 179 error = cache->ClearDirty(local_id); 180 if (error != FILE_ERROR_OK) 181 return error; 182 } 183 return FILE_ERROR_OK; 184} 185 186} // namespace 187 188EntryUpdatePerformer::EntryUpdatePerformer( 189 base::SequencedTaskRunner* blocking_task_runner, 190 file_system::OperationDelegate* delegate, 191 JobScheduler* scheduler, 192 ResourceMetadata* metadata, 193 FileCache* cache, 194 LoaderController* loader_controller) 195 : blocking_task_runner_(blocking_task_runner), 196 delegate_(delegate), 197 scheduler_(scheduler), 198 metadata_(metadata), 199 cache_(cache), 200 loader_controller_(loader_controller), 201 remove_performer_(new RemovePerformer(blocking_task_runner, 202 delegate, 203 scheduler, 204 metadata)), 205 entry_revert_performer_(new EntryRevertPerformer(blocking_task_runner, 206 delegate, 207 scheduler, 208 metadata)), 209 weak_ptr_factory_(this) { 210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 211} 212 213EntryUpdatePerformer::~EntryUpdatePerformer() { 214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 215} 216 217void EntryUpdatePerformer::UpdateEntry(const std::string& local_id, 218 const ClientContext& context, 219 const FileOperationCallback& callback) { 220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 221 DCHECK(!callback.is_null()); 222 223 scoped_ptr<LocalState> local_state(new LocalState); 224 LocalState* local_state_ptr = local_state.get(); 225 base::PostTaskAndReplyWithResult( 226 blocking_task_runner_.get(), 227 FROM_HERE, 228 base::Bind(&PrepareUpdate, metadata_, cache_, local_id, local_state_ptr), 229 base::Bind(&EntryUpdatePerformer::UpdateEntryAfterPrepare, 230 weak_ptr_factory_.GetWeakPtr(), context, callback, 231 base::Passed(&local_state))); 232} 233 234void EntryUpdatePerformer::UpdateEntryAfterPrepare( 235 const ClientContext& context, 236 const FileOperationCallback& callback, 237 scoped_ptr<LocalState> local_state, 238 FileError error) { 239 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 240 DCHECK(!callback.is_null()); 241 242 if (error != FILE_ERROR_OK) { 243 callback.Run(error); 244 return; 245 } 246 247 // Trashed entry should be removed. 248 if (local_state->entry.parent_local_id() == util::kDriveTrashDirLocalId) { 249 remove_performer_->Remove(local_state->entry.local_id(), context, callback); 250 return; 251 } 252 253 // Parent was locally created and needs update. Just return for now. 254 // This entry should be updated again after the parent update completes. 255 if (local_state->parent_entry.resource_id().empty() && 256 local_state->parent_entry.metadata_edit_state() != ResourceEntry::CLEAN) { 257 callback.Run(FILE_ERROR_OK); 258 return; 259 } 260 261 base::Time last_modified = base::Time::FromInternalValue( 262 local_state->entry.file_info().last_modified()); 263 base::Time last_accessed = base::Time::FromInternalValue( 264 local_state->entry.file_info().last_accessed()); 265 266 // Perform content update. 267 if (local_state->should_content_update) { 268 if (local_state->entry.resource_id().empty()) { 269 // Not locking the loader intentionally here to avoid making the UI 270 // unresponsive while uploading large files. 271 // FinishUpdate() is responsible to resolve conflicts caused by this. 272 scoped_ptr<base::ScopedClosureRunner> null_loader_lock; 273 274 DriveUploader::UploadNewFileOptions options; 275 options.modified_date = last_modified; 276 options.last_viewed_by_me_date = last_accessed; 277 scheduler_->UploadNewFile( 278 local_state->parent_entry.resource_id(), 279 local_state->drive_file_path, 280 local_state->cache_file_path, 281 local_state->entry.title(), 282 local_state->entry.file_specific_info().content_mime_type(), 283 options, 284 context, 285 base::Bind(&EntryUpdatePerformer::UpdateEntryAfterUpdateResource, 286 weak_ptr_factory_.GetWeakPtr(), 287 context, 288 callback, 289 local_state->entry.local_id(), 290 base::Passed(&null_loader_lock))); 291 } else { 292 DriveUploader::UploadExistingFileOptions options; 293 options.title = local_state->entry.title(); 294 options.parent_resource_id = local_state->parent_entry.resource_id(); 295 options.modified_date = last_modified; 296 options.last_viewed_by_me_date = last_accessed; 297 scheduler_->UploadExistingFile( 298 local_state->entry.resource_id(), 299 local_state->drive_file_path, 300 local_state->cache_file_path, 301 local_state->entry.file_specific_info().content_mime_type(), 302 options, 303 context, 304 base::Bind(&EntryUpdatePerformer::UpdateEntryAfterUpdateResource, 305 weak_ptr_factory_.GetWeakPtr(), 306 context, 307 callback, 308 local_state->entry.local_id(), 309 base::Passed(scoped_ptr<base::ScopedClosureRunner>()))); 310 } 311 return; 312 } 313 314 // Create directory. 315 if (local_state->entry.file_info().is_directory() && 316 local_state->entry.resource_id().empty()) { 317 // Lock the loader to avoid race conditions. 318 scoped_ptr<base::ScopedClosureRunner> loader_lock = 319 loader_controller_->GetLock(); 320 321 DriveServiceInterface::AddNewDirectoryOptions options; 322 options.modified_date = last_modified; 323 options.last_viewed_by_me_date = last_accessed; 324 scheduler_->AddNewDirectory( 325 local_state->parent_entry.resource_id(), 326 local_state->entry.title(), 327 options, 328 context, 329 base::Bind(&EntryUpdatePerformer::UpdateEntryAfterUpdateResource, 330 weak_ptr_factory_.GetWeakPtr(), 331 context, 332 callback, 333 local_state->entry.local_id(), 334 base::Passed(&loader_lock))); 335 return; 336 } 337 338 // No need to perform update. 339 if (local_state->entry.metadata_edit_state() == ResourceEntry::CLEAN || 340 local_state->entry.resource_id().empty()) { 341 callback.Run(FILE_ERROR_OK); 342 return; 343 } 344 345 // Perform metadata update. 346 scheduler_->UpdateResource( 347 local_state->entry.resource_id(), local_state->parent_entry.resource_id(), 348 local_state->entry.title(), last_modified, last_accessed, 349 context, 350 base::Bind(&EntryUpdatePerformer::UpdateEntryAfterUpdateResource, 351 weak_ptr_factory_.GetWeakPtr(), 352 context, callback, local_state->entry.local_id(), 353 base::Passed(scoped_ptr<base::ScopedClosureRunner>()))); 354} 355 356void EntryUpdatePerformer::UpdateEntryAfterUpdateResource( 357 const ClientContext& context, 358 const FileOperationCallback& callback, 359 const std::string& local_id, 360 scoped_ptr<base::ScopedClosureRunner> loader_lock, 361 google_apis::GDataErrorCode status, 362 scoped_ptr<google_apis::FileResource> entry) { 363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 364 DCHECK(!callback.is_null()); 365 366 if (status == google_apis::HTTP_FORBIDDEN) { 367 // Editing this entry is not allowed, revert local changes. 368 entry_revert_performer_->RevertEntry(local_id, context, callback); 369 return; 370 } 371 372 FileError error = GDataToFileError(status); 373 if (error != FILE_ERROR_OK) { 374 callback.Run(error); 375 return; 376 } 377 378 FileChange* changed_files = new FileChange; 379 base::PostTaskAndReplyWithResult( 380 blocking_task_runner_.get(), 381 FROM_HERE, 382 base::Bind(&FinishUpdate, 383 metadata_, 384 cache_, 385 local_id, 386 base::Passed(&entry), 387 changed_files), 388 base::Bind(&EntryUpdatePerformer::UpdateEntryAfterFinish, 389 weak_ptr_factory_.GetWeakPtr(), 390 callback, 391 base::Owned(changed_files))); 392} 393 394void EntryUpdatePerformer::UpdateEntryAfterFinish( 395 const FileOperationCallback& callback, 396 const FileChange* changed_files, 397 FileError error) { 398 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 399 DCHECK(!callback.is_null()); 400 401 delegate_->OnFileChangedByOperation(*changed_files); 402 callback.Run(error); 403} 404 405} // namespace internal 406} // namespace drive 407