create_file_operation.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
1// Copyright 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_file_operation.h"
6
7#include <string>
8
9#include "base/file_util.h"
10#include "chrome/browser/chromeos/drive/drive.pb.h"
11#include "chrome/browser/chromeos/drive/file_cache.h"
12#include "chrome/browser/chromeos/drive/file_system/operation_observer.h"
13#include "chrome/browser/chromeos/drive/file_system_util.h"
14#include "chrome/browser/chromeos/drive/job_scheduler.h"
15#include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
16#include "chrome/browser/chromeos/drive/resource_metadata.h"
17#include "content/public/browser/browser_thread.h"
18#include "net/base/mime_util.h"
19
20using content::BrowserThread;
21
22namespace drive {
23namespace file_system {
24
25namespace {
26
27const char kMimeTypeOctetStream[] = "application/octet-stream";
28
29// Part of CreateFileOperation::CreateFile(), runs on |blocking_task_runner_|
30// of the operation, before server-side file creation.
31FileError CheckPreConditionForCreateFile(internal::ResourceMetadata* metadata,
32                                         const base::FilePath& file_path,
33                                         bool is_exclusive,
34                                         std::string* parent_resource_id,
35                                         std::string* mime_type) {
36  DCHECK(metadata);
37  DCHECK(parent_resource_id);
38  DCHECK(mime_type);
39
40  ResourceEntry entry;
41  FileError error = metadata->GetResourceEntryByPath(file_path, &entry);
42  if (error == FILE_ERROR_OK) {
43    // Error if an exclusive mode is requested, or the entry is not a file.
44    return (is_exclusive ||
45            entry.file_info().is_directory() ||
46            entry.file_specific_info().is_hosted_document()) ?
47        FILE_ERROR_EXISTS : FILE_ERROR_OK;
48  }
49
50  // If the file is not found, an actual request to create a new file will be
51  // sent to the server.
52  if (error == FILE_ERROR_NOT_FOUND) {
53    // If parent path is not a directory, it is an error.
54    ResourceEntry parent;
55    if (metadata->GetResourceEntryByPath(
56            file_path.DirName(), &parent) != FILE_ERROR_OK ||
57        !parent.file_info().is_directory())
58      return FILE_ERROR_NOT_A_DIRECTORY;
59
60    // In the request, parent_resource_id and mime_type are needed.
61    // Here, populate them.
62    *parent_resource_id = parent.resource_id();
63
64    // If mime type is unsure, use octet stream by default.
65    if (!net::GetMimeTypeFromFile(file_path, mime_type))
66      *mime_type = kMimeTypeOctetStream;
67  }
68
69  return error;
70}
71
72// Part of CreateFileOperation::CreateFile(), runs on |blocking_task_runner_|
73// of the operation, after server side file creation.
74FileError UpdateLocalStateForCreateFile(
75    internal::ResourceMetadata* metadata,
76    internal::FileCache* cache,
77    scoped_ptr<google_apis::ResourceEntry> resource_entry,
78    base::FilePath* file_path) {
79  DCHECK(metadata);
80  DCHECK(cache);
81  DCHECK(resource_entry);
82  DCHECK(file_path);
83
84  // Add the entry to the local resource metadata.
85  ResourceEntry entry = ConvertToResourceEntry(*resource_entry);
86  FileError error = metadata->AddEntry(entry);
87
88  // Depending on timing, the metadata may have inserted via change list feed
89  // already. So, FILE_ERROR_EXISTS is not an error.
90  if (error == FILE_ERROR_EXISTS)
91    error = FILE_ERROR_OK;
92
93  if (error == FILE_ERROR_OK) {
94    // At this point, upload to the server is fully succeeded.
95    // Populate the |file_path| which will be used to notify the observer.
96    *file_path = metadata->GetFilePath(entry.resource_id());
97
98    // Also store an empty file to the cache.
99    // Here, failure is not a fatal error, so ignore the returned code.
100    FileError cache_store_error = FILE_ERROR_FAILED;
101    base::FilePath empty_file;
102    if (file_util::CreateTemporaryFile(&empty_file)) {
103      cache_store_error =  cache->Store(
104          entry.resource_id(),
105          entry.file_specific_info().file_md5(),
106          empty_file,
107          internal::FileCache::FILE_OPERATION_MOVE);
108    }
109    DLOG_IF(WARNING, cache_store_error != FILE_ERROR_OK)
110        << "Failed to store a cache file: "
111        << FileErrorToString(cache_store_error)
112        << ", resource_id: " << entry.resource_id();
113  }
114
115  return error;
116}
117
118}  // namespace
119
120CreateFileOperation::CreateFileOperation(
121    base::SequencedTaskRunner* blocking_task_runner,
122    OperationObserver* observer,
123    JobScheduler* scheduler,
124    internal::ResourceMetadata* metadata,
125    internal::FileCache* cache)
126    : blocking_task_runner_(blocking_task_runner),
127      observer_(observer),
128      scheduler_(scheduler),
129      metadata_(metadata),
130      cache_(cache),
131      weak_ptr_factory_(this) {
132  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
133}
134
135CreateFileOperation::~CreateFileOperation() {
136  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
137}
138
139void CreateFileOperation::CreateFile(const base::FilePath& file_path,
140                                     bool is_exclusive,
141                                     const FileOperationCallback& callback) {
142  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
143  DCHECK(!callback.is_null());
144
145  std::string* parent_resource_id = new std::string;
146  std::string* mime_type = new std::string;
147  base::PostTaskAndReplyWithResult(
148      blocking_task_runner_,
149      FROM_HERE,
150      base::Bind(&CheckPreConditionForCreateFile,
151                 metadata_, file_path, is_exclusive,
152                 parent_resource_id, mime_type),
153      base::Bind(&CreateFileOperation::CreateFileAfterCheckPreCondition,
154                 weak_ptr_factory_.GetWeakPtr(),
155                 file_path, callback,
156                 base::Owned(parent_resource_id), base::Owned(mime_type)));
157}
158
159void CreateFileOperation::CreateFileAfterCheckPreCondition(
160    const base::FilePath& file_path,
161    const FileOperationCallback& callback,
162    std::string* parent_resource_id,
163    std::string* mime_type,
164    FileError error) {
165  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
166  DCHECK(!callback.is_null());
167  DCHECK(parent_resource_id);
168  DCHECK(mime_type);
169
170  // If the file is found, or an error other than "not found" is found,
171  // runs callback and quit the operation.
172  if (error != FILE_ERROR_NOT_FOUND) {
173    callback.Run(error);
174    return;
175  }
176
177  scheduler_->CreateFile(
178      *parent_resource_id,
179      file_path,
180      file_path.BaseName().value(),
181      *mime_type,
182      ClientContext(USER_INITIATED),
183      base::Bind(&CreateFileOperation::CreateFileAfterUpload,
184                 weak_ptr_factory_.GetWeakPtr(),
185                 callback));
186}
187
188void CreateFileOperation::CreateFileAfterUpload(
189    const FileOperationCallback& callback,
190    google_apis::GDataErrorCode gdata_error,
191    scoped_ptr<google_apis::ResourceEntry> resource_entry) {
192  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
193  DCHECK(!callback.is_null());
194
195  FileError error = util::GDataToFileError(gdata_error);
196  if (error != FILE_ERROR_OK) {
197    callback.Run(error);
198    return;
199  }
200  DCHECK(resource_entry);
201
202  base::FilePath* file_path = new base::FilePath;
203  base::PostTaskAndReplyWithResult(
204      blocking_task_runner_,
205      FROM_HERE,
206      base::Bind(&UpdateLocalStateForCreateFile,
207                 metadata_, cache_, base::Passed(&resource_entry), file_path),
208      base::Bind(&CreateFileOperation::CreateFileAfterUpdateLocalState,
209                 weak_ptr_factory_.GetWeakPtr(),
210                 callback, base::Owned(file_path)));
211}
212
213void CreateFileOperation::CreateFileAfterUpdateLocalState(
214    const FileOperationCallback& callback,
215    base::FilePath* file_path,
216    FileError error) {
217  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
218  DCHECK(!callback.is_null());
219  DCHECK(file_path);
220
221  // Notify observer if the file creation process is successfully done.
222  if (error == FILE_ERROR_OK)
223    observer_->OnDirectoryChangedByOperation(file_path->DirName());
224
225  callback.Run(error);
226}
227
228}  // namespace file_system
229}  // namespace drive
230