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.
5 #include "chrome/browser/chromeos/drive/file_system/create_file_operation.h"
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"
20 using content::BrowserThread;
23 namespace file_system {
27 const char kMimeTypeOctetStream[] = "application/octet-stream";
29 // Part of CreateFileOperation::CreateFile(), runs on |blocking_task_runner_|
30 // of the operation, before server-side file creation.
31 FileError CheckPreConditionForCreateFile(internal::ResourceMetadata* metadata,
32 const base::FilePath& file_path,
34 std::string* parent_resource_id,
35 std::string* mime_type) {
37 DCHECK(parent_resource_id);
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;
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.
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;
60 // In the request, parent_resource_id and mime_type are needed.
61 // Here, populate them.
62 *parent_resource_id = parent.resource_id();
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;
75 // Part of CreateFileOperation::CreateFile(), runs on |blocking_task_runner_|
76 // of the operation, after server side file creation.
77 FileError UpdateLocalStateForCreateFile(
78 internal::ResourceMetadata* metadata,
79 internal::FileCache* cache,
80 scoped_ptr<google_apis::ResourceEntry> resource_entry,
81 base::FilePath* file_path) {
84 DCHECK(resource_entry);
87 // Add the entry to the local resource metadata.
89 std::string parent_resource_id;
90 if (!ConvertToResourceEntry(*resource_entry, &entry, &parent_resource_id))
91 return FILE_ERROR_NOT_A_FILE;
93 std::string parent_local_id;
94 FileError error = metadata->GetIdByResourceId(parent_resource_id,
96 if (error != FILE_ERROR_OK)
98 entry.set_parent_local_id(parent_local_id);
100 std::string local_id;
101 error = metadata->AddEntry(entry, &local_id);
103 // Depending on timing, the metadata may have inserted via change list
104 // already. So, FILE_ERROR_EXISTS is not an error.
105 if (error == FILE_ERROR_EXISTS)
106 error = metadata->GetIdByResourceId(entry.resource_id(), &local_id);
108 if (error == FILE_ERROR_OK) {
109 // At this point, upload to the server is fully succeeded.
110 // Populate the |file_path| which will be used to notify the observer.
111 *file_path = metadata->GetFilePath(local_id);
113 // Also store an empty file to the cache.
114 // Here, failure is not a fatal error, so ignore the returned code.
115 FileError cache_store_error = FILE_ERROR_FAILED;
116 base::FilePath empty_file;
117 if (file_util::CreateTemporaryFile(&empty_file)) {
118 cache_store_error = cache->Store(
120 entry.file_specific_info().md5(),
122 internal::FileCache::FILE_OPERATION_MOVE);
124 DLOG_IF(WARNING, cache_store_error != FILE_ERROR_OK)
125 << "Failed to store a cache file: "
126 << FileErrorToString(cache_store_error)
127 << ", local_id: " << local_id;
135 CreateFileOperation::CreateFileOperation(
136 base::SequencedTaskRunner* blocking_task_runner,
137 OperationObserver* observer,
138 JobScheduler* scheduler,
139 internal::ResourceMetadata* metadata,
140 internal::FileCache* cache)
141 : blocking_task_runner_(blocking_task_runner),
143 scheduler_(scheduler),
146 weak_ptr_factory_(this) {
147 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
150 CreateFileOperation::~CreateFileOperation() {
151 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
154 void CreateFileOperation::CreateFile(const base::FilePath& file_path,
156 const std::string& mime_type,
157 const FileOperationCallback& callback) {
158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
159 DCHECK(!callback.is_null());
161 std::string* parent_resource_id = new std::string;
162 std::string* determined_mime_type = new std::string(mime_type);
163 base::PostTaskAndReplyWithResult(
164 blocking_task_runner_.get(),
166 base::Bind(&CheckPreConditionForCreateFile,
171 determined_mime_type),
172 base::Bind(&CreateFileOperation::CreateFileAfterCheckPreCondition,
173 weak_ptr_factory_.GetWeakPtr(),
176 base::Owned(parent_resource_id),
177 base::Owned(determined_mime_type)));
180 void CreateFileOperation::CreateFileAfterCheckPreCondition(
181 const base::FilePath& file_path,
182 const FileOperationCallback& callback,
183 std::string* parent_resource_id,
184 std::string* mime_type,
186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
187 DCHECK(!callback.is_null());
188 DCHECK(parent_resource_id);
191 // If the file is found, or an error other than "not found" is found,
192 // runs callback and quit the operation.
193 if (error != FILE_ERROR_NOT_FOUND) {
198 scheduler_->CreateFile(
201 file_path.BaseName().value(),
203 ClientContext(USER_INITIATED),
204 base::Bind(&CreateFileOperation::CreateFileAfterUpload,
205 weak_ptr_factory_.GetWeakPtr(),
209 void CreateFileOperation::CreateFileAfterUpload(
210 const FileOperationCallback& callback,
211 google_apis::GDataErrorCode gdata_error,
212 scoped_ptr<google_apis::ResourceEntry> resource_entry) {
213 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
214 DCHECK(!callback.is_null());
216 FileError error = GDataToFileError(gdata_error);
217 if (error != FILE_ERROR_OK) {
221 DCHECK(resource_entry);
223 base::FilePath* file_path = new base::FilePath;
224 base::PostTaskAndReplyWithResult(
225 blocking_task_runner_.get(),
227 base::Bind(&UpdateLocalStateForCreateFile,
230 base::Passed(&resource_entry),
232 base::Bind(&CreateFileOperation::CreateFileAfterUpdateLocalState,
233 weak_ptr_factory_.GetWeakPtr(),
235 base::Owned(file_path)));
238 void CreateFileOperation::CreateFileAfterUpdateLocalState(
239 const FileOperationCallback& callback,
240 base::FilePath* file_path,
242 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
243 DCHECK(!callback.is_null());
246 // Notify observer if the file creation process is successfully done.
247 if (error == FILE_ERROR_OK)
248 observer_->OnDirectoryChangedByOperation(file_path->DirName());
253 } // namespace file_system