create_file_operation.cc revision 3551c9c881056c480085172ff9840cab31610854
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 not set or "application/octet-stream", guess from the
65    // |file_path|. If it is still unsure, use octet-stream by default.
66    if ((mime_type->empty() || *mime_type == kMimeTypeOctetStream) &&
67        !net::GetMimeTypeFromFile(file_path, mime_type)) {
68      *mime_type = kMimeTypeOctetStream;
69    }
70  }
71
72  return error;
73}
74
75// Part of CreateFileOperation::CreateFile(), runs on |blocking_task_runner_|
76// of the operation, after server side file creation.
77FileError UpdateLocalStateForCreateFile(
78    internal::ResourceMetadata* metadata,
79    internal::FileCache* cache,
80    scoped_ptr<google_apis::ResourceEntry> resource_entry,
81    base::FilePath* file_path) {
82  DCHECK(metadata);
83  DCHECK(cache);
84  DCHECK(resource_entry);
85  DCHECK(file_path);
86
87  // Add the entry to the local resource metadata.
88  FileError error = FILE_ERROR_NOT_A_FILE;
89  ResourceEntry entry;
90  if (ConvertToResourceEntry(*resource_entry, &entry))
91    error = metadata->AddEntry(entry);
92
93  // Depending on timing, the metadata may have inserted via change list
94  // already. So, FILE_ERROR_EXISTS is not an error.
95  if (error == FILE_ERROR_EXISTS)
96    error = FILE_ERROR_OK;
97
98  if (error == FILE_ERROR_OK) {
99    // At this point, upload to the server is fully succeeded.
100    // Populate the |file_path| which will be used to notify the observer.
101    *file_path = metadata->GetFilePath(entry.resource_id());
102
103    // Also store an empty file to the cache.
104    // Here, failure is not a fatal error, so ignore the returned code.
105    FileError cache_store_error = FILE_ERROR_FAILED;
106    base::FilePath empty_file;
107    if (file_util::CreateTemporaryFile(&empty_file)) {
108      cache_store_error =  cache->Store(
109          entry.resource_id(),
110          entry.file_specific_info().md5(),
111          empty_file,
112          internal::FileCache::FILE_OPERATION_MOVE);
113    }
114    DLOG_IF(WARNING, cache_store_error != FILE_ERROR_OK)
115        << "Failed to store a cache file: "
116        << FileErrorToString(cache_store_error)
117        << ", resource_id: " << entry.resource_id();
118  }
119
120  return error;
121}
122
123}  // namespace
124
125CreateFileOperation::CreateFileOperation(
126    base::SequencedTaskRunner* blocking_task_runner,
127    OperationObserver* observer,
128    JobScheduler* scheduler,
129    internal::ResourceMetadata* metadata,
130    internal::FileCache* cache)
131    : blocking_task_runner_(blocking_task_runner),
132      observer_(observer),
133      scheduler_(scheduler),
134      metadata_(metadata),
135      cache_(cache),
136      weak_ptr_factory_(this) {
137  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
138}
139
140CreateFileOperation::~CreateFileOperation() {
141  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
142}
143
144void CreateFileOperation::CreateFile(const base::FilePath& file_path,
145                                     bool is_exclusive,
146                                     const std::string& mime_type,
147                                     const FileOperationCallback& callback) {
148  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
149  DCHECK(!callback.is_null());
150
151  std::string* parent_resource_id = new std::string;
152  std::string* determined_mime_type = new std::string(mime_type);
153  base::PostTaskAndReplyWithResult(
154      blocking_task_runner_.get(),
155      FROM_HERE,
156      base::Bind(&CheckPreConditionForCreateFile,
157                 metadata_,
158                 file_path,
159                 is_exclusive,
160                 parent_resource_id,
161                 determined_mime_type),
162      base::Bind(&CreateFileOperation::CreateFileAfterCheckPreCondition,
163                 weak_ptr_factory_.GetWeakPtr(),
164                 file_path,
165                 callback,
166                 base::Owned(parent_resource_id),
167                 base::Owned(determined_mime_type)));
168}
169
170void CreateFileOperation::CreateFileAfterCheckPreCondition(
171    const base::FilePath& file_path,
172    const FileOperationCallback& callback,
173    std::string* parent_resource_id,
174    std::string* mime_type,
175    FileError error) {
176  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
177  DCHECK(!callback.is_null());
178  DCHECK(parent_resource_id);
179  DCHECK(mime_type);
180
181  // If the file is found, or an error other than "not found" is found,
182  // runs callback and quit the operation.
183  if (error != FILE_ERROR_NOT_FOUND) {
184    callback.Run(error);
185    return;
186  }
187
188  scheduler_->CreateFile(
189      *parent_resource_id,
190      file_path,
191      file_path.BaseName().value(),
192      *mime_type,
193      ClientContext(USER_INITIATED),
194      base::Bind(&CreateFileOperation::CreateFileAfterUpload,
195                 weak_ptr_factory_.GetWeakPtr(),
196                 callback));
197}
198
199void CreateFileOperation::CreateFileAfterUpload(
200    const FileOperationCallback& callback,
201    google_apis::GDataErrorCode gdata_error,
202    scoped_ptr<google_apis::ResourceEntry> resource_entry) {
203  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
204  DCHECK(!callback.is_null());
205
206  FileError error = GDataToFileError(gdata_error);
207  if (error != FILE_ERROR_OK) {
208    callback.Run(error);
209    return;
210  }
211  DCHECK(resource_entry);
212
213  base::FilePath* file_path = new base::FilePath;
214  base::PostTaskAndReplyWithResult(
215      blocking_task_runner_.get(),
216      FROM_HERE,
217      base::Bind(&UpdateLocalStateForCreateFile,
218                 metadata_,
219                 cache_,
220                 base::Passed(&resource_entry),
221                 file_path),
222      base::Bind(&CreateFileOperation::CreateFileAfterUpdateLocalState,
223                 weak_ptr_factory_.GetWeakPtr(),
224                 callback,
225                 base::Owned(file_path)));
226}
227
228void CreateFileOperation::CreateFileAfterUpdateLocalState(
229    const FileOperationCallback& callback,
230    base::FilePath* file_path,
231    FileError error) {
232  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
233  DCHECK(!callback.is_null());
234  DCHECK(file_path);
235
236  // Notify observer if the file creation process is successfully done.
237  if (error == FILE_ERROR_OK)
238    observer_->OnDirectoryChangedByOperation(file_path->DirName());
239
240  callback.Run(error);
241}
242
243}  // namespace file_system
244}  // namespace drive
245