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