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