create_directory_operation.cc revision a36e5920737c6adbddd3e43b760e5de8431db6e0
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    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
47CreateDirectoryOperation::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
60CreateDirectoryOperation::~CreateDirectoryOperation() {
61  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
62}
63
64void 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
91base::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
122void 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
159void 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
182void 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
224void 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