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 "content/public/browser/browser_thread.h"
13 #include "google_apis/drive/gdata_errorcode.h"
14 #include "google_apis/drive/gdata_wapi_parser.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 parent_resource_id
.empty())
37 return FILE_ERROR_NOT_A_FILE
;
39 std::string parent_local_id
;
40 FileError result
= metadata
->GetIdByResourceId(parent_resource_id
,
42 if (result
!= FILE_ERROR_OK
)
44 entry
.set_parent_local_id(parent_local_id
);
47 result
= metadata
->AddEntry(entry
, &local_id
);
48 // Depending on timing, a metadata may be updated by change list already.
49 // So, FILE_ERROR_EXISTS is not an error.
50 if (result
== FILE_ERROR_EXISTS
)
51 result
= metadata
->GetIdByResourceId(entry
.resource_id(), &local_id
);
53 if (result
== FILE_ERROR_OK
)
54 *file_path
= metadata
->GetFilePath(local_id
);
61 CreateDirectoryOperation::CreateDirectoryOperation(
62 base::SequencedTaskRunner
* blocking_task_runner
,
63 OperationObserver
* observer
,
64 JobScheduler
* scheduler
,
65 internal::ResourceMetadata
* metadata
)
66 : blocking_task_runner_(blocking_task_runner
),
68 scheduler_(scheduler
),
70 weak_ptr_factory_(this) {
71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
74 CreateDirectoryOperation::~CreateDirectoryOperation() {
75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
78 void CreateDirectoryOperation::CreateDirectory(
79 const base::FilePath
& directory_path
,
82 const FileOperationCallback
& callback
) {
83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
84 DCHECK(!callback
.is_null());
86 ResourceEntry
* entry
= new ResourceEntry
;
87 base::PostTaskAndReplyWithResult(
88 blocking_task_runner_
.get(),
90 base::Bind(&CreateDirectoryOperation::GetExistingDeepestDirectory
,
94 base::Bind(&CreateDirectoryOperation::
95 CreateDirectoryAfterGetExistingDeepestDirectory
,
96 weak_ptr_factory_
.GetWeakPtr(),
101 base::Owned(entry
)));
105 base::FilePath
CreateDirectoryOperation::GetExistingDeepestDirectory(
106 internal::ResourceMetadata
* metadata
,
107 const base::FilePath
& directory_path
,
108 ResourceEntry
* entry
) {
112 std::vector
<base::FilePath::StringType
> components
;
113 directory_path
.GetComponents(&components
);
115 if (components
.empty() || components
[0] != util::kDriveGrandRootDirName
)
116 return base::FilePath();
118 base::FilePath
result_path(components
[0]);
119 std::string local_id
= util::kDriveGrandRootLocalId
;
120 for (size_t i
= 1; i
< components
.size(); ++i
) {
121 std::string child_local_id
= metadata
->GetChildId(local_id
, components
[i
]);
122 if (child_local_id
.empty())
124 result_path
= result_path
.Append(components
[i
]);
125 local_id
= child_local_id
;
128 FileError error
= metadata
->GetResourceEntryById(local_id
, entry
);
129 DCHECK_EQ(FILE_ERROR_OK
, error
);
131 if (!entry
->file_info().is_directory())
132 return base::FilePath();
137 void CreateDirectoryOperation::CreateDirectoryAfterGetExistingDeepestDirectory(
138 const base::FilePath
& directory_path
,
141 const FileOperationCallback
& callback
,
142 ResourceEntry
* existing_deepest_directory_entry
,
143 const base::FilePath
& existing_deepest_directory_path
) {
144 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
145 DCHECK(!callback
.is_null());
146 DCHECK(existing_deepest_directory_entry
);
148 if (existing_deepest_directory_path
.empty()) {
149 callback
.Run(FILE_ERROR_NOT_FOUND
);
153 if (directory_path
== existing_deepest_directory_path
) {
154 callback
.Run(is_exclusive
? FILE_ERROR_EXISTS
: FILE_ERROR_OK
);
158 // If it is not recursive creation, the found directory must be the direct
159 // parent of |directory_path| to ensure creating exact one directory.
161 existing_deepest_directory_path
!= directory_path
.DirName()) {
162 callback
.Run(FILE_ERROR_NOT_FOUND
);
166 // Create directories under the found directory.
167 base::FilePath remaining_path
;
168 existing_deepest_directory_path
.AppendRelativePath(
169 directory_path
, &remaining_path
);
170 CreateDirectoryRecursively(existing_deepest_directory_entry
->resource_id(),
171 remaining_path
, callback
);
174 void CreateDirectoryOperation::CreateDirectoryRecursively(
175 const std::string
& parent_resource_id
,
176 const base::FilePath
& relative_file_path
,
177 const FileOperationCallback
& callback
) {
178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
179 DCHECK(!callback
.is_null());
181 // Split the first component and remaining ones of |relative_file_path|.
182 std::vector
<base::FilePath::StringType
> components
;
183 relative_file_path
.GetComponents(&components
);
184 DCHECK(!components
.empty());
185 base::FilePath
title(components
[0]);
186 base::FilePath remaining_path
;
187 title
.AppendRelativePath(relative_file_path
, &remaining_path
);
189 scheduler_
->AddNewDirectory(
191 title
.AsUTF8Unsafe(),
192 base::Bind(&CreateDirectoryOperation
193 ::CreateDirectoryRecursivelyAfterAddNewDirectory
,
194 weak_ptr_factory_
.GetWeakPtr(), remaining_path
, callback
));
197 void CreateDirectoryOperation::CreateDirectoryRecursivelyAfterAddNewDirectory(
198 const base::FilePath
& remaining_path
,
199 const FileOperationCallback
& callback
,
200 google_apis::GDataErrorCode gdata_error
,
201 scoped_ptr
<google_apis::ResourceEntry
> resource_entry
) {
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
203 DCHECK(!callback
.is_null());
205 FileError error
= GDataToFileError(gdata_error
);
206 if (error
!= FILE_ERROR_OK
) {
210 DCHECK(resource_entry
);
211 const std::string
& resource_id
= resource_entry
->resource_id();
213 // Note that the created directory may be renamed inside
214 // ResourceMetadata::AddEntry due to name confliction.
215 // What we actually need here is the new created path (not the path we try
217 base::FilePath
* file_path
= new base::FilePath
;
218 base::PostTaskAndReplyWithResult(
219 blocking_task_runner_
.get(),
221 base::Bind(&UpdateLocalStateForCreateDirectoryRecursively
,
223 base::Passed(&resource_entry
),
225 base::Bind(&CreateDirectoryOperation::
226 CreateDirectoryRecursivelyAfterUpdateLocalState
,
227 weak_ptr_factory_
.GetWeakPtr(),
231 base::Owned(file_path
)));
234 void CreateDirectoryOperation::CreateDirectoryRecursivelyAfterUpdateLocalState(
235 const std::string
& resource_id
,
236 const base::FilePath
& remaining_path
,
237 const FileOperationCallback
& callback
,
238 base::FilePath
* file_path
,
240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
241 DCHECK(!callback
.is_null());
243 if (error
!= FILE_ERROR_OK
) {
248 observer_
->OnDirectoryChangedByOperation(file_path
->DirName());
250 if (remaining_path
.empty()) {
251 // All directories are created successfully.
252 callback
.Run(FILE_ERROR_OK
);
256 // Create descendant directories.
257 CreateDirectoryRecursively(resource_id
, remaining_path
, callback
);
260 } // namespace file_system