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. 4 5#include "chrome/browser/chromeos/drive/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/file_system_util.h" 13#include "chrome/browser/chromeos/drive/fileapi_worker.h" 14#include "chrome/browser/google_apis/task_util.h" 15#include "content/public/browser/browser_thread.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 drive { 23namespace internal { 24namespace { 25 26// Posts fileapi_internal::RunFileSystemCallback to UI thread. 27// This function must be called on IO thread. 28// The |on_error_callback| will be called (on error case) on IO thread. 29void PostFileSystemCallback( 30 const fileapi_internal::FileSystemGetter& file_system_getter, 31 const base::Callback<void(FileSystemInterface*)>& function, 32 const base::Closure& on_error_callback) { 33 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 34 35 BrowserThread::PostTask( 36 BrowserThread::UI, 37 FROM_HERE, 38 base::Bind(&fileapi_internal::RunFileSystemCallback, 39 file_system_getter, function, 40 on_error_callback.is_null() ? 41 base::Closure() : 42 base::Bind(&google_apis::RunTaskOnThread, 43 base::MessageLoopProxy::current(), 44 on_error_callback))); 45} 46 47// Runs CreateOrOpenFile callback based on the given |error| and |file|. 48void RunCreateOrOpenFileCallback( 49 const AsyncFileUtil::FileSystemGetter& file_system_getter, 50 const base::FilePath& file_path, 51 const AsyncFileUtil::CreateOrOpenCallback& callback, 52 base::PlatformFileError error, 53 base::PlatformFile file, 54 const base::Closure& close_callback_on_ui_thread) { 55 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 56 57 // It is necessary to make a closure, which runs on file closing here. 58 // It will be provided as a FileSystem::OpenFileCallback's argument later. 59 // (crbug.com/259184). 60 callback.Run( 61 error, base::PassPlatformFile(&file), 62 base::Bind(&google_apis::RunTaskOnThread, 63 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), 64 close_callback_on_ui_thread)); 65} 66 67// Runs CreateOrOpenFile when the error happens. 68void RunCreateOrOpenFileCallbackOnError( 69 const AsyncFileUtil::CreateOrOpenCallback& callback, 70 base::PlatformFileError error) { 71 // Because the |callback| takes PassPlatformFile as its argument, and 72 // it is necessary to guarantee the pointer passed to PassPlatformFile is 73 // alive during the |callback| invocation, here we prepare a thin adapter 74 // to have PlatformFile on stack frame. 75 base::PlatformFile file = base::kInvalidPlatformFileValue; 76 callback.Run(error, base::PassPlatformFile(&file), base::Closure()); 77} 78 79// Runs EnsureFileExistsCallback based on the given |error|. 80void RunEnsureFileExistsCallback( 81 const AsyncFileUtil::EnsureFileExistsCallback& callback, 82 base::PlatformFileError error) { 83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 84 85 // Remember if the file is actually created or not. 86 bool created = (error == base::PLATFORM_FILE_OK); 87 88 // PLATFORM_FILE_ERROR_EXISTS is not an actual error here. 89 if (error == base::PLATFORM_FILE_ERROR_EXISTS) 90 error = base::PLATFORM_FILE_OK; 91 92 callback.Run(error, created); 93} 94 95 96// Runs |callback| with the arguments based on the given arguments. 97void RunCreateSnapshotFileCallback( 98 const AsyncFileUtil::CreateSnapshotFileCallback& callback, 99 base::PlatformFileError error, 100 const base::PlatformFileInfo& 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(const FileSystemGetter& file_system_getter) 119 : file_system_getter_(file_system_getter) { 120} 121 122AsyncFileUtil::~AsyncFileUtil() { 123} 124 125void AsyncFileUtil::CreateOrOpen( 126 scoped_ptr<fileapi::FileSystemOperationContext> context, 127 const fileapi::FileSystemURL& url, 128 int file_flags, 129 const CreateOrOpenCallback& callback) { 130 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 131 132 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 133 if (file_path.empty()) { 134 base::PlatformFile platform_file = base::kInvalidPlatformFileValue; 135 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, 136 base::PassPlatformFile(&platform_file), 137 base::Closure()); 138 return; 139 } 140 141 PostFileSystemCallback( 142 file_system_getter_, 143 base::Bind(&fileapi_internal::OpenFile, 144 file_path, file_flags, 145 google_apis::CreateRelayCallback( 146 base::Bind(&RunCreateOrOpenFileCallback, 147 file_system_getter_, file_path, callback))), 148 base::Bind(&RunCreateOrOpenFileCallbackOnError, 149 callback, base::PLATFORM_FILE_ERROR_FAILED)); 150} 151 152void AsyncFileUtil::EnsureFileExists( 153 scoped_ptr<fileapi::FileSystemOperationContext> context, 154 const fileapi::FileSystemURL& url, 155 const EnsureFileExistsCallback& callback) { 156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 157 158 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 159 if (file_path.empty()) { 160 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, false); 161 return; 162 } 163 164 PostFileSystemCallback( 165 file_system_getter_, 166 base::Bind(&fileapi_internal::CreateFile, 167 file_path, true /* is_exlusive */, 168 google_apis::CreateRelayCallback( 169 base::Bind(&RunEnsureFileExistsCallback, callback))), 170 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED, false)); 171} 172 173void AsyncFileUtil::CreateDirectory( 174 scoped_ptr<fileapi::FileSystemOperationContext> context, 175 const fileapi::FileSystemURL& url, 176 bool exclusive, 177 bool recursive, 178 const StatusCallback& callback) { 179 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 180 181 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 182 if (file_path.empty()) { 183 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND); 184 return; 185 } 186 187 PostFileSystemCallback( 188 file_system_getter_, 189 base::Bind(&fileapi_internal::CreateDirectory, 190 file_path, exclusive, recursive, 191 google_apis::CreateRelayCallback(callback)), 192 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 193} 194 195void AsyncFileUtil::GetFileInfo( 196 scoped_ptr<fileapi::FileSystemOperationContext> context, 197 const fileapi::FileSystemURL& url, 198 const GetFileInfoCallback& callback) { 199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 200 201 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 202 if (file_path.empty()) { 203 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, base::PlatformFileInfo()); 204 return; 205 } 206 207 PostFileSystemCallback( 208 file_system_getter_, 209 base::Bind(&fileapi_internal::GetFileInfo, 210 file_path, google_apis::CreateRelayCallback(callback)), 211 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED, 212 base::PlatformFileInfo())); 213} 214 215void AsyncFileUtil::ReadDirectory( 216 scoped_ptr<fileapi::FileSystemOperationContext> context, 217 const fileapi::FileSystemURL& url, 218 const ReadDirectoryCallback& callback) { 219 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 220 221 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 222 if (file_path.empty()) { 223 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, EntryList(), false); 224 return; 225 } 226 227 PostFileSystemCallback( 228 file_system_getter_, 229 base::Bind(&fileapi_internal::ReadDirectory, 230 file_path, google_apis::CreateRelayCallback(callback)), 231 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED, 232 EntryList(), false)); 233} 234 235void AsyncFileUtil::Touch( 236 scoped_ptr<fileapi::FileSystemOperationContext> context, 237 const fileapi::FileSystemURL& url, 238 const base::Time& last_access_time, 239 const base::Time& last_modified_time, 240 const StatusCallback& callback) { 241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 242 243 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 244 if (file_path.empty()) { 245 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND); 246 return; 247 } 248 249 PostFileSystemCallback( 250 file_system_getter_, 251 base::Bind(&fileapi_internal::TouchFile, 252 file_path, last_access_time, last_modified_time, 253 google_apis::CreateRelayCallback(callback)), 254 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 255} 256 257void AsyncFileUtil::Truncate( 258 scoped_ptr<fileapi::FileSystemOperationContext> context, 259 const fileapi::FileSystemURL& url, 260 int64 length, 261 const StatusCallback& callback) { 262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 263 264 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 265 if (file_path.empty()) { 266 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND); 267 return; 268 } 269 270 PostFileSystemCallback( 271 file_system_getter_, 272 base::Bind(&fileapi_internal::Truncate, 273 file_path, length, google_apis::CreateRelayCallback(callback)), 274 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 275} 276 277void AsyncFileUtil::CopyFileLocal( 278 scoped_ptr<fileapi::FileSystemOperationContext> context, 279 const fileapi::FileSystemURL& src_url, 280 const fileapi::FileSystemURL& dest_url, 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::PLATFORM_FILE_ERROR_NOT_FOUND); 288 return; 289 } 290 291 PostFileSystemCallback( 292 file_system_getter_, 293 base::Bind(&fileapi_internal::Copy, 294 src_path, dest_path, 295 google_apis::CreateRelayCallback(callback)), 296 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 297} 298 299void AsyncFileUtil::MoveFileLocal( 300 scoped_ptr<fileapi::FileSystemOperationContext> context, 301 const fileapi::FileSystemURL& src_url, 302 const fileapi::FileSystemURL& dest_url, 303 const StatusCallback& callback) { 304 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 305 306 base::FilePath src_path = util::ExtractDrivePathFromFileSystemUrl(src_url); 307 base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url); 308 if (src_path.empty() || dest_path.empty()) { 309 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND); 310 return; 311 } 312 313 PostFileSystemCallback( 314 file_system_getter_, 315 base::Bind(&fileapi_internal::Move, 316 src_path, dest_path, 317 google_apis::CreateRelayCallback(callback)), 318 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 319} 320 321void AsyncFileUtil::CopyInForeignFile( 322 scoped_ptr<fileapi::FileSystemOperationContext> context, 323 const base::FilePath& src_file_path, 324 const fileapi::FileSystemURL& dest_url, 325 const StatusCallback& callback) { 326 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 327 328 base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url); 329 if (dest_path.empty()) { 330 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND); 331 return; 332 } 333 334 PostFileSystemCallback( 335 file_system_getter_, 336 base::Bind(&fileapi_internal::CopyInForeignFile, 337 src_file_path, dest_path, 338 google_apis::CreateRelayCallback(callback)), 339 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 340} 341 342void AsyncFileUtil::DeleteFile( 343 scoped_ptr<fileapi::FileSystemOperationContext> context, 344 const fileapi::FileSystemURL& url, 345 const StatusCallback& callback) { 346 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 347 348 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 349 if (file_path.empty()) { 350 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND); 351 return; 352 } 353 354 PostFileSystemCallback( 355 file_system_getter_, 356 base::Bind(&fileapi_internal::Remove, 357 file_path, false /* not recursive */, 358 google_apis::CreateRelayCallback(callback)), 359 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 360} 361 362void AsyncFileUtil::DeleteDirectory( 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::PLATFORM_FILE_ERROR_NOT_FOUND); 371 return; 372 } 373 374 PostFileSystemCallback( 375 file_system_getter_, 376 base::Bind(&fileapi_internal::Remove, 377 file_path, false /* not recursive */, 378 google_apis::CreateRelayCallback(callback)), 379 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 380} 381 382void AsyncFileUtil::DeleteRecursively( 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::PLATFORM_FILE_ERROR_NOT_FOUND); 391 return; 392 } 393 394 PostFileSystemCallback( 395 file_system_getter_, 396 base::Bind(&fileapi_internal::Remove, 397 file_path, true /* recursive */, 398 google_apis::CreateRelayCallback(callback)), 399 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 400} 401 402void AsyncFileUtil::CreateSnapshotFile( 403 scoped_ptr<fileapi::FileSystemOperationContext> context, 404 const fileapi::FileSystemURL& url, 405 const CreateSnapshotFileCallback& 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::PLATFORM_FILE_ERROR_NOT_FOUND, 411 base::PlatformFileInfo(), 412 base::FilePath(), 413 scoped_refptr<webkit_blob::ShareableFileReference>()); 414 return; 415 } 416 417 PostFileSystemCallback( 418 file_system_getter_, 419 base::Bind(&fileapi_internal::CreateSnapshotFile, 420 file_path, 421 google_apis::CreateRelayCallback( 422 base::Bind(&RunCreateSnapshotFileCallback, callback))), 423 base::Bind(callback, 424 base::PLATFORM_FILE_ERROR_FAILED, 425 base::PlatformFileInfo(), 426 base::FilePath(), 427 scoped_refptr<webkit_blob::ShareableFileReference>())); 428} 429 430} // namespace internal 431} // namespace drive 432