1 // Copyright (c) 2012 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 "webkit/browser/fileapi/obfuscated_file_util.h" 6 7 #include <queue> 8 #include <string> 9 #include <vector> 10 11 #include "base/file_util.h" 12 #include "base/format_macros.h" 13 #include "base/logging.h" 14 #include "base/message_loop/message_loop.h" 15 #include "base/metrics/histogram.h" 16 #include "base/stl_util.h" 17 #include "base/strings/string_number_conversions.h" 18 #include "base/strings/stringprintf.h" 19 #include "base/strings/sys_string_conversions.h" 20 #include "base/strings/utf_string_conversions.h" 21 #include "base/time/time.h" 22 #include "url/gurl.h" 23 #include "webkit/browser/fileapi/file_observers.h" 24 #include "webkit/browser/fileapi/file_system_context.h" 25 #include "webkit/browser/fileapi/file_system_operation_context.h" 26 #include "webkit/browser/fileapi/file_system_url.h" 27 #include "webkit/browser/fileapi/native_file_util.h" 28 #include "webkit/browser/fileapi/sandbox_file_system_backend.h" 29 #include "webkit/browser/fileapi/sandbox_isolated_origin_database.h" 30 #include "webkit/browser/fileapi/sandbox_origin_database.h" 31 #include "webkit/browser/fileapi/sandbox_prioritized_origin_database.h" 32 #include "webkit/browser/fileapi/timed_task_helper.h" 33 #include "webkit/browser/quota/quota_manager.h" 34 #include "webkit/common/database/database_identifier.h" 35 #include "webkit/common/fileapi/file_system_util.h" 36 37 // Example of various paths: 38 // void ObfuscatedFileUtil::DoSomething(const FileSystemURL& url) { 39 // base::FilePath virtual_path = url.path(); 40 // base::FilePath local_path = GetLocalFilePath(url); 41 // 42 // NativeFileUtil::DoSomething(local_path); 43 // file_util::DoAnother(local_path); 44 // } 45 46 namespace fileapi { 47 48 namespace { 49 50 typedef SandboxDirectoryDatabase::FileId FileId; 51 typedef SandboxDirectoryDatabase::FileInfo FileInfo; 52 53 void InitFileInfo( 54 SandboxDirectoryDatabase::FileInfo* file_info, 55 SandboxDirectoryDatabase::FileId parent_id, 56 const base::FilePath::StringType& file_name) { 57 DCHECK(file_info); 58 file_info->parent_id = parent_id; 59 file_info->name = file_name; 60 } 61 62 // Costs computed as per crbug.com/86114, based on the LevelDB implementation of 63 // path storage under Linux. It's not clear if that will differ on Windows, on 64 // which base::FilePath uses wide chars [since they're converted to UTF-8 for 65 // storage anyway], but as long as the cost is high enough that one can't cheat 66 // on quota by storing data in paths, it doesn't need to be all that accurate. 67 const int64 kPathCreationQuotaCost = 146; // Bytes per inode, basically. 68 const int64 kPathByteQuotaCost = 2; // Bytes per byte of path length in UTF-8. 69 70 int64 UsageForPath(size_t length) { 71 return kPathCreationQuotaCost + 72 static_cast<int64>(length) * kPathByteQuotaCost; 73 } 74 75 bool AllocateQuota(FileSystemOperationContext* context, int64 growth) { 76 if (context->allowed_bytes_growth() == quota::QuotaManager::kNoLimit) 77 return true; 78 79 int64 new_quota = context->allowed_bytes_growth() - growth; 80 if (growth > 0 && new_quota < 0) 81 return false; 82 context->set_allowed_bytes_growth(new_quota); 83 return true; 84 } 85 86 void UpdateUsage( 87 FileSystemOperationContext* context, 88 const FileSystemURL& url, 89 int64 growth) { 90 context->update_observers()->Notify( 91 &FileUpdateObserver::OnUpdate, MakeTuple(url, growth)); 92 } 93 94 void TouchDirectory(SandboxDirectoryDatabase* db, FileId dir_id) { 95 DCHECK(db); 96 if (!db->UpdateModificationTime(dir_id, base::Time::Now())) 97 NOTREACHED(); 98 } 99 100 enum IsolatedOriginStatus { 101 kIsolatedOriginMatch, 102 kIsolatedOriginDontMatch, 103 kIsolatedOriginStatusMax, 104 }; 105 106 } // namespace 107 108 using base::PlatformFile; 109 using base::PlatformFileError; 110 111 class ObfuscatedFileEnumerator 112 : public FileSystemFileUtil::AbstractFileEnumerator { 113 public: 114 ObfuscatedFileEnumerator( 115 SandboxDirectoryDatabase* db, 116 FileSystemOperationContext* context, 117 ObfuscatedFileUtil* obfuscated_file_util, 118 const FileSystemURL& root_url, 119 bool recursive) 120 : db_(db), 121 context_(context), 122 obfuscated_file_util_(obfuscated_file_util), 123 root_url_(root_url), 124 recursive_(recursive), 125 current_file_id_(0) { 126 base::FilePath root_virtual_path = root_url.path(); 127 FileId file_id; 128 129 if (!db_->GetFileWithPath(root_virtual_path, &file_id)) 130 return; 131 132 FileRecord record = { file_id, root_virtual_path }; 133 recurse_queue_.push(record); 134 } 135 136 virtual ~ObfuscatedFileEnumerator() {} 137 138 virtual base::FilePath Next() OVERRIDE { 139 ProcessRecurseQueue(); 140 if (display_stack_.empty()) 141 return base::FilePath(); 142 143 current_file_id_ = display_stack_.back(); 144 display_stack_.pop_back(); 145 146 FileInfo file_info; 147 base::FilePath platform_file_path; 148 base::PlatformFileError error = 149 obfuscated_file_util_->GetFileInfoInternal( 150 db_, context_, root_url_, current_file_id_, 151 &file_info, ¤t_platform_file_info_, &platform_file_path); 152 if (error != base::PLATFORM_FILE_OK) 153 return Next(); 154 155 base::FilePath virtual_path = 156 current_parent_virtual_path_.Append(file_info.name); 157 if (recursive_ && file_info.is_directory()) { 158 FileRecord record = { current_file_id_, virtual_path }; 159 recurse_queue_.push(record); 160 } 161 return virtual_path; 162 } 163 164 virtual int64 Size() OVERRIDE { 165 return current_platform_file_info_.size; 166 } 167 168 virtual base::Time LastModifiedTime() OVERRIDE { 169 return current_platform_file_info_.last_modified; 170 } 171 172 virtual bool IsDirectory() OVERRIDE { 173 return current_platform_file_info_.is_directory; 174 } 175 176 private: 177 typedef SandboxDirectoryDatabase::FileId FileId; 178 typedef SandboxDirectoryDatabase::FileInfo FileInfo; 179 180 struct FileRecord { 181 FileId file_id; 182 base::FilePath virtual_path; 183 }; 184 185 void ProcessRecurseQueue() { 186 while (display_stack_.empty() && !recurse_queue_.empty()) { 187 FileRecord entry = recurse_queue_.front(); 188 recurse_queue_.pop(); 189 if (!db_->ListChildren(entry.file_id, &display_stack_)) { 190 display_stack_.clear(); 191 return; 192 } 193 current_parent_virtual_path_ = entry.virtual_path; 194 } 195 } 196 197 SandboxDirectoryDatabase* db_; 198 FileSystemOperationContext* context_; 199 ObfuscatedFileUtil* obfuscated_file_util_; 200 FileSystemURL root_url_; 201 bool recursive_; 202 203 std::queue<FileRecord> recurse_queue_; 204 std::vector<FileId> display_stack_; 205 base::FilePath current_parent_virtual_path_; 206 207 FileId current_file_id_; 208 base::PlatformFileInfo current_platform_file_info_; 209 }; 210 211 class ObfuscatedOriginEnumerator 212 : public ObfuscatedFileUtil::AbstractOriginEnumerator { 213 public: 214 typedef SandboxOriginDatabase::OriginRecord OriginRecord; 215 ObfuscatedOriginEnumerator( 216 SandboxOriginDatabaseInterface* origin_database, 217 const base::FilePath& base_file_path) 218 : base_file_path_(base_file_path) { 219 if (origin_database) 220 origin_database->ListAllOrigins(&origins_); 221 } 222 223 virtual ~ObfuscatedOriginEnumerator() {} 224 225 // Returns the next origin. Returns empty if there are no more origins. 226 virtual GURL Next() OVERRIDE { 227 OriginRecord record; 228 if (!origins_.empty()) { 229 record = origins_.back(); 230 origins_.pop_back(); 231 } 232 current_ = record; 233 return webkit_database::GetOriginFromIdentifier(record.origin); 234 } 235 236 // Returns the current origin's information. 237 virtual bool HasTypeDirectory(const std::string& type_string) const OVERRIDE { 238 if (current_.path.empty()) 239 return false; 240 if (type_string.empty()) { 241 NOTREACHED(); 242 return false; 243 } 244 base::FilePath path = 245 base_file_path_.Append(current_.path).AppendASCII(type_string); 246 return base::DirectoryExists(path); 247 } 248 249 private: 250 std::vector<OriginRecord> origins_; 251 OriginRecord current_; 252 base::FilePath base_file_path_; 253 }; 254 255 ObfuscatedFileUtil::ObfuscatedFileUtil( 256 quota::SpecialStoragePolicy* special_storage_policy, 257 const base::FilePath& file_system_directory, 258 base::SequencedTaskRunner* file_task_runner, 259 const GetTypeStringForURLCallback& get_type_string_for_url, 260 const std::set<std::string>& known_type_strings, 261 SandboxFileSystemBackendDelegate* sandbox_delegate) 262 : special_storage_policy_(special_storage_policy), 263 file_system_directory_(file_system_directory), 264 db_flush_delay_seconds_(10 * 60), // 10 mins. 265 file_task_runner_(file_task_runner), 266 get_type_string_for_url_(get_type_string_for_url), 267 known_type_strings_(known_type_strings), 268 sandbox_delegate_(sandbox_delegate) { 269 } 270 271 ObfuscatedFileUtil::~ObfuscatedFileUtil() { 272 DropDatabases(); 273 } 274 275 PlatformFileError ObfuscatedFileUtil::CreateOrOpen( 276 FileSystemOperationContext* context, 277 const FileSystemURL& url, int file_flags, 278 PlatformFile* file_handle, bool* created) { 279 PlatformFileError error = CreateOrOpenInternal(context, url, file_flags, 280 file_handle, created); 281 if (*file_handle != base::kInvalidPlatformFileValue && 282 file_flags & base::PLATFORM_FILE_WRITE && 283 context->quota_limit_type() == quota::kQuotaLimitTypeUnlimited && 284 sandbox_delegate_) { 285 DCHECK_EQ(base::PLATFORM_FILE_OK, error); 286 sandbox_delegate_->StickyInvalidateUsageCache(url.origin(), url.type()); 287 } 288 return error; 289 } 290 291 PlatformFileError ObfuscatedFileUtil::Close( 292 FileSystemOperationContext* context, 293 base::PlatformFile file) { 294 return NativeFileUtil::Close(file); 295 } 296 297 PlatformFileError ObfuscatedFileUtil::EnsureFileExists( 298 FileSystemOperationContext* context, 299 const FileSystemURL& url, 300 bool* created) { 301 SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true); 302 if (!db) 303 return base::PLATFORM_FILE_ERROR_FAILED; 304 305 FileId file_id; 306 if (db->GetFileWithPath(url.path(), &file_id)) { 307 FileInfo file_info; 308 if (!db->GetFileInfo(file_id, &file_info)) { 309 NOTREACHED(); 310 return base::PLATFORM_FILE_ERROR_FAILED; 311 } 312 if (file_info.is_directory()) 313 return base::PLATFORM_FILE_ERROR_NOT_A_FILE; 314 if (created) 315 *created = false; 316 return base::PLATFORM_FILE_OK; 317 } 318 FileId parent_id; 319 if (!db->GetFileWithPath(VirtualPath::DirName(url.path()), &parent_id)) 320 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 321 322 FileInfo file_info; 323 InitFileInfo(&file_info, parent_id, 324 VirtualPath::BaseName(url.path()).value()); 325 326 int64 growth = UsageForPath(file_info.name.size()); 327 if (!AllocateQuota(context, growth)) 328 return base::PLATFORM_FILE_ERROR_NO_SPACE; 329 PlatformFileError error = CreateFile( 330 context, base::FilePath(), url, &file_info, 0, NULL); 331 if (created && base::PLATFORM_FILE_OK == error) { 332 *created = true; 333 UpdateUsage(context, url, growth); 334 context->change_observers()->Notify( 335 &FileChangeObserver::OnCreateFile, MakeTuple(url)); 336 } 337 return error; 338 } 339 340 PlatformFileError ObfuscatedFileUtil::CreateDirectory( 341 FileSystemOperationContext* context, 342 const FileSystemURL& url, 343 bool exclusive, 344 bool recursive) { 345 SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true); 346 if (!db) 347 return base::PLATFORM_FILE_ERROR_FAILED; 348 349 FileId file_id; 350 if (db->GetFileWithPath(url.path(), &file_id)) { 351 FileInfo file_info; 352 if (exclusive) 353 return base::PLATFORM_FILE_ERROR_EXISTS; 354 if (!db->GetFileInfo(file_id, &file_info)) { 355 NOTREACHED(); 356 return base::PLATFORM_FILE_ERROR_FAILED; 357 } 358 if (!file_info.is_directory()) 359 return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; 360 return base::PLATFORM_FILE_OK; 361 } 362 363 std::vector<base::FilePath::StringType> components; 364 VirtualPath::GetComponents(url.path(), &components); 365 FileId parent_id = 0; 366 size_t index; 367 for (index = 0; index < components.size(); ++index) { 368 base::FilePath::StringType name = components[index]; 369 if (name == FILE_PATH_LITERAL("/")) 370 continue; 371 if (!db->GetChildWithName(parent_id, name, &parent_id)) 372 break; 373 } 374 if (!db->IsDirectory(parent_id)) 375 return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; 376 if (!recursive && components.size() - index > 1) 377 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 378 bool first = true; 379 for (; index < components.size(); ++index) { 380 FileInfo file_info; 381 file_info.name = components[index]; 382 if (file_info.name == FILE_PATH_LITERAL("/")) 383 continue; 384 file_info.modification_time = base::Time::Now(); 385 file_info.parent_id = parent_id; 386 int64 growth = UsageForPath(file_info.name.size()); 387 if (!AllocateQuota(context, growth)) 388 return base::PLATFORM_FILE_ERROR_NO_SPACE; 389 base::PlatformFileError error = db->AddFileInfo(file_info, &parent_id); 390 if (error != base::PLATFORM_FILE_OK) 391 return error; 392 UpdateUsage(context, url, growth); 393 context->change_observers()->Notify( 394 &FileChangeObserver::OnCreateDirectory, MakeTuple(url)); 395 if (first) { 396 first = false; 397 TouchDirectory(db, file_info.parent_id); 398 } 399 } 400 return base::PLATFORM_FILE_OK; 401 } 402 403 PlatformFileError ObfuscatedFileUtil::GetFileInfo( 404 FileSystemOperationContext* context, 405 const FileSystemURL& url, 406 base::PlatformFileInfo* file_info, 407 base::FilePath* platform_file_path) { 408 SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, false); 409 if (!db) 410 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 411 FileId file_id; 412 if (!db->GetFileWithPath(url.path(), &file_id)) 413 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 414 FileInfo local_info; 415 return GetFileInfoInternal(db, context, url, 416 file_id, &local_info, 417 file_info, platform_file_path); 418 } 419 420 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> 421 ObfuscatedFileUtil::CreateFileEnumerator( 422 FileSystemOperationContext* context, 423 const FileSystemURL& root_url) { 424 return CreateFileEnumerator(context, root_url, false /* recursive */); 425 } 426 427 PlatformFileError ObfuscatedFileUtil::GetLocalFilePath( 428 FileSystemOperationContext* context, 429 const FileSystemURL& url, 430 base::FilePath* local_path) { 431 SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, false); 432 if (!db) 433 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 434 FileId file_id; 435 if (!db->GetFileWithPath(url.path(), &file_id)) 436 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 437 FileInfo file_info; 438 if (!db->GetFileInfo(file_id, &file_info) || file_info.is_directory()) { 439 NOTREACHED(); 440 // Directories have no local file path. 441 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 442 } 443 *local_path = DataPathToLocalPath(url, file_info.data_path); 444 445 if (local_path->empty()) 446 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 447 return base::PLATFORM_FILE_OK; 448 } 449 450 PlatformFileError ObfuscatedFileUtil::Touch( 451 FileSystemOperationContext* context, 452 const FileSystemURL& url, 453 const base::Time& last_access_time, 454 const base::Time& last_modified_time) { 455 SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, false); 456 if (!db) 457 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 458 FileId file_id; 459 if (!db->GetFileWithPath(url.path(), &file_id)) 460 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 461 462 FileInfo file_info; 463 if (!db->GetFileInfo(file_id, &file_info)) { 464 NOTREACHED(); 465 return base::PLATFORM_FILE_ERROR_FAILED; 466 } 467 if (file_info.is_directory()) { 468 if (!db->UpdateModificationTime(file_id, last_modified_time)) 469 return base::PLATFORM_FILE_ERROR_FAILED; 470 return base::PLATFORM_FILE_OK; 471 } 472 return NativeFileUtil::Touch( 473 DataPathToLocalPath(url, file_info.data_path), 474 last_access_time, last_modified_time); 475 } 476 477 PlatformFileError ObfuscatedFileUtil::Truncate( 478 FileSystemOperationContext* context, 479 const FileSystemURL& url, 480 int64 length) { 481 base::PlatformFileInfo file_info; 482 base::FilePath local_path; 483 base::PlatformFileError error = 484 GetFileInfo(context, url, &file_info, &local_path); 485 if (error != base::PLATFORM_FILE_OK) 486 return error; 487 488 int64 growth = length - file_info.size; 489 if (!AllocateQuota(context, growth)) 490 return base::PLATFORM_FILE_ERROR_NO_SPACE; 491 error = NativeFileUtil::Truncate(local_path, length); 492 if (error == base::PLATFORM_FILE_OK) { 493 UpdateUsage(context, url, growth); 494 context->change_observers()->Notify( 495 &FileChangeObserver::OnModifyFile, MakeTuple(url)); 496 } 497 return error; 498 } 499 500 PlatformFileError ObfuscatedFileUtil::CopyOrMoveFile( 501 FileSystemOperationContext* context, 502 const FileSystemURL& src_url, 503 const FileSystemURL& dest_url, 504 CopyOrMoveOption option, 505 bool copy) { 506 // Cross-filesystem copies and moves should be handled via CopyInForeignFile. 507 DCHECK(src_url.origin() == dest_url.origin()); 508 DCHECK(src_url.type() == dest_url.type()); 509 510 SandboxDirectoryDatabase* db = GetDirectoryDatabase(src_url, true); 511 if (!db) 512 return base::PLATFORM_FILE_ERROR_FAILED; 513 514 FileId src_file_id; 515 if (!db->GetFileWithPath(src_url.path(), &src_file_id)) 516 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 517 518 FileId dest_file_id; 519 bool overwrite = db->GetFileWithPath(dest_url.path(), 520 &dest_file_id); 521 522 FileInfo src_file_info; 523 base::PlatformFileInfo src_platform_file_info; 524 base::FilePath src_local_path; 525 base::PlatformFileError error = GetFileInfoInternal( 526 db, context, src_url, src_file_id, 527 &src_file_info, &src_platform_file_info, &src_local_path); 528 if (error != base::PLATFORM_FILE_OK) 529 return error; 530 if (src_file_info.is_directory()) 531 return base::PLATFORM_FILE_ERROR_NOT_A_FILE; 532 533 FileInfo dest_file_info; 534 base::PlatformFileInfo dest_platform_file_info; // overwrite case only 535 base::FilePath dest_local_path; // overwrite case only 536 if (overwrite) { 537 base::PlatformFileError error = GetFileInfoInternal( 538 db, context, dest_url, dest_file_id, 539 &dest_file_info, &dest_platform_file_info, &dest_local_path); 540 if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) 541 overwrite = false; // fallback to non-overwrite case 542 else if (error != base::PLATFORM_FILE_OK) 543 return error; 544 else if (dest_file_info.is_directory()) 545 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION; 546 } 547 if (!overwrite) { 548 FileId dest_parent_id; 549 if (!db->GetFileWithPath(VirtualPath::DirName(dest_url.path()), 550 &dest_parent_id)) { 551 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 552 } 553 554 dest_file_info = src_file_info; 555 dest_file_info.parent_id = dest_parent_id; 556 dest_file_info.name = 557 VirtualPath::BaseName(dest_url.path()).value(); 558 } 559 560 int64 growth = 0; 561 if (copy) 562 growth += src_platform_file_info.size; 563 else 564 growth -= UsageForPath(src_file_info.name.size()); 565 if (overwrite) 566 growth -= dest_platform_file_info.size; 567 else 568 growth += UsageForPath(dest_file_info.name.size()); 569 if (!AllocateQuota(context, growth)) 570 return base::PLATFORM_FILE_ERROR_NO_SPACE; 571 572 /* 573 * Copy-with-overwrite 574 * Just overwrite data file 575 * Copy-without-overwrite 576 * Copy backing file 577 * Create new metadata pointing to new backing file. 578 * Move-with-overwrite 579 * transaction: 580 * Remove source entry. 581 * Point target entry to source entry's backing file. 582 * Delete target entry's old backing file 583 * Move-without-overwrite 584 * Just update metadata 585 */ 586 error = base::PLATFORM_FILE_ERROR_FAILED; 587 if (copy) { 588 if (overwrite) { 589 error = NativeFileUtil::CopyOrMoveFile( 590 src_local_path, 591 dest_local_path, 592 option, 593 fileapi::NativeFileUtil::CopyOrMoveModeForDestination( 594 dest_url, true /* copy */)); 595 } else { // non-overwrite 596 error = CreateFile(context, src_local_path, 597 dest_url, &dest_file_info, 0, NULL); 598 } 599 } else { 600 if (overwrite) { 601 if (db->OverwritingMoveFile(src_file_id, dest_file_id)) { 602 if (base::PLATFORM_FILE_OK != 603 NativeFileUtil::DeleteFile(dest_local_path)) 604 LOG(WARNING) << "Leaked a backing file."; 605 error = base::PLATFORM_FILE_OK; 606 } else { 607 error = base::PLATFORM_FILE_ERROR_FAILED; 608 } 609 } else { // non-overwrite 610 if (db->UpdateFileInfo(src_file_id, dest_file_info)) 611 error = base::PLATFORM_FILE_OK; 612 else 613 error = base::PLATFORM_FILE_ERROR_FAILED; 614 } 615 } 616 617 if (error != base::PLATFORM_FILE_OK) 618 return error; 619 620 if (overwrite) { 621 context->change_observers()->Notify( 622 &FileChangeObserver::OnModifyFile, 623 MakeTuple(dest_url)); 624 } else { 625 context->change_observers()->Notify( 626 &FileChangeObserver::OnCreateFileFrom, 627 MakeTuple(dest_url, src_url)); 628 } 629 630 if (!copy) { 631 context->change_observers()->Notify( 632 &FileChangeObserver::OnRemoveFile, MakeTuple(src_url)); 633 TouchDirectory(db, src_file_info.parent_id); 634 } 635 636 TouchDirectory(db, dest_file_info.parent_id); 637 638 UpdateUsage(context, dest_url, growth); 639 return error; 640 } 641 642 PlatformFileError ObfuscatedFileUtil::CopyInForeignFile( 643 FileSystemOperationContext* context, 644 const base::FilePath& src_file_path, 645 const FileSystemURL& dest_url) { 646 SandboxDirectoryDatabase* db = GetDirectoryDatabase(dest_url, true); 647 if (!db) 648 return base::PLATFORM_FILE_ERROR_FAILED; 649 650 base::PlatformFileInfo src_platform_file_info; 651 if (!base::GetFileInfo(src_file_path, &src_platform_file_info)) 652 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 653 654 FileId dest_file_id; 655 bool overwrite = db->GetFileWithPath(dest_url.path(), 656 &dest_file_id); 657 658 FileInfo dest_file_info; 659 base::PlatformFileInfo dest_platform_file_info; // overwrite case only 660 if (overwrite) { 661 base::FilePath dest_local_path; 662 base::PlatformFileError error = GetFileInfoInternal( 663 db, context, dest_url, dest_file_id, 664 &dest_file_info, &dest_platform_file_info, &dest_local_path); 665 if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) 666 overwrite = false; // fallback to non-overwrite case 667 else if (error != base::PLATFORM_FILE_OK) 668 return error; 669 else if (dest_file_info.is_directory()) 670 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION; 671 } 672 if (!overwrite) { 673 FileId dest_parent_id; 674 if (!db->GetFileWithPath(VirtualPath::DirName(dest_url.path()), 675 &dest_parent_id)) { 676 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 677 } 678 if (!dest_file_info.is_directory()) 679 return base::PLATFORM_FILE_ERROR_FAILED; 680 InitFileInfo(&dest_file_info, dest_parent_id, 681 VirtualPath::BaseName(dest_url.path()).value()); 682 } 683 684 int64 growth = src_platform_file_info.size; 685 if (overwrite) 686 growth -= dest_platform_file_info.size; 687 else 688 growth += UsageForPath(dest_file_info.name.size()); 689 if (!AllocateQuota(context, growth)) 690 return base::PLATFORM_FILE_ERROR_NO_SPACE; 691 692 base::PlatformFileError error; 693 if (overwrite) { 694 base::FilePath dest_local_path = 695 DataPathToLocalPath(dest_url, dest_file_info.data_path); 696 error = NativeFileUtil::CopyOrMoveFile( 697 src_file_path, dest_local_path, 698 FileSystemOperation::OPTION_NONE, 699 fileapi::NativeFileUtil::CopyOrMoveModeForDestination(dest_url, 700 true /* copy */)); 701 } else { 702 error = CreateFile(context, src_file_path, 703 dest_url, &dest_file_info, 0, NULL); 704 } 705 706 if (error != base::PLATFORM_FILE_OK) 707 return error; 708 709 if (overwrite) { 710 context->change_observers()->Notify( 711 &FileChangeObserver::OnModifyFile, MakeTuple(dest_url)); 712 } else { 713 context->change_observers()->Notify( 714 &FileChangeObserver::OnCreateFile, MakeTuple(dest_url)); 715 } 716 717 UpdateUsage(context, dest_url, growth); 718 TouchDirectory(db, dest_file_info.parent_id); 719 return base::PLATFORM_FILE_OK; 720 } 721 722 PlatformFileError ObfuscatedFileUtil::DeleteFile( 723 FileSystemOperationContext* context, 724 const FileSystemURL& url) { 725 SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true); 726 if (!db) 727 return base::PLATFORM_FILE_ERROR_FAILED; 728 FileId file_id; 729 if (!db->GetFileWithPath(url.path(), &file_id)) 730 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 731 732 FileInfo file_info; 733 base::PlatformFileInfo platform_file_info; 734 base::FilePath local_path; 735 base::PlatformFileError error = GetFileInfoInternal( 736 db, context, url, file_id, &file_info, &platform_file_info, &local_path); 737 if (error != base::PLATFORM_FILE_ERROR_NOT_FOUND && 738 error != base::PLATFORM_FILE_OK) 739 return error; 740 741 if (file_info.is_directory()) 742 return base::PLATFORM_FILE_ERROR_NOT_A_FILE; 743 744 int64 growth = -UsageForPath(file_info.name.size()) - platform_file_info.size; 745 AllocateQuota(context, growth); 746 if (!db->RemoveFileInfo(file_id)) { 747 NOTREACHED(); 748 return base::PLATFORM_FILE_ERROR_FAILED; 749 } 750 UpdateUsage(context, url, growth); 751 TouchDirectory(db, file_info.parent_id); 752 753 context->change_observers()->Notify( 754 &FileChangeObserver::OnRemoveFile, MakeTuple(url)); 755 756 if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) 757 return base::PLATFORM_FILE_OK; 758 759 error = NativeFileUtil::DeleteFile(local_path); 760 if (base::PLATFORM_FILE_OK != error) 761 LOG(WARNING) << "Leaked a backing file."; 762 return base::PLATFORM_FILE_OK; 763 } 764 765 PlatformFileError ObfuscatedFileUtil::DeleteDirectory( 766 FileSystemOperationContext* context, 767 const FileSystemURL& url) { 768 SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true); 769 if (!db) 770 return base::PLATFORM_FILE_ERROR_FAILED; 771 772 FileId file_id; 773 if (!db->GetFileWithPath(url.path(), &file_id)) 774 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 775 FileInfo file_info; 776 if (!db->GetFileInfo(file_id, &file_info)) { 777 NOTREACHED(); 778 return base::PLATFORM_FILE_ERROR_FAILED; 779 } 780 if (!file_info.is_directory()) 781 return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; 782 if (!db->RemoveFileInfo(file_id)) 783 return base::PLATFORM_FILE_ERROR_NOT_EMPTY; 784 int64 growth = -UsageForPath(file_info.name.size()); 785 AllocateQuota(context, growth); 786 UpdateUsage(context, url, growth); 787 TouchDirectory(db, file_info.parent_id); 788 context->change_observers()->Notify( 789 &FileChangeObserver::OnRemoveDirectory, MakeTuple(url)); 790 return base::PLATFORM_FILE_OK; 791 } 792 793 webkit_blob::ScopedFile ObfuscatedFileUtil::CreateSnapshotFile( 794 FileSystemOperationContext* context, 795 const FileSystemURL& url, 796 base::PlatformFileError* error, 797 base::PlatformFileInfo* file_info, 798 base::FilePath* platform_path) { 799 // We're just returning the local file information. 800 *error = GetFileInfo(context, url, file_info, platform_path); 801 if (*error == base::PLATFORM_FILE_OK && file_info->is_directory) { 802 *file_info = base::PlatformFileInfo(); 803 *error = base::PLATFORM_FILE_ERROR_NOT_A_FILE; 804 } 805 return webkit_blob::ScopedFile(); 806 } 807 808 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> 809 ObfuscatedFileUtil::CreateFileEnumerator( 810 FileSystemOperationContext* context, 811 const FileSystemURL& root_url, 812 bool recursive) { 813 SandboxDirectoryDatabase* db = GetDirectoryDatabase(root_url, false); 814 if (!db) { 815 return scoped_ptr<AbstractFileEnumerator>(new EmptyFileEnumerator()); 816 } 817 return scoped_ptr<AbstractFileEnumerator>( 818 new ObfuscatedFileEnumerator(db, context, this, root_url, recursive)); 819 } 820 821 bool ObfuscatedFileUtil::IsDirectoryEmpty( 822 FileSystemOperationContext* context, 823 const FileSystemURL& url) { 824 SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, false); 825 if (!db) 826 return true; // Not a great answer, but it's what others do. 827 FileId file_id; 828 if (!db->GetFileWithPath(url.path(), &file_id)) 829 return true; // Ditto. 830 FileInfo file_info; 831 if (!db->GetFileInfo(file_id, &file_info)) { 832 DCHECK(!file_id); 833 // It's the root directory and the database hasn't been initialized yet. 834 return true; 835 } 836 if (!file_info.is_directory()) 837 return true; 838 std::vector<FileId> children; 839 // TODO(ericu): This could easily be made faster with help from the database. 840 if (!db->ListChildren(file_id, &children)) 841 return true; 842 return children.empty(); 843 } 844 845 base::FilePath ObfuscatedFileUtil::GetDirectoryForOriginAndType( 846 const GURL& origin, 847 const std::string& type_string, 848 bool create, 849 base::PlatformFileError* error_code) { 850 base::FilePath origin_dir = GetDirectoryForOrigin(origin, create, error_code); 851 if (origin_dir.empty()) 852 return base::FilePath(); 853 if (type_string.empty()) 854 return origin_dir; 855 base::FilePath path = origin_dir.AppendASCII(type_string); 856 base::PlatformFileError error = base::PLATFORM_FILE_OK; 857 if (!base::DirectoryExists(path) && 858 (!create || !base::CreateDirectory(path))) { 859 error = create ? 860 base::PLATFORM_FILE_ERROR_FAILED : 861 base::PLATFORM_FILE_ERROR_NOT_FOUND; 862 } 863 864 if (error_code) 865 *error_code = error; 866 return path; 867 } 868 869 bool ObfuscatedFileUtil::DeleteDirectoryForOriginAndType( 870 const GURL& origin, 871 const std::string& type_string) { 872 base::PlatformFileError error = base::PLATFORM_FILE_OK; 873 base::FilePath origin_type_path = GetDirectoryForOriginAndType( 874 origin, type_string, false, &error); 875 if (origin_type_path.empty()) 876 return true; 877 if (error != base::PLATFORM_FILE_ERROR_NOT_FOUND) { 878 // TODO(dmikurube): Consider the return value of DestroyDirectoryDatabase. 879 // We ignore its error now since 1) it doesn't matter the final result, and 880 // 2) it always returns false in Windows because of LevelDB's 881 // implementation. 882 // Information about failure would be useful for debugging. 883 if (!type_string.empty()) 884 DestroyDirectoryDatabase(origin, type_string); 885 if (!base::DeleteFile(origin_type_path, true /* recursive */)) 886 return false; 887 } 888 889 base::FilePath origin_path = VirtualPath::DirName(origin_type_path); 890 DCHECK_EQ(origin_path.value(), 891 GetDirectoryForOrigin(origin, false, NULL).value()); 892 893 if (!type_string.empty()) { 894 // At this point we are sure we had successfully deleted the origin/type 895 // directory (i.e. we're ready to just return true). 896 // See if we have other directories in this origin directory. 897 for (std::set<std::string>::iterator iter = known_type_strings_.begin(); 898 iter != known_type_strings_.end(); 899 ++iter) { 900 if (*iter == type_string) 901 continue; 902 if (base::DirectoryExists(origin_path.AppendASCII(*iter))) { 903 // Other type's directory exists; just return true here. 904 return true; 905 } 906 } 907 } 908 909 // No other directories seem exist. Try deleting the entire origin directory. 910 InitOriginDatabase(origin, false); 911 if (origin_database_) { 912 origin_database_->RemovePathForOrigin( 913 webkit_database::GetIdentifierFromOrigin(origin)); 914 } 915 if (!base::DeleteFile(origin_path, true /* recursive */)) 916 return false; 917 918 return true; 919 } 920 921 ObfuscatedFileUtil::AbstractOriginEnumerator* 922 ObfuscatedFileUtil::CreateOriginEnumerator() { 923 std::vector<SandboxOriginDatabase::OriginRecord> origins; 924 925 InitOriginDatabase(GURL(), false); 926 return new ObfuscatedOriginEnumerator( 927 origin_database_.get(), file_system_directory_); 928 } 929 930 bool ObfuscatedFileUtil::DestroyDirectoryDatabase( 931 const GURL& origin, 932 const std::string& type_string) { 933 std::string key = GetDirectoryDatabaseKey(origin, type_string); 934 if (key.empty()) 935 return true; 936 DirectoryMap::iterator iter = directories_.find(key); 937 if (iter != directories_.end()) { 938 SandboxDirectoryDatabase* database = iter->second; 939 directories_.erase(iter); 940 delete database; 941 } 942 943 PlatformFileError error = base::PLATFORM_FILE_OK; 944 base::FilePath path = GetDirectoryForOriginAndType( 945 origin, type_string, false, &error); 946 if (path.empty() || error == base::PLATFORM_FILE_ERROR_NOT_FOUND) 947 return true; 948 return SandboxDirectoryDatabase::DestroyDatabase(path); 949 } 950 951 // static 952 int64 ObfuscatedFileUtil::ComputeFilePathCost(const base::FilePath& path) { 953 return UsageForPath(VirtualPath::BaseName(path).value().size()); 954 } 955 956 void ObfuscatedFileUtil::MaybePrepopulateDatabase( 957 const std::vector<std::string>& type_strings_to_prepopulate) { 958 SandboxPrioritizedOriginDatabase database(file_system_directory_); 959 std::string origin_string = database.GetPrimaryOrigin(); 960 if (origin_string.empty() || !database.HasOriginPath(origin_string)) 961 return; 962 const GURL origin = webkit_database::GetOriginFromIdentifier(origin_string); 963 964 // Prepopulate the directory database(s) if and only if this instance 965 // has primary origin and the directory database is already there. 966 for (size_t i = 0; i < type_strings_to_prepopulate.size(); ++i) { 967 const std::string type_string = type_strings_to_prepopulate[i]; 968 // Only handles known types. 969 if (!ContainsKey(known_type_strings_, type_string)) 970 continue; 971 PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; 972 base::FilePath path = GetDirectoryForOriginAndType( 973 origin, type_string, false, &error); 974 if (error != base::PLATFORM_FILE_OK) 975 continue; 976 scoped_ptr<SandboxDirectoryDatabase> db(new SandboxDirectoryDatabase(path)); 977 if (db->Init(SandboxDirectoryDatabase::FAIL_ON_CORRUPTION)) { 978 directories_[GetDirectoryDatabaseKey(origin, type_string)] = db.release(); 979 MarkUsed(); 980 // Don't populate more than one database, as it may rather hurt 981 // performance. 982 break; 983 } 984 } 985 } 986 987 base::FilePath ObfuscatedFileUtil::GetDirectoryForURL( 988 const FileSystemURL& url, 989 bool create, 990 base::PlatformFileError* error_code) { 991 return GetDirectoryForOriginAndType( 992 url.origin(), CallGetTypeStringForURL(url), create, error_code); 993 } 994 995 std::string ObfuscatedFileUtil::CallGetTypeStringForURL( 996 const FileSystemURL& url) { 997 DCHECK(!get_type_string_for_url_.is_null()); 998 return get_type_string_for_url_.Run(url); 999 } 1000 1001 PlatformFileError ObfuscatedFileUtil::GetFileInfoInternal( 1002 SandboxDirectoryDatabase* db, 1003 FileSystemOperationContext* context, 1004 const FileSystemURL& url, 1005 FileId file_id, 1006 FileInfo* local_info, 1007 base::PlatformFileInfo* file_info, 1008 base::FilePath* platform_file_path) { 1009 DCHECK(db); 1010 DCHECK(context); 1011 DCHECK(file_info); 1012 DCHECK(platform_file_path); 1013 1014 if (!db->GetFileInfo(file_id, local_info)) { 1015 NOTREACHED(); 1016 return base::PLATFORM_FILE_ERROR_FAILED; 1017 } 1018 1019 if (local_info->is_directory()) { 1020 file_info->size = 0; 1021 file_info->is_directory = true; 1022 file_info->is_symbolic_link = false; 1023 file_info->last_modified = local_info->modification_time; 1024 *platform_file_path = base::FilePath(); 1025 // We don't fill in ctime or atime. 1026 return base::PLATFORM_FILE_OK; 1027 } 1028 if (local_info->data_path.empty()) 1029 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION; 1030 base::FilePath local_path = DataPathToLocalPath(url, local_info->data_path); 1031 base::PlatformFileError error = NativeFileUtil::GetFileInfo( 1032 local_path, file_info); 1033 // We should not follow symbolic links in sandboxed file system. 1034 if (base::IsLink(local_path)) { 1035 LOG(WARNING) << "Found a symbolic file."; 1036 error = base::PLATFORM_FILE_ERROR_NOT_FOUND; 1037 } 1038 if (error == base::PLATFORM_FILE_OK) { 1039 *platform_file_path = local_path; 1040 } else if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) { 1041 LOG(WARNING) << "Lost a backing file."; 1042 InvalidateUsageCache(context, url.origin(), url.type()); 1043 if (!db->RemoveFileInfo(file_id)) 1044 return base::PLATFORM_FILE_ERROR_FAILED; 1045 } 1046 return error; 1047 } 1048 1049 PlatformFileError ObfuscatedFileUtil::CreateFile( 1050 FileSystemOperationContext* context, 1051 const base::FilePath& src_file_path, 1052 const FileSystemURL& dest_url, 1053 FileInfo* dest_file_info, int file_flags, PlatformFile* handle) { 1054 if (handle) 1055 *handle = base::kInvalidPlatformFileValue; 1056 SandboxDirectoryDatabase* db = GetDirectoryDatabase(dest_url, true); 1057 1058 PlatformFileError error = base::PLATFORM_FILE_OK; 1059 base::FilePath root = GetDirectoryForURL(dest_url, false, &error); 1060 if (error != base::PLATFORM_FILE_OK) 1061 return error; 1062 1063 base::FilePath dest_local_path; 1064 error = GenerateNewLocalPath(db, context, dest_url, &dest_local_path); 1065 if (error != base::PLATFORM_FILE_OK) 1066 return error; 1067 1068 bool created = false; 1069 if (!src_file_path.empty()) { 1070 DCHECK(!file_flags); 1071 DCHECK(!handle); 1072 error = NativeFileUtil::CopyOrMoveFile( 1073 src_file_path, dest_local_path, 1074 FileSystemOperation::OPTION_NONE, 1075 fileapi::NativeFileUtil::CopyOrMoveModeForDestination(dest_url, 1076 true /* copy */)); 1077 created = true; 1078 } else { 1079 if (base::PathExists(dest_local_path)) { 1080 if (!base::DeleteFile(dest_local_path, true /* recursive */)) { 1081 NOTREACHED(); 1082 return base::PLATFORM_FILE_ERROR_FAILED; 1083 } 1084 LOG(WARNING) << "A stray file detected"; 1085 InvalidateUsageCache(context, dest_url.origin(), dest_url.type()); 1086 } 1087 1088 if (handle) { 1089 error = NativeFileUtil::CreateOrOpen( 1090 dest_local_path, file_flags, handle, &created); 1091 // If this succeeds, we must close handle on any subsequent error. 1092 } else { 1093 DCHECK(!file_flags); // file_flags is only used by CreateOrOpen. 1094 error = NativeFileUtil::EnsureFileExists(dest_local_path, &created); 1095 } 1096 } 1097 if (error != base::PLATFORM_FILE_OK) 1098 return error; 1099 1100 if (!created) { 1101 NOTREACHED(); 1102 if (handle) { 1103 DCHECK_NE(base::kInvalidPlatformFileValue, *handle); 1104 base::ClosePlatformFile(*handle); 1105 base::DeleteFile(dest_local_path, false /* recursive */); 1106 *handle = base::kInvalidPlatformFileValue; 1107 } 1108 return base::PLATFORM_FILE_ERROR_FAILED; 1109 } 1110 1111 // This removes the root, including the trailing slash, leaving a relative 1112 // path. 1113 dest_file_info->data_path = base::FilePath( 1114 dest_local_path.value().substr(root.value().length() + 1)); 1115 1116 FileId file_id; 1117 error = db->AddFileInfo(*dest_file_info, &file_id); 1118 if (error != base::PLATFORM_FILE_OK) { 1119 if (handle) { 1120 DCHECK_NE(base::kInvalidPlatformFileValue, *handle); 1121 base::ClosePlatformFile(*handle); 1122 *handle = base::kInvalidPlatformFileValue; 1123 } 1124 base::DeleteFile(dest_local_path, false /* recursive */); 1125 return error; 1126 } 1127 TouchDirectory(db, dest_file_info->parent_id); 1128 1129 return base::PLATFORM_FILE_OK; 1130 } 1131 1132 base::FilePath ObfuscatedFileUtil::DataPathToLocalPath( 1133 const FileSystemURL& url, const base::FilePath& data_path) { 1134 PlatformFileError error = base::PLATFORM_FILE_OK; 1135 base::FilePath root = GetDirectoryForURL(url, false, &error); 1136 if (error != base::PLATFORM_FILE_OK) 1137 return base::FilePath(); 1138 return root.Append(data_path); 1139 } 1140 1141 std::string ObfuscatedFileUtil::GetDirectoryDatabaseKey( 1142 const GURL& origin, const std::string& type_string) { 1143 if (type_string.empty()) { 1144 LOG(WARNING) << "Unknown filesystem type requested:" << type_string; 1145 return std::string(); 1146 } 1147 // For isolated origin we just use a type string as a key. 1148 return webkit_database::GetIdentifierFromOrigin(origin) + 1149 type_string; 1150 } 1151 1152 // TODO(ericu): How to do the whole validation-without-creation thing? 1153 // We may not have quota even to create the database. 1154 // Ah, in that case don't even get here? 1155 // Still doesn't answer the quota issue, though. 1156 SandboxDirectoryDatabase* ObfuscatedFileUtil::GetDirectoryDatabase( 1157 const FileSystemURL& url, bool create) { 1158 std::string key = GetDirectoryDatabaseKey( 1159 url.origin(), CallGetTypeStringForURL(url)); 1160 if (key.empty()) 1161 return NULL; 1162 1163 DirectoryMap::iterator iter = directories_.find(key); 1164 if (iter != directories_.end()) { 1165 MarkUsed(); 1166 return iter->second; 1167 } 1168 1169 PlatformFileError error = base::PLATFORM_FILE_OK; 1170 base::FilePath path = GetDirectoryForURL(url, create, &error); 1171 if (error != base::PLATFORM_FILE_OK) { 1172 LOG(WARNING) << "Failed to get origin+type directory: " 1173 << url.DebugString() << " error:" << error; 1174 return NULL; 1175 } 1176 MarkUsed(); 1177 SandboxDirectoryDatabase* database = new SandboxDirectoryDatabase(path); 1178 directories_[key] = database; 1179 return database; 1180 } 1181 1182 base::FilePath ObfuscatedFileUtil::GetDirectoryForOrigin( 1183 const GURL& origin, bool create, base::PlatformFileError* error_code) { 1184 if (!InitOriginDatabase(origin, create)) { 1185 if (error_code) { 1186 *error_code = create ? 1187 base::PLATFORM_FILE_ERROR_FAILED : 1188 base::PLATFORM_FILE_ERROR_NOT_FOUND; 1189 } 1190 return base::FilePath(); 1191 } 1192 base::FilePath directory_name; 1193 std::string id = webkit_database::GetIdentifierFromOrigin(origin); 1194 1195 bool exists_in_db = origin_database_->HasOriginPath(id); 1196 if (!exists_in_db && !create) { 1197 if (error_code) 1198 *error_code = base::PLATFORM_FILE_ERROR_NOT_FOUND; 1199 return base::FilePath(); 1200 } 1201 if (!origin_database_->GetPathForOrigin(id, &directory_name)) { 1202 if (error_code) 1203 *error_code = base::PLATFORM_FILE_ERROR_FAILED; 1204 return base::FilePath(); 1205 } 1206 1207 base::FilePath path = file_system_directory_.Append(directory_name); 1208 bool exists_in_fs = base::DirectoryExists(path); 1209 if (!exists_in_db && exists_in_fs) { 1210 if (!base::DeleteFile(path, true)) { 1211 if (error_code) 1212 *error_code = base::PLATFORM_FILE_ERROR_FAILED; 1213 return base::FilePath(); 1214 } 1215 exists_in_fs = false; 1216 } 1217 1218 if (!exists_in_fs) { 1219 if (!create || !base::CreateDirectory(path)) { 1220 if (error_code) 1221 *error_code = create ? 1222 base::PLATFORM_FILE_ERROR_FAILED : 1223 base::PLATFORM_FILE_ERROR_NOT_FOUND; 1224 return base::FilePath(); 1225 } 1226 } 1227 1228 if (error_code) 1229 *error_code = base::PLATFORM_FILE_OK; 1230 1231 return path; 1232 } 1233 1234 void ObfuscatedFileUtil::InvalidateUsageCache( 1235 FileSystemOperationContext* context, 1236 const GURL& origin, 1237 FileSystemType type) { 1238 if (sandbox_delegate_) 1239 sandbox_delegate_->InvalidateUsageCache(origin, type); 1240 } 1241 1242 void ObfuscatedFileUtil::MarkUsed() { 1243 if (!timer_) 1244 timer_.reset(new TimedTaskHelper(file_task_runner_.get())); 1245 1246 if (timer_->IsRunning()) { 1247 timer_->Reset(); 1248 } else { 1249 timer_->Start(FROM_HERE, 1250 base::TimeDelta::FromSeconds(db_flush_delay_seconds_), 1251 base::Bind(&ObfuscatedFileUtil::DropDatabases, 1252 base::Unretained(this))); 1253 } 1254 } 1255 1256 void ObfuscatedFileUtil::DropDatabases() { 1257 origin_database_.reset(); 1258 STLDeleteContainerPairSecondPointers( 1259 directories_.begin(), directories_.end()); 1260 directories_.clear(); 1261 timer_.reset(); 1262 } 1263 1264 bool ObfuscatedFileUtil::InitOriginDatabase(const GURL& origin_hint, 1265 bool create) { 1266 if (origin_database_) 1267 return true; 1268 1269 if (!create && !base::DirectoryExists(file_system_directory_)) 1270 return false; 1271 if (!base::CreateDirectory(file_system_directory_)) { 1272 LOG(WARNING) << "Failed to create FileSystem directory: " << 1273 file_system_directory_.value(); 1274 return false; 1275 } 1276 1277 SandboxPrioritizedOriginDatabase* prioritized_origin_database = 1278 new SandboxPrioritizedOriginDatabase(file_system_directory_); 1279 origin_database_.reset(prioritized_origin_database); 1280 1281 if (origin_hint.is_empty() || !HasIsolatedStorage(origin_hint)) 1282 return true; 1283 1284 const std::string isolated_origin_string = 1285 webkit_database::GetIdentifierFromOrigin(origin_hint); 1286 1287 // TODO(kinuko): Deprecate this after a few release cycles, e.g. around M33. 1288 base::FilePath isolated_origin_dir = file_system_directory_.Append( 1289 SandboxIsolatedOriginDatabase::kObsoleteOriginDirectory); 1290 if (base::DirectoryExists(isolated_origin_dir) && 1291 prioritized_origin_database->GetSandboxOriginDatabase()) { 1292 SandboxIsolatedOriginDatabase::MigrateBackFromObsoleteOriginDatabase( 1293 isolated_origin_string, 1294 file_system_directory_, 1295 prioritized_origin_database->GetSandboxOriginDatabase()); 1296 } 1297 1298 prioritized_origin_database->InitializePrimaryOrigin( 1299 isolated_origin_string); 1300 1301 return true; 1302 } 1303 1304 PlatformFileError ObfuscatedFileUtil::GenerateNewLocalPath( 1305 SandboxDirectoryDatabase* db, 1306 FileSystemOperationContext* context, 1307 const FileSystemURL& url, 1308 base::FilePath* local_path) { 1309 DCHECK(local_path); 1310 int64 number; 1311 if (!db || !db->GetNextInteger(&number)) 1312 return base::PLATFORM_FILE_ERROR_FAILED; 1313 1314 PlatformFileError error = base::PLATFORM_FILE_OK; 1315 base::FilePath new_local_path = GetDirectoryForURL(url, false, &error); 1316 if (error != base::PLATFORM_FILE_OK) 1317 return base::PLATFORM_FILE_ERROR_FAILED; 1318 1319 // We use the third- and fourth-to-last digits as the directory. 1320 int64 directory_number = number % 10000 / 100; 1321 new_local_path = new_local_path.AppendASCII( 1322 base::StringPrintf("%02" PRId64, directory_number)); 1323 1324 error = NativeFileUtil::CreateDirectory( 1325 new_local_path, false /* exclusive */, false /* recursive */); 1326 if (error != base::PLATFORM_FILE_OK) 1327 return error; 1328 1329 *local_path = 1330 new_local_path.AppendASCII(base::StringPrintf("%08" PRId64, number)); 1331 return base::PLATFORM_FILE_OK; 1332 } 1333 1334 PlatformFileError ObfuscatedFileUtil::CreateOrOpenInternal( 1335 FileSystemOperationContext* context, 1336 const FileSystemURL& url, int file_flags, 1337 PlatformFile* file_handle, bool* created) { 1338 DCHECK(!(file_flags & (base::PLATFORM_FILE_DELETE_ON_CLOSE | 1339 base::PLATFORM_FILE_HIDDEN | base::PLATFORM_FILE_EXCLUSIVE_READ | 1340 base::PLATFORM_FILE_EXCLUSIVE_WRITE))); 1341 SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true); 1342 if (!db) 1343 return base::PLATFORM_FILE_ERROR_FAILED; 1344 FileId file_id; 1345 if (!db->GetFileWithPath(url.path(), &file_id)) { 1346 // The file doesn't exist. 1347 if (!(file_flags & (base::PLATFORM_FILE_CREATE | 1348 base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_OPEN_ALWAYS))) 1349 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 1350 FileId parent_id; 1351 if (!db->GetFileWithPath(VirtualPath::DirName(url.path()), 1352 &parent_id)) 1353 return base::PLATFORM_FILE_ERROR_NOT_FOUND; 1354 FileInfo file_info; 1355 InitFileInfo(&file_info, parent_id, 1356 VirtualPath::BaseName(url.path()).value()); 1357 1358 int64 growth = UsageForPath(file_info.name.size()); 1359 if (!AllocateQuota(context, growth)) 1360 return base::PLATFORM_FILE_ERROR_NO_SPACE; 1361 PlatformFileError error = CreateFile( 1362 context, base::FilePath(), 1363 url, &file_info, file_flags, file_handle); 1364 if (created && base::PLATFORM_FILE_OK == error) { 1365 *created = true; 1366 UpdateUsage(context, url, growth); 1367 context->change_observers()->Notify( 1368 &FileChangeObserver::OnCreateFile, MakeTuple(url)); 1369 } 1370 return error; 1371 } 1372 1373 if (file_flags & base::PLATFORM_FILE_CREATE) 1374 return base::PLATFORM_FILE_ERROR_EXISTS; 1375 1376 base::PlatformFileInfo platform_file_info; 1377 base::FilePath local_path; 1378 FileInfo file_info; 1379 base::PlatformFileError error = GetFileInfoInternal( 1380 db, context, url, file_id, &file_info, &platform_file_info, &local_path); 1381 if (error != base::PLATFORM_FILE_OK) 1382 return error; 1383 if (file_info.is_directory()) 1384 return base::PLATFORM_FILE_ERROR_NOT_A_FILE; 1385 1386 int64 delta = 0; 1387 if (file_flags & (base::PLATFORM_FILE_CREATE_ALWAYS | 1388 base::PLATFORM_FILE_OPEN_TRUNCATED)) { 1389 // The file exists and we're truncating. 1390 delta = -platform_file_info.size; 1391 AllocateQuota(context, delta); 1392 } 1393 1394 error = NativeFileUtil::CreateOrOpen( 1395 local_path, file_flags, file_handle, created); 1396 if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) { 1397 // TODO(tzik): Also invalidate on-memory usage cache in UsageTracker. 1398 // TODO(tzik): Delete database entry after ensuring the file lost. 1399 InvalidateUsageCache(context, url.origin(), url.type()); 1400 LOG(WARNING) << "Lost a backing file."; 1401 error = base::PLATFORM_FILE_ERROR_FAILED; 1402 } 1403 1404 // If truncating we need to update the usage. 1405 if (error == base::PLATFORM_FILE_OK && delta) { 1406 UpdateUsage(context, url, delta); 1407 context->change_observers()->Notify( 1408 &FileChangeObserver::OnModifyFile, MakeTuple(url)); 1409 } 1410 return error; 1411 } 1412 1413 bool ObfuscatedFileUtil::HasIsolatedStorage(const GURL& origin) { 1414 return special_storage_policy_.get() && 1415 special_storage_policy_->HasIsolatedStorage(origin); 1416 } 1417 1418 } // namespace fileapi 1419