async_file_util.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
1// Copyright 2014 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/fileapi/async_file_util.h" 6 7#include "base/callback.h" 8#include "base/files/file_path.h" 9#include "base/logging.h" 10#include "base/threading/sequenced_worker_pool.h" 11#include "chrome/browser/chromeos/drive/drive_integration_service.h" 12#include "chrome/browser/chromeos/drive/file_system_util.h" 13#include "chrome/browser/chromeos/drive/fileapi/fileapi_worker.h" 14#include "content/public/browser/browser_thread.h" 15#include "google_apis/drive/task_util.h" 16#include "webkit/browser/fileapi/file_system_operation_context.h" 17#include "webkit/browser/fileapi/file_system_url.h" 18#include "webkit/common/blob/shareable_file_reference.h" 19 20using content::BrowserThread; 21 22namespace google_apis { 23namespace internal { 24 25// Partial specialization of helper template from google_apis/drive/task_util.h 26// to enable google_apis::CreateRelayCallback to work with CreateOrOpenCallback. 27template<typename T2> 28struct ComposedCallback<void(base::File, T2)> { 29 static void Run( 30 const base::Callback<void(const base::Closure&)>& runner, 31 const base::Callback<void(base::File, T2)>& callback, 32 base::File arg1, T2 arg2) { 33 runner.Run(base::Bind(callback, Passed(&arg1), arg2)); 34 } 35}; 36 37} // namespace internal 38} // namespace google_apis 39 40namespace drive { 41namespace internal { 42namespace { 43 44// Posts fileapi_internal::RunFileSystemCallback to UI thread. 45// This function must be called on IO thread. 46// The |on_error_callback| will be called (on error case) on IO thread. 47void PostFileSystemCallback( 48 const fileapi_internal::FileSystemGetter& file_system_getter, 49 const base::Callback<void(FileSystemInterface*)>& function, 50 const base::Closure& on_error_callback) { 51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 52 53 BrowserThread::PostTask( 54 BrowserThread::UI, 55 FROM_HERE, 56 base::Bind(&fileapi_internal::RunFileSystemCallback, 57 file_system_getter, function, 58 on_error_callback.is_null() ? 59 base::Closure() : 60 base::Bind(&google_apis::RunTaskOnThread, 61 base::MessageLoopProxy::current(), 62 on_error_callback))); 63} 64 65// Runs CreateOrOpenFile callback based on the given |error| and |file|. 66void RunCreateOrOpenFileCallback( 67 const AsyncFileUtil::CreateOrOpenCallback& callback, 68 base::File file, 69 const base::Closure& close_callback_on_ui_thread) { 70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 71 72 // It is necessary to make a closure, which runs on file closing here. 73 // It will be provided as a FileSystem::OpenFileCallback's argument later. 74 // (crbug.com/259184). 75 callback.Run( 76 file.Pass(), 77 base::Bind(&google_apis::RunTaskOnThread, 78 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), 79 close_callback_on_ui_thread)); 80} 81 82// Runs CreateOrOpenFile when the error happens. 83void RunCreateOrOpenFileCallbackOnError( 84 const AsyncFileUtil::CreateOrOpenCallback& callback, 85 base::File::Error error) { 86 callback.Run(base::File(error), base::Closure()); 87} 88 89// Runs EnsureFileExistsCallback based on the given |error|. 90void RunEnsureFileExistsCallback( 91 const AsyncFileUtil::EnsureFileExistsCallback& callback, 92 base::File::Error error) { 93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 94 95 // Remember if the file is actually created or not. 96 bool created = (error == base::File::FILE_OK); 97 98 // File::FILE_ERROR_EXISTS is not an actual error here. 99 if (error == base::File::FILE_ERROR_EXISTS) 100 error = base::File::FILE_OK; 101 102 callback.Run(error, created); 103} 104 105// Runs |callback| with the arguments based on the given arguments. 106void RunCreateSnapshotFileCallback( 107 const AsyncFileUtil::CreateSnapshotFileCallback& callback, 108 base::File::Error error, 109 const base::File::Info& file_info, 110 const base::FilePath& local_path, 111 webkit_blob::ScopedFile::ScopeOutPolicy scope_out_policy) { 112 // ShareableFileReference is thread *unsafe* class. So it is necessary to 113 // create the instance (by invoking GetOrCreate) on IO thread, though 114 // most drive file system related operations run on UI thread. 115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 116 117 scoped_refptr<webkit_blob::ShareableFileReference> file_reference = 118 webkit_blob::ShareableFileReference::GetOrCreate(webkit_blob::ScopedFile( 119 local_path, 120 scope_out_policy, 121 BrowserThread::GetBlockingPool())); 122 callback.Run(error, file_info, local_path, file_reference); 123} 124 125} // namespace 126 127AsyncFileUtil::AsyncFileUtil() { 128} 129 130AsyncFileUtil::~AsyncFileUtil() { 131} 132 133void AsyncFileUtil::CreateOrOpen( 134 scoped_ptr<fileapi::FileSystemOperationContext> context, 135 const fileapi::FileSystemURL& url, 136 int file_flags, 137 const CreateOrOpenCallback& callback) { 138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 139 140 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 141 if (file_path.empty()) { 142 callback.Run(base::File(base::File::FILE_ERROR_NOT_FOUND), base::Closure()); 143 return; 144 } 145 146 const fileapi_internal::FileSystemGetter getter = 147 base::Bind(&fileapi_internal::GetFileSystemFromUrl, url); 148 PostFileSystemCallback( 149 getter, 150 base::Bind(&fileapi_internal::OpenFile, 151 file_path, file_flags, 152 google_apis::CreateRelayCallback( 153 base::Bind(&RunCreateOrOpenFileCallback, callback))), 154 base::Bind(&RunCreateOrOpenFileCallbackOnError, 155 callback, base::File::FILE_ERROR_FAILED)); 156} 157 158void AsyncFileUtil::EnsureFileExists( 159 scoped_ptr<fileapi::FileSystemOperationContext> context, 160 const fileapi::FileSystemURL& url, 161 const EnsureFileExistsCallback& callback) { 162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 163 164 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 165 if (file_path.empty()) { 166 callback.Run(base::File::FILE_ERROR_NOT_FOUND, false); 167 return; 168 } 169 170 PostFileSystemCallback( 171 base::Bind(&fileapi_internal::GetFileSystemFromUrl, url), 172 base::Bind(&fileapi_internal::CreateFile, 173 file_path, true /* is_exlusive */, 174 google_apis::CreateRelayCallback( 175 base::Bind(&RunEnsureFileExistsCallback, callback))), 176 base::Bind(callback, base::File::FILE_ERROR_FAILED, false)); 177} 178 179void AsyncFileUtil::CreateDirectory( 180 scoped_ptr<fileapi::FileSystemOperationContext> context, 181 const fileapi::FileSystemURL& url, 182 bool exclusive, 183 bool recursive, 184 const StatusCallback& callback) { 185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 186 187 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 188 if (file_path.empty()) { 189 callback.Run(base::File::FILE_ERROR_NOT_FOUND); 190 return; 191 } 192 193 PostFileSystemCallback( 194 base::Bind(&fileapi_internal::GetFileSystemFromUrl, url), 195 base::Bind(&fileapi_internal::CreateDirectory, 196 file_path, exclusive, recursive, 197 google_apis::CreateRelayCallback(callback)), 198 base::Bind(callback, base::File::FILE_ERROR_FAILED)); 199} 200 201void AsyncFileUtil::GetFileInfo( 202 scoped_ptr<fileapi::FileSystemOperationContext> context, 203 const fileapi::FileSystemURL& url, 204 const GetFileInfoCallback& callback) { 205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 206 207 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 208 if (file_path.empty()) { 209 callback.Run(base::File::FILE_ERROR_NOT_FOUND, base::File::Info()); 210 return; 211 } 212 213 PostFileSystemCallback( 214 base::Bind(&fileapi_internal::GetFileSystemFromUrl, url), 215 base::Bind(&fileapi_internal::GetFileInfo, 216 file_path, google_apis::CreateRelayCallback(callback)), 217 base::Bind(callback, base::File::FILE_ERROR_FAILED, 218 base::File::Info())); 219} 220 221void AsyncFileUtil::ReadDirectory( 222 scoped_ptr<fileapi::FileSystemOperationContext> context, 223 const fileapi::FileSystemURL& url, 224 const ReadDirectoryCallback& callback) { 225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 226 227 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 228 if (file_path.empty()) { 229 callback.Run(base::File::FILE_ERROR_NOT_FOUND, EntryList(), false); 230 return; 231 } 232 233 PostFileSystemCallback( 234 base::Bind(&fileapi_internal::GetFileSystemFromUrl, url), 235 base::Bind(&fileapi_internal::ReadDirectory, 236 file_path, google_apis::CreateRelayCallback(callback)), 237 base::Bind(callback, base::File::FILE_ERROR_FAILED, 238 EntryList(), false)); 239} 240 241void AsyncFileUtil::Touch( 242 scoped_ptr<fileapi::FileSystemOperationContext> context, 243 const fileapi::FileSystemURL& url, 244 const base::Time& last_access_time, 245 const base::Time& last_modified_time, 246 const StatusCallback& callback) { 247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 248 249 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 250 if (file_path.empty()) { 251 callback.Run(base::File::FILE_ERROR_NOT_FOUND); 252 return; 253 } 254 255 PostFileSystemCallback( 256 base::Bind(&fileapi_internal::GetFileSystemFromUrl, url), 257 base::Bind(&fileapi_internal::TouchFile, 258 file_path, last_access_time, last_modified_time, 259 google_apis::CreateRelayCallback(callback)), 260 base::Bind(callback, base::File::FILE_ERROR_FAILED)); 261} 262 263void AsyncFileUtil::Truncate( 264 scoped_ptr<fileapi::FileSystemOperationContext> context, 265 const fileapi::FileSystemURL& url, 266 int64 length, 267 const StatusCallback& callback) { 268 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 269 270 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 271 if (file_path.empty()) { 272 callback.Run(base::File::FILE_ERROR_NOT_FOUND); 273 return; 274 } 275 276 PostFileSystemCallback( 277 base::Bind(&fileapi_internal::GetFileSystemFromUrl, url), 278 base::Bind(&fileapi_internal::Truncate, 279 file_path, length, google_apis::CreateRelayCallback(callback)), 280 base::Bind(callback, base::File::FILE_ERROR_FAILED)); 281} 282 283void AsyncFileUtil::CopyFileLocal( 284 scoped_ptr<fileapi::FileSystemOperationContext> context, 285 const fileapi::FileSystemURL& src_url, 286 const fileapi::FileSystemURL& dest_url, 287 CopyOrMoveOption option, 288 const CopyFileProgressCallback& progress_callback, 289 const StatusCallback& callback) { 290 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 291 292 base::FilePath src_path = util::ExtractDrivePathFromFileSystemUrl(src_url); 293 base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url); 294 if (src_path.empty() || dest_path.empty()) { 295 callback.Run(base::File::FILE_ERROR_NOT_FOUND); 296 return; 297 } 298 299 // TODO(kinaba): crbug.com/339794. 300 // Assumption here is that |src_url| and |dest_url| are always from the same 301 // profile. This indeed holds as long as we mount different profiles onto 302 // different mount point. Hence, using GetFileSystemFromUrl(dest_url) is safe. 303 // This will change after we introduce cross-profile sharing etc., and we 304 // need to deal with files from different profiles here. 305 PostFileSystemCallback( 306 base::Bind(&fileapi_internal::GetFileSystemFromUrl, dest_url), 307 base::Bind( 308 &fileapi_internal::Copy, 309 src_path, dest_path, 310 option == fileapi::FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED, 311 google_apis::CreateRelayCallback(callback)), 312 base::Bind(callback, base::File::FILE_ERROR_FAILED)); 313} 314 315void AsyncFileUtil::MoveFileLocal( 316 scoped_ptr<fileapi::FileSystemOperationContext> context, 317 const fileapi::FileSystemURL& src_url, 318 const fileapi::FileSystemURL& dest_url, 319 CopyOrMoveOption option, 320 const StatusCallback& callback) { 321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 322 323 base::FilePath src_path = util::ExtractDrivePathFromFileSystemUrl(src_url); 324 base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url); 325 if (src_path.empty() || dest_path.empty()) { 326 callback.Run(base::File::FILE_ERROR_NOT_FOUND); 327 return; 328 } 329 330 // TODO(kinaba): see the comment in CopyFileLocal(). |src_url| and |dest_url| 331 // always return the same FileSystem by GetFileSystemFromUrl, but we need to 332 // change it in order to support cross-profile file sharing etc. 333 PostFileSystemCallback( 334 base::Bind(&fileapi_internal::GetFileSystemFromUrl, dest_url), 335 base::Bind(&fileapi_internal::Move, 336 src_path, dest_path, 337 google_apis::CreateRelayCallback(callback)), 338 base::Bind(callback, base::File::FILE_ERROR_FAILED)); 339} 340 341void AsyncFileUtil::CopyInForeignFile( 342 scoped_ptr<fileapi::FileSystemOperationContext> context, 343 const base::FilePath& src_file_path, 344 const fileapi::FileSystemURL& dest_url, 345 const StatusCallback& callback) { 346 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 347 348 base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url); 349 if (dest_path.empty()) { 350 callback.Run(base::File::FILE_ERROR_NOT_FOUND); 351 return; 352 } 353 354 PostFileSystemCallback( 355 base::Bind(&fileapi_internal::GetFileSystemFromUrl, dest_url), 356 base::Bind(&fileapi_internal::CopyInForeignFile, 357 src_file_path, dest_path, 358 google_apis::CreateRelayCallback(callback)), 359 base::Bind(callback, base::File::FILE_ERROR_FAILED)); 360} 361 362void AsyncFileUtil::DeleteFile( 363 scoped_ptr<fileapi::FileSystemOperationContext> context, 364 const fileapi::FileSystemURL& url, 365 const StatusCallback& callback) { 366 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 367 368 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 369 if (file_path.empty()) { 370 callback.Run(base::File::FILE_ERROR_NOT_FOUND); 371 return; 372 } 373 374 PostFileSystemCallback( 375 base::Bind(&fileapi_internal::GetFileSystemFromUrl, url), 376 base::Bind(&fileapi_internal::Remove, 377 file_path, false /* not recursive */, 378 google_apis::CreateRelayCallback(callback)), 379 base::Bind(callback, base::File::FILE_ERROR_FAILED)); 380} 381 382void AsyncFileUtil::DeleteDirectory( 383 scoped_ptr<fileapi::FileSystemOperationContext> context, 384 const fileapi::FileSystemURL& url, 385 const StatusCallback& callback) { 386 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 387 388 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 389 if (file_path.empty()) { 390 callback.Run(base::File::FILE_ERROR_NOT_FOUND); 391 return; 392 } 393 394 PostFileSystemCallback( 395 base::Bind(&fileapi_internal::GetFileSystemFromUrl, url), 396 base::Bind(&fileapi_internal::Remove, 397 file_path, false /* not recursive */, 398 google_apis::CreateRelayCallback(callback)), 399 base::Bind(callback, base::File::FILE_ERROR_FAILED)); 400} 401 402void AsyncFileUtil::DeleteRecursively( 403 scoped_ptr<fileapi::FileSystemOperationContext> context, 404 const fileapi::FileSystemURL& url, 405 const StatusCallback& callback) { 406 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 407 408 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 409 if (file_path.empty()) { 410 callback.Run(base::File::FILE_ERROR_NOT_FOUND); 411 return; 412 } 413 414 PostFileSystemCallback( 415 base::Bind(&fileapi_internal::GetFileSystemFromUrl, url), 416 base::Bind(&fileapi_internal::Remove, 417 file_path, true /* recursive */, 418 google_apis::CreateRelayCallback(callback)), 419 base::Bind(callback, base::File::FILE_ERROR_FAILED)); 420} 421 422void AsyncFileUtil::CreateSnapshotFile( 423 scoped_ptr<fileapi::FileSystemOperationContext> context, 424 const fileapi::FileSystemURL& url, 425 const CreateSnapshotFileCallback& callback) { 426 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 427 428 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 429 if (file_path.empty()) { 430 callback.Run(base::File::FILE_ERROR_NOT_FOUND, 431 base::File::Info(), 432 base::FilePath(), 433 scoped_refptr<webkit_blob::ShareableFileReference>()); 434 return; 435 } 436 437 PostFileSystemCallback( 438 base::Bind(&fileapi_internal::GetFileSystemFromUrl, url), 439 base::Bind(&fileapi_internal::CreateSnapshotFile, 440 file_path, 441 google_apis::CreateRelayCallback( 442 base::Bind(&RunCreateSnapshotFileCallback, callback))), 443 base::Bind(callback, 444 base::File::FILE_ERROR_FAILED, 445 base::File::Info(), 446 base::FilePath(), 447 scoped_refptr<webkit_blob::ShareableFileReference>())); 448} 449 450} // namespace internal 451} // namespace drive 452