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.
5 #include "chrome/browser/chromeos/drive/file_system/create_directory_operation.h"
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"
16 using content::BrowserThread;
19 namespace file_system {
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 scoped_ptr<google_apis::ResourceEntry> resource_entry,
29 base::FilePath* file_path) {
34 std::string parent_resource_id;
35 if (!ConvertToResourceEntry(*resource_entry, &entry, &parent_resource_id))
36 return FILE_ERROR_NOT_A_FILE;
38 std::string parent_local_id;
39 FileError result = metadata->GetIdByResourceId(parent_resource_id,
41 if (result != FILE_ERROR_OK)
43 entry.set_parent_local_id(parent_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);
52 if (result == FILE_ERROR_OK)
53 *file_path = metadata->GetFilePath(local_id);
60 CreateDirectoryOperation::CreateDirectoryOperation(
61 base::SequencedTaskRunner* blocking_task_runner,
62 OperationObserver* observer,
63 JobScheduler* scheduler,
64 internal::ResourceMetadata* metadata)
65 : blocking_task_runner_(blocking_task_runner),
67 scheduler_(scheduler),
69 weak_ptr_factory_(this) {
70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
73 CreateDirectoryOperation::~CreateDirectoryOperation() {
74 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
77 void CreateDirectoryOperation::CreateDirectory(
78 const base::FilePath& directory_path,
81 const FileOperationCallback& callback) {
82 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
83 DCHECK(!callback.is_null());
85 ResourceEntry* entry = new ResourceEntry;
86 base::PostTaskAndReplyWithResult(
87 blocking_task_runner_.get(),
89 base::Bind(&CreateDirectoryOperation::GetExistingDeepestDirectory,
93 base::Bind(&CreateDirectoryOperation::
94 CreateDirectoryAfterGetExistingDeepestDirectory,
95 weak_ptr_factory_.GetWeakPtr(),
100 base::Owned(entry)));
104 base::FilePath CreateDirectoryOperation::GetExistingDeepestDirectory(
105 internal::ResourceMetadata* metadata,
106 const base::FilePath& directory_path,
107 ResourceEntry* entry) {
111 std::vector<base::FilePath::StringType> components;
112 directory_path.GetComponents(&components);
114 if (components.empty() || components[0] != util::kDriveGrandRootDirName)
115 return base::FilePath();
117 std::string local_id = util::kDriveGrandRootSpecialResourceId;
118 for (size_t i = 1; i < components.size(); ++i) {
119 std::string child_local_id = metadata->GetChildId(local_id, components[i]);
120 if (child_local_id.empty())
122 local_id = child_local_id;
125 FileError error = metadata->GetResourceEntryById(local_id, entry);
126 DCHECK_EQ(FILE_ERROR_OK, error);
128 if (!entry->file_info().is_directory())
129 return base::FilePath();
131 return metadata->GetFilePath(local_id);
134 void CreateDirectoryOperation::CreateDirectoryAfterGetExistingDeepestDirectory(
135 const base::FilePath& directory_path,
138 const FileOperationCallback& callback,
139 ResourceEntry* existing_deepest_directory_entry,
140 const base::FilePath& existing_deepest_directory_path) {
141 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
142 DCHECK(!callback.is_null());
143 DCHECK(existing_deepest_directory_entry);
145 if (existing_deepest_directory_path.empty()) {
146 callback.Run(FILE_ERROR_NOT_FOUND);
150 if (directory_path == existing_deepest_directory_path) {
151 callback.Run(is_exclusive ? FILE_ERROR_EXISTS : FILE_ERROR_OK);
155 // If it is not recursive creation, the found directory must be the direct
156 // parent of |directory_path| to ensure creating exact one directory.
158 existing_deepest_directory_path != directory_path.DirName()) {
159 callback.Run(FILE_ERROR_NOT_FOUND);
163 // Create directories under the found directory.
164 base::FilePath remaining_path;
165 existing_deepest_directory_path.AppendRelativePath(
166 directory_path, &remaining_path);
167 CreateDirectoryRecursively(existing_deepest_directory_entry->resource_id(),
168 remaining_path, callback);
171 void CreateDirectoryOperation::CreateDirectoryRecursively(
172 const std::string& parent_resource_id,
173 const base::FilePath& relative_file_path,
174 const FileOperationCallback& callback) {
175 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
176 DCHECK(!callback.is_null());
178 // Split the first component and remaining ones of |relative_file_path|.
179 std::vector<base::FilePath::StringType> components;
180 relative_file_path.GetComponents(&components);
181 DCHECK(!components.empty());
182 base::FilePath title(components[0]);
183 base::FilePath remaining_path;
184 title.AppendRelativePath(relative_file_path, &remaining_path);
186 scheduler_->AddNewDirectory(
188 title.AsUTF8Unsafe(),
189 base::Bind(&CreateDirectoryOperation
190 ::CreateDirectoryRecursivelyAfterAddNewDirectory,
191 weak_ptr_factory_.GetWeakPtr(), remaining_path, callback));
194 void CreateDirectoryOperation::CreateDirectoryRecursivelyAfterAddNewDirectory(
195 const base::FilePath& remaining_path,
196 const FileOperationCallback& callback,
197 google_apis::GDataErrorCode gdata_error,
198 scoped_ptr<google_apis::ResourceEntry> resource_entry) {
199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
200 DCHECK(!callback.is_null());
202 FileError error = GDataToFileError(gdata_error);
203 if (error != FILE_ERROR_OK) {
207 DCHECK(resource_entry);
208 const std::string& resource_id = resource_entry->resource_id();
210 // Note that the created directory may be renamed inside
211 // ResourceMetadata::AddEntry due to name confliction.
212 // What we actually need here is the new created path (not the path we try
214 base::FilePath* file_path = new base::FilePath;
215 base::PostTaskAndReplyWithResult(
216 blocking_task_runner_.get(),
218 base::Bind(&UpdateLocalStateForCreateDirectoryRecursively,
220 base::Passed(&resource_entry),
222 base::Bind(&CreateDirectoryOperation::
223 CreateDirectoryRecursivelyAfterUpdateLocalState,
224 weak_ptr_factory_.GetWeakPtr(),
228 base::Owned(file_path)));
231 void CreateDirectoryOperation::CreateDirectoryRecursivelyAfterUpdateLocalState(
232 const std::string& resource_id,
233 const base::FilePath& remaining_path,
234 const FileOperationCallback& callback,
235 base::FilePath* file_path,
237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
238 DCHECK(!callback.is_null());
240 if (error != FILE_ERROR_OK) {
245 observer_->OnDirectoryChangedByOperation(file_path->DirName());
247 if (remaining_path.empty()) {
248 // All directories are created successfully.
249 callback.Run(FILE_ERROR_OK);
253 // Create descendant directories.
254 CreateDirectoryRecursively(resource_id, remaining_path, callback);
257 } // namespace file_system