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