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