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 parent_resource_id
.empty())
92 return FILE_ERROR_NOT_A_FILE
;
94 std::string parent_local_id
;
95 FileError error
= metadata
->GetIdByResourceId(parent_resource_id
,
97 if (error
!= FILE_ERROR_OK
)
99 entry
.set_parent_local_id(parent_local_id
);
101 std::string local_id
;
102 error
= metadata
->AddEntry(entry
, &local_id
);
104 // Depending on timing, the metadata may have inserted via change list
105 // already. So, FILE_ERROR_EXISTS is not an error.
106 if (error
== FILE_ERROR_EXISTS
)
107 error
= metadata
->GetIdByResourceId(entry
.resource_id(), &local_id
);
109 if (error
== FILE_ERROR_OK
) {
110 // At this point, upload to the server is fully succeeded.
111 // Populate the |file_path| which will be used to notify the observer.
112 *file_path
= metadata
->GetFilePath(local_id
);
114 // Also store an empty file to the cache.
115 // Here, failure is not a fatal error, so ignore the returned code.
116 FileError cache_store_error
= FILE_ERROR_FAILED
;
117 base::FilePath empty_file
;
118 if (base::CreateTemporaryFile(&empty_file
)) {
119 cache_store_error
= cache
->Store(
121 entry
.file_specific_info().md5(),
123 internal::FileCache::FILE_OPERATION_MOVE
);
125 DLOG_IF(WARNING
, cache_store_error
!= FILE_ERROR_OK
)
126 << "Failed to store a cache file: "
127 << FileErrorToString(cache_store_error
)
128 << ", local_id: " << local_id
;
136 CreateFileOperation::CreateFileOperation(
137 base::SequencedTaskRunner
* blocking_task_runner
,
138 OperationObserver
* observer
,
139 JobScheduler
* scheduler
,
140 internal::ResourceMetadata
* metadata
,
141 internal::FileCache
* cache
)
142 : blocking_task_runner_(blocking_task_runner
),
144 scheduler_(scheduler
),
147 weak_ptr_factory_(this) {
148 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
151 CreateFileOperation::~CreateFileOperation() {
152 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
155 void CreateFileOperation::CreateFile(const base::FilePath
& file_path
,
157 const std::string
& mime_type
,
158 const FileOperationCallback
& callback
) {
159 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
160 DCHECK(!callback
.is_null());
162 std::string
* parent_resource_id
= new std::string
;
163 std::string
* determined_mime_type
= new std::string(mime_type
);
164 base::PostTaskAndReplyWithResult(
165 blocking_task_runner_
.get(),
167 base::Bind(&CheckPreConditionForCreateFile
,
172 determined_mime_type
),
173 base::Bind(&CreateFileOperation::CreateFileAfterCheckPreCondition
,
174 weak_ptr_factory_
.GetWeakPtr(),
177 base::Owned(parent_resource_id
),
178 base::Owned(determined_mime_type
)));
181 void CreateFileOperation::CreateFileAfterCheckPreCondition(
182 const base::FilePath
& file_path
,
183 const FileOperationCallback
& callback
,
184 std::string
* parent_resource_id
,
185 std::string
* mime_type
,
187 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
188 DCHECK(!callback
.is_null());
189 DCHECK(parent_resource_id
);
192 // If the file is found, or an error other than "not found" is found,
193 // runs callback and quit the operation.
194 if (error
!= FILE_ERROR_NOT_FOUND
) {
199 scheduler_
->CreateFile(
202 file_path
.BaseName().value(),
204 ClientContext(USER_INITIATED
),
205 base::Bind(&CreateFileOperation::CreateFileAfterUpload
,
206 weak_ptr_factory_
.GetWeakPtr(),
210 void CreateFileOperation::CreateFileAfterUpload(
211 const FileOperationCallback
& callback
,
212 google_apis::GDataErrorCode gdata_error
,
213 scoped_ptr
<google_apis::ResourceEntry
> resource_entry
) {
214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
215 DCHECK(!callback
.is_null());
217 FileError error
= GDataToFileError(gdata_error
);
218 if (error
!= FILE_ERROR_OK
) {
222 DCHECK(resource_entry
);
224 base::FilePath
* file_path
= new base::FilePath
;
225 base::PostTaskAndReplyWithResult(
226 blocking_task_runner_
.get(),
228 base::Bind(&UpdateLocalStateForCreateFile
,
231 base::Passed(&resource_entry
),
233 base::Bind(&CreateFileOperation::CreateFileAfterUpdateLocalState
,
234 weak_ptr_factory_
.GetWeakPtr(),
236 base::Owned(file_path
)));
239 void CreateFileOperation::CreateFileAfterUpdateLocalState(
240 const FileOperationCallback
& callback
,
241 base::FilePath
* file_path
,
243 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
244 DCHECK(!callback
.is_null());
247 // Notify observer if the file creation process is successfully done.
248 if (error
== FILE_ERROR_OK
)
249 observer_
->OnDirectoryChangedByOperation(file_path
->DirName());
254 } // namespace file_system