async_file_util.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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(&fileapi_internal::Move, 331 src_path, dest_path, 332 google_apis::CreateRelayCallback(callback)), 333 base::Bind(callback, base::File::FILE_ERROR_FAILED)); 334} 335 336void AsyncFileUtil::CopyInForeignFile( 337 scoped_ptr<fileapi::FileSystemOperationContext> context, 338 const base::FilePath& src_file_path, 339 const fileapi::FileSystemURL& dest_url, 340 const StatusCallback& callback) { 341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 342 343 base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url); 344 if (dest_path.empty()) { 345 callback.Run(base::File::FILE_ERROR_NOT_FOUND); 346 return; 347 } 348 349 PostFileSystemCallback( 350 base::Bind(&fileapi_internal::GetFileSystemFromUrl, dest_url), 351 base::Bind(&fileapi_internal::CopyInForeignFile, 352 src_file_path, dest_path, 353 google_apis::CreateRelayCallback(callback)), 354 base::Bind(callback, base::File::FILE_ERROR_FAILED)); 355} 356 357void AsyncFileUtil::DeleteFile( 358 scoped_ptr<fileapi::FileSystemOperationContext> context, 359 const fileapi::FileSystemURL& url, 360 const StatusCallback& callback) { 361 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 362 363 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 364 if (file_path.empty()) { 365 callback.Run(base::File::FILE_ERROR_NOT_FOUND); 366 return; 367 } 368 369 PostFileSystemCallback( 370 base::Bind(&fileapi_internal::GetFileSystemFromUrl, url), 371 base::Bind(&fileapi_internal::Remove, 372 file_path, false /* not recursive */, 373 google_apis::CreateRelayCallback(callback)), 374 base::Bind(callback, base::File::FILE_ERROR_FAILED)); 375} 376 377void AsyncFileUtil::DeleteDirectory( 378 scoped_ptr<fileapi::FileSystemOperationContext> context, 379 const fileapi::FileSystemURL& url, 380 const StatusCallback& callback) { 381 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 382 383 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 384 if (file_path.empty()) { 385 callback.Run(base::File::FILE_ERROR_NOT_FOUND); 386 return; 387 } 388 389 PostFileSystemCallback( 390 base::Bind(&fileapi_internal::GetFileSystemFromUrl, url), 391 base::Bind(&fileapi_internal::Remove, 392 file_path, false /* not recursive */, 393 google_apis::CreateRelayCallback(callback)), 394 base::Bind(callback, base::File::FILE_ERROR_FAILED)); 395} 396 397void AsyncFileUtil::DeleteRecursively( 398 scoped_ptr<fileapi::FileSystemOperationContext> context, 399 const fileapi::FileSystemURL& url, 400 const StatusCallback& callback) { 401 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 402 403 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 404 if (file_path.empty()) { 405 callback.Run(base::File::FILE_ERROR_NOT_FOUND); 406 return; 407 } 408 409 PostFileSystemCallback( 410 base::Bind(&fileapi_internal::GetFileSystemFromUrl, url), 411 base::Bind(&fileapi_internal::Remove, 412 file_path, true /* recursive */, 413 google_apis::CreateRelayCallback(callback)), 414 base::Bind(callback, base::File::FILE_ERROR_FAILED)); 415} 416 417void AsyncFileUtil::CreateSnapshotFile( 418 scoped_ptr<fileapi::FileSystemOperationContext> context, 419 const fileapi::FileSystemURL& url, 420 const CreateSnapshotFileCallback& callback) { 421 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 422 423 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 424 if (file_path.empty()) { 425 callback.Run(base::File::FILE_ERROR_NOT_FOUND, 426 base::File::Info(), 427 base::FilePath(), 428 scoped_refptr<webkit_blob::ShareableFileReference>()); 429 return; 430 } 431 432 PostFileSystemCallback( 433 base::Bind(&fileapi_internal::GetFileSystemFromUrl, url), 434 base::Bind(&fileapi_internal::CreateSnapshotFile, 435 file_path, 436 google_apis::CreateRelayCallback( 437 base::Bind(&RunCreateSnapshotFileCallback, callback))), 438 base::Bind(callback, 439 base::File::FILE_ERROR_FAILED, 440 base::File::Info(), 441 base::FilePath(), 442 scoped_refptr<webkit_blob::ShareableFileReference>())); 443} 444 445} // namespace internal 446} // namespace drive 447