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