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