1 // Copyright (c) 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_directory_operation.h" 6 7 #include "chrome/browser/chromeos/drive/drive.pb.h" 8 #include "chrome/browser/chromeos/drive/file_system/operation_observer.h" 9 #include "chrome/browser/chromeos/drive/file_system_util.h" 10 #include "chrome/browser/chromeos/drive/job_scheduler.h" 11 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h" 12 #include "chrome/browser/google_apis/gdata_errorcode.h" 13 #include "chrome/browser/google_apis/gdata_wapi_parser.h" 14 #include "content/public/browser/browser_thread.h" 15 16 using content::BrowserThread; 17 18 namespace drive { 19 namespace file_system { 20 21 namespace { 22 23 // Part of CreateDirectoryRecursively(). Adds an |entry| for new directory 24 // to |metadata|, and return the status. If succeeded, |file_path| will store 25 // the path to the result file. 26 FileError UpdateLocalStateForCreateDirectoryRecursively( 27 internal::ResourceMetadata* metadata, 28 const ResourceEntry& entry, 29 base::FilePath* file_path) { 30 DCHECK(metadata); 31 DCHECK(file_path); 32 33 FileError result = metadata->AddEntry(entry); 34 // Depending on timing, a metadata may be updated by change list already. 35 // So, FILE_ERROR_EXISTS is not an error. 36 if (result == FILE_ERROR_EXISTS) 37 result = FILE_ERROR_OK; 38 39 if (result == FILE_ERROR_OK) 40 *file_path = metadata->GetFilePath(entry.resource_id()); 41 42 return result; 43 } 44 45 } // namespace 46 47 CreateDirectoryOperation::CreateDirectoryOperation( 48 base::SequencedTaskRunner* blocking_task_runner, 49 OperationObserver* observer, 50 JobScheduler* scheduler, 51 internal::ResourceMetadata* metadata) 52 : blocking_task_runner_(blocking_task_runner), 53 observer_(observer), 54 scheduler_(scheduler), 55 metadata_(metadata), 56 weak_ptr_factory_(this) { 57 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 58 } 59 60 CreateDirectoryOperation::~CreateDirectoryOperation() { 61 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 62 } 63 64 void CreateDirectoryOperation::CreateDirectory( 65 const base::FilePath& directory_path, 66 bool is_exclusive, 67 bool is_recursive, 68 const FileOperationCallback& callback) { 69 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 70 DCHECK(!callback.is_null()); 71 72 ResourceEntry* entry = new ResourceEntry; 73 base::PostTaskAndReplyWithResult( 74 blocking_task_runner_.get(), 75 FROM_HERE, 76 base::Bind(&CreateDirectoryOperation::GetExistingDeepestDirectory, 77 metadata_, 78 directory_path, 79 entry), 80 base::Bind(&CreateDirectoryOperation:: 81 CreateDirectoryAfterGetExistingDeepestDirectory, 82 weak_ptr_factory_.GetWeakPtr(), 83 directory_path, 84 is_exclusive, 85 is_recursive, 86 callback, 87 base::Owned(entry))); 88 } 89 90 // static 91 base::FilePath CreateDirectoryOperation::GetExistingDeepestDirectory( 92 internal::ResourceMetadata* metadata, 93 const base::FilePath& directory_path, 94 ResourceEntry* entry) { 95 DCHECK(metadata); 96 DCHECK(entry); 97 98 std::vector<base::FilePath::StringType> components; 99 directory_path.GetComponents(&components); 100 101 if (components.empty() || components[0] != util::kDriveGrandRootDirName) 102 return base::FilePath(); 103 104 std::string resource_id = util::kDriveGrandRootSpecialResourceId; 105 for (size_t i = 1; i < components.size(); ++i) { 106 std::string child_resource_id = 107 metadata->GetChildResourceId(resource_id, components[i]); 108 if (child_resource_id.empty()) 109 break; 110 resource_id = child_resource_id; 111 } 112 113 FileError error = metadata->GetResourceEntryById(resource_id, entry); 114 DCHECK_EQ(FILE_ERROR_OK, error); 115 116 if (!entry->file_info().is_directory()) 117 return base::FilePath(); 118 119 return metadata->GetFilePath(resource_id); 120 } 121 122 void CreateDirectoryOperation::CreateDirectoryAfterGetExistingDeepestDirectory( 123 const base::FilePath& directory_path, 124 bool is_exclusive, 125 bool is_recursive, 126 const FileOperationCallback& callback, 127 ResourceEntry* existing_deepest_directory_entry, 128 const base::FilePath& existing_deepest_directory_path) { 129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 130 DCHECK(!callback.is_null()); 131 DCHECK(existing_deepest_directory_entry); 132 133 if (existing_deepest_directory_path.empty()) { 134 callback.Run(FILE_ERROR_NOT_FOUND); 135 return; 136 } 137 138 if (directory_path == existing_deepest_directory_path) { 139 callback.Run(is_exclusive ? FILE_ERROR_EXISTS : FILE_ERROR_OK); 140 return; 141 } 142 143 // If it is not recursive creation, the found directory must be the direct 144 // parent of |directory_path| to ensure creating exact one directory. 145 if (!is_recursive && 146 existing_deepest_directory_path != directory_path.DirName()) { 147 callback.Run(FILE_ERROR_NOT_FOUND); 148 return; 149 } 150 151 // Create directories under the found directory. 152 base::FilePath remaining_path; 153 existing_deepest_directory_path.AppendRelativePath( 154 directory_path, &remaining_path); 155 CreateDirectoryRecursively(existing_deepest_directory_entry->resource_id(), 156 remaining_path, callback); 157 } 158 159 void CreateDirectoryOperation::CreateDirectoryRecursively( 160 const std::string& parent_resource_id, 161 const base::FilePath& relative_file_path, 162 const FileOperationCallback& callback) { 163 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 164 DCHECK(!callback.is_null()); 165 166 // Split the first component and remaining ones of |relative_file_path|. 167 std::vector<base::FilePath::StringType> components; 168 relative_file_path.GetComponents(&components); 169 DCHECK(!components.empty()); 170 base::FilePath title(components[0]); 171 base::FilePath remaining_path; 172 title.AppendRelativePath(relative_file_path, &remaining_path); 173 174 scheduler_->AddNewDirectory( 175 parent_resource_id, 176 title.AsUTF8Unsafe(), 177 base::Bind(&CreateDirectoryOperation 178 ::CreateDirectoryRecursivelyAfterAddNewDirectory, 179 weak_ptr_factory_.GetWeakPtr(), remaining_path, callback)); 180 } 181 182 void CreateDirectoryOperation::CreateDirectoryRecursivelyAfterAddNewDirectory( 183 const base::FilePath& remaining_path, 184 const FileOperationCallback& callback, 185 google_apis::GDataErrorCode gdata_error, 186 scoped_ptr<google_apis::ResourceEntry> resource_entry) { 187 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 188 DCHECK(!callback.is_null()); 189 190 FileError error = GDataToFileError(gdata_error); 191 if (error != FILE_ERROR_OK) { 192 callback.Run(error); 193 return; 194 } 195 DCHECK(resource_entry); 196 197 ResourceEntry entry; 198 if (!ConvertToResourceEntry(*resource_entry, &entry)) { 199 callback.Run(FILE_ERROR_NOT_A_FILE); 200 return; 201 } 202 203 // Note that the created directory may be renamed inside 204 // ResourceMetadata::AddEntry due to name confliction. 205 // What we actually need here is the new created path (not the path we try 206 // to create). 207 base::FilePath* file_path = new base::FilePath; 208 base::PostTaskAndReplyWithResult( 209 blocking_task_runner_.get(), 210 FROM_HERE, 211 base::Bind(&UpdateLocalStateForCreateDirectoryRecursively, 212 metadata_, 213 entry, 214 file_path), 215 base::Bind(&CreateDirectoryOperation:: 216 CreateDirectoryRecursivelyAfterUpdateLocalState, 217 weak_ptr_factory_.GetWeakPtr(), 218 resource_entry->resource_id(), 219 remaining_path, 220 callback, 221 base::Owned(file_path))); 222 } 223 224 void CreateDirectoryOperation::CreateDirectoryRecursivelyAfterUpdateLocalState( 225 const std::string& resource_id, 226 const base::FilePath& remaining_path, 227 const FileOperationCallback& callback, 228 base::FilePath* file_path, 229 FileError error) { 230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 231 DCHECK(!callback.is_null()); 232 233 if (error != FILE_ERROR_OK) { 234 callback.Run(error); 235 return; 236 } 237 238 observer_->OnDirectoryChangedByOperation(file_path->DirName()); 239 240 if (remaining_path.empty()) { 241 // All directories are created successfully. 242 callback.Run(FILE_ERROR_OK); 243 return; 244 } 245 246 // Create descendant directories. 247 CreateDirectoryRecursively(resource_id, remaining_path, callback); 248 } 249 250 } // namespace file_system 251 } // namespace drive 252