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