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