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 "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 20 using content::BrowserThread; 21 22 namespace drive { 23 namespace internal { 24 namespace { 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. 29 void 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|. 48 void 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. 68 void 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|. 80 void 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 // Runs |callback| with the arguments based on the given arguments. 96 void RunCreateSnapshotFileCallback( 97 const AsyncFileUtil::CreateSnapshotFileCallback& callback, 98 base::PlatformFileError error, 99 const base::PlatformFileInfo& file_info, 100 const base::FilePath& local_path, 101 webkit_blob::ScopedFile::ScopeOutPolicy scope_out_policy) { 102 // ShareableFileReference is thread *unsafe* class. So it is necessary to 103 // create the instance (by invoking GetOrCreate) on IO thread, though 104 // most drive file system related operations run on UI thread. 105 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 106 107 scoped_refptr<webkit_blob::ShareableFileReference> file_reference = 108 webkit_blob::ShareableFileReference::GetOrCreate(webkit_blob::ScopedFile( 109 local_path, 110 scope_out_policy, 111 BrowserThread::GetBlockingPool())); 112 callback.Run(error, file_info, local_path, file_reference); 113 } 114 115 } // namespace 116 117 AsyncFileUtil::AsyncFileUtil(const FileSystemGetter& file_system_getter) 118 : file_system_getter_(file_system_getter) { 119 } 120 121 AsyncFileUtil::~AsyncFileUtil() { 122 } 123 124 void 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::PLATFORM_FILE_ERROR_NOT_FOUND, 135 base::PassPlatformFile(&platform_file), 136 base::Closure()); 137 return; 138 } 139 140 PostFileSystemCallback( 141 file_system_getter_, 142 base::Bind(&fileapi_internal::OpenFile, 143 file_path, file_flags, 144 google_apis::CreateRelayCallback( 145 base::Bind(&RunCreateOrOpenFileCallback, 146 file_system_getter_, file_path, callback))), 147 base::Bind(&RunCreateOrOpenFileCallbackOnError, 148 callback, base::PLATFORM_FILE_ERROR_FAILED)); 149 } 150 151 void AsyncFileUtil::EnsureFileExists( 152 scoped_ptr<fileapi::FileSystemOperationContext> context, 153 const fileapi::FileSystemURL& url, 154 const EnsureFileExistsCallback& callback) { 155 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 156 157 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 158 if (file_path.empty()) { 159 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, false); 160 return; 161 } 162 163 PostFileSystemCallback( 164 file_system_getter_, 165 base::Bind(&fileapi_internal::CreateFile, 166 file_path, true /* is_exlusive */, 167 google_apis::CreateRelayCallback( 168 base::Bind(&RunEnsureFileExistsCallback, callback))), 169 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED, false)); 170 } 171 172 void AsyncFileUtil::CreateDirectory( 173 scoped_ptr<fileapi::FileSystemOperationContext> context, 174 const fileapi::FileSystemURL& url, 175 bool exclusive, 176 bool recursive, 177 const StatusCallback& callback) { 178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 179 180 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 181 if (file_path.empty()) { 182 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND); 183 return; 184 } 185 186 PostFileSystemCallback( 187 file_system_getter_, 188 base::Bind(&fileapi_internal::CreateDirectory, 189 file_path, exclusive, recursive, 190 google_apis::CreateRelayCallback(callback)), 191 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 192 } 193 194 void AsyncFileUtil::GetFileInfo( 195 scoped_ptr<fileapi::FileSystemOperationContext> context, 196 const fileapi::FileSystemURL& url, 197 const GetFileInfoCallback& callback) { 198 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 199 200 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 201 if (file_path.empty()) { 202 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, base::PlatformFileInfo()); 203 return; 204 } 205 206 PostFileSystemCallback( 207 file_system_getter_, 208 base::Bind(&fileapi_internal::GetFileInfo, 209 file_path, google_apis::CreateRelayCallback(callback)), 210 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED, 211 base::PlatformFileInfo())); 212 } 213 214 void AsyncFileUtil::ReadDirectory( 215 scoped_ptr<fileapi::FileSystemOperationContext> context, 216 const fileapi::FileSystemURL& url, 217 const ReadDirectoryCallback& callback) { 218 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 219 220 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 221 if (file_path.empty()) { 222 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, EntryList(), false); 223 return; 224 } 225 226 PostFileSystemCallback( 227 file_system_getter_, 228 base::Bind(&fileapi_internal::ReadDirectory, 229 file_path, google_apis::CreateRelayCallback(callback)), 230 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED, 231 EntryList(), false)); 232 } 233 234 void AsyncFileUtil::Touch( 235 scoped_ptr<fileapi::FileSystemOperationContext> context, 236 const fileapi::FileSystemURL& url, 237 const base::Time& last_access_time, 238 const base::Time& last_modified_time, 239 const StatusCallback& callback) { 240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 241 242 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 243 if (file_path.empty()) { 244 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND); 245 return; 246 } 247 248 PostFileSystemCallback( 249 file_system_getter_, 250 base::Bind(&fileapi_internal::TouchFile, 251 file_path, last_access_time, last_modified_time, 252 google_apis::CreateRelayCallback(callback)), 253 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 254 } 255 256 void AsyncFileUtil::Truncate( 257 scoped_ptr<fileapi::FileSystemOperationContext> context, 258 const fileapi::FileSystemURL& url, 259 int64 length, 260 const StatusCallback& callback) { 261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 262 263 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 264 if (file_path.empty()) { 265 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND); 266 return; 267 } 268 269 PostFileSystemCallback( 270 file_system_getter_, 271 base::Bind(&fileapi_internal::Truncate, 272 file_path, length, google_apis::CreateRelayCallback(callback)), 273 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 274 } 275 276 void AsyncFileUtil::CopyFileLocal( 277 scoped_ptr<fileapi::FileSystemOperationContext> context, 278 const fileapi::FileSystemURL& src_url, 279 const fileapi::FileSystemURL& dest_url, 280 CopyOrMoveOption option, 281 const CopyFileProgressCallback& progress_callback, 282 const StatusCallback& callback) { 283 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 284 285 base::FilePath src_path = util::ExtractDrivePathFromFileSystemUrl(src_url); 286 base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url); 287 if (src_path.empty() || dest_path.empty()) { 288 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND); 289 return; 290 } 291 292 PostFileSystemCallback( 293 file_system_getter_, 294 base::Bind( 295 &fileapi_internal::Copy, 296 src_path, dest_path, 297 option == fileapi::FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED, 298 google_apis::CreateRelayCallback(callback)), 299 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 300 } 301 302 void AsyncFileUtil::MoveFileLocal( 303 scoped_ptr<fileapi::FileSystemOperationContext> context, 304 const fileapi::FileSystemURL& src_url, 305 const fileapi::FileSystemURL& dest_url, 306 CopyOrMoveOption option, 307 const StatusCallback& callback) { 308 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 309 310 base::FilePath src_path = util::ExtractDrivePathFromFileSystemUrl(src_url); 311 base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url); 312 if (src_path.empty() || dest_path.empty()) { 313 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND); 314 return; 315 } 316 317 PostFileSystemCallback( 318 file_system_getter_, 319 base::Bind( 320 &fileapi_internal::Move, 321 src_path, dest_path, 322 option == fileapi::FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED, 323 google_apis::CreateRelayCallback(callback)), 324 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 325 } 326 327 void AsyncFileUtil::CopyInForeignFile( 328 scoped_ptr<fileapi::FileSystemOperationContext> context, 329 const base::FilePath& src_file_path, 330 const fileapi::FileSystemURL& dest_url, 331 const StatusCallback& callback) { 332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 333 334 base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url); 335 if (dest_path.empty()) { 336 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND); 337 return; 338 } 339 340 PostFileSystemCallback( 341 file_system_getter_, 342 base::Bind(&fileapi_internal::CopyInForeignFile, 343 src_file_path, dest_path, 344 google_apis::CreateRelayCallback(callback)), 345 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 346 } 347 348 void AsyncFileUtil::DeleteFile( 349 scoped_ptr<fileapi::FileSystemOperationContext> context, 350 const fileapi::FileSystemURL& url, 351 const StatusCallback& callback) { 352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 353 354 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 355 if (file_path.empty()) { 356 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND); 357 return; 358 } 359 360 PostFileSystemCallback( 361 file_system_getter_, 362 base::Bind(&fileapi_internal::Remove, 363 file_path, false /* not recursive */, 364 google_apis::CreateRelayCallback(callback)), 365 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 366 } 367 368 void AsyncFileUtil::DeleteDirectory( 369 scoped_ptr<fileapi::FileSystemOperationContext> context, 370 const fileapi::FileSystemURL& url, 371 const StatusCallback& callback) { 372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 373 374 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 375 if (file_path.empty()) { 376 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND); 377 return; 378 } 379 380 PostFileSystemCallback( 381 file_system_getter_, 382 base::Bind(&fileapi_internal::Remove, 383 file_path, false /* not recursive */, 384 google_apis::CreateRelayCallback(callback)), 385 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 386 } 387 388 void AsyncFileUtil::DeleteRecursively( 389 scoped_ptr<fileapi::FileSystemOperationContext> context, 390 const fileapi::FileSystemURL& url, 391 const StatusCallback& callback) { 392 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 393 394 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 395 if (file_path.empty()) { 396 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND); 397 return; 398 } 399 400 PostFileSystemCallback( 401 file_system_getter_, 402 base::Bind(&fileapi_internal::Remove, 403 file_path, true /* recursive */, 404 google_apis::CreateRelayCallback(callback)), 405 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 406 } 407 408 void AsyncFileUtil::CreateSnapshotFile( 409 scoped_ptr<fileapi::FileSystemOperationContext> context, 410 const fileapi::FileSystemURL& url, 411 const CreateSnapshotFileCallback& callback) { 412 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 413 414 base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url); 415 if (file_path.empty()) { 416 callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, 417 base::PlatformFileInfo(), 418 base::FilePath(), 419 scoped_refptr<webkit_blob::ShareableFileReference>()); 420 return; 421 } 422 423 PostFileSystemCallback( 424 file_system_getter_, 425 base::Bind(&fileapi_internal::CreateSnapshotFile, 426 file_path, 427 google_apis::CreateRelayCallback( 428 base::Bind(&RunCreateSnapshotFileCallback, callback))), 429 base::Bind(callback, 430 base::PLATFORM_FILE_ERROR_FAILED, 431 base::PlatformFileInfo(), 432 base::FilePath(), 433 scoped_refptr<webkit_blob::ShareableFileReference>())); 434 } 435 436 } // namespace internal 437 } // namespace drive 438