OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/storage_partition_impl_map.h" | 5 #include "content/browser/storage_partition_impl_map.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/callback.h" | 8 #include "base/callback.h" |
9 #include "base/file_path.h" | 9 #include "base/file_path.h" |
10 #include "base/file_util.h" | 10 #include "base/file_util.h" |
11 #include "base/stl_util.h" | 11 #include "base/stl_util.h" |
| 12 #include "base/string_number_conversions.h" |
12 #include "base/string_util.h" | 13 #include "base/string_util.h" |
13 #include "base/string_number_conversions.h" | 14 #include "base/stringprintf.h" |
14 #include "base/threading/worker_pool.h" | 15 #include "base/threading/sequenced_worker_pool.h" |
15 #include "content/browser/appcache/chrome_appcache_service.h" | 16 #include "content/browser/appcache/chrome_appcache_service.h" |
16 #include "content/browser/fileapi/browser_file_system_helper.h" | 17 #include "content/browser/fileapi/browser_file_system_helper.h" |
17 #include "content/browser/fileapi/chrome_blob_storage_context.h" | 18 #include "content/browser/fileapi/chrome_blob_storage_context.h" |
18 #include "content/browser/histogram_internals_request_job.h" | 19 #include "content/browser/histogram_internals_request_job.h" |
19 #include "content/browser/loader/resource_request_info_impl.h" | 20 #include "content/browser/loader/resource_request_info_impl.h" |
20 #include "content/browser/net/view_blob_internals_job_factory.h" | 21 #include "content/browser/net/view_blob_internals_job_factory.h" |
21 #include "content/browser/net/view_http_cache_job_factory.h" | 22 #include "content/browser/net/view_http_cache_job_factory.h" |
22 #include "content/browser/resource_context_impl.h" | 23 #include "content/browser/resource_context_impl.h" |
23 #include "content/browser/storage_partition_impl.h" | 24 #include "content/browser/storage_partition_impl.h" |
24 #include "content/browser/tcmalloc_internals_request_job.h" | 25 #include "content/browser/tcmalloc_internals_request_job.h" |
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
203 // | 204 // |
204 // The code in GetStoragePartitionPath() constructs these path names. | 205 // The code in GetStoragePartitionPath() constructs these path names. |
205 // | 206 // |
206 // TODO(nasko): Move extension related path code out of content. | 207 // TODO(nasko): Move extension related path code out of content. |
207 const FilePath::CharType kStoragePartitionDirname[] = | 208 const FilePath::CharType kStoragePartitionDirname[] = |
208 FILE_PATH_LITERAL("Storage"); | 209 FILE_PATH_LITERAL("Storage"); |
209 const FilePath::CharType kExtensionsDirname[] = | 210 const FilePath::CharType kExtensionsDirname[] = |
210 FILE_PATH_LITERAL("ext"); | 211 FILE_PATH_LITERAL("ext"); |
211 const FilePath::CharType kDefaultPartitionDirname[] = | 212 const FilePath::CharType kDefaultPartitionDirname[] = |
212 FILE_PATH_LITERAL("def"); | 213 FILE_PATH_LITERAL("def"); |
| 214 const FilePath::CharType kTrashDirname[] = |
| 215 FILE_PATH_LITERAL("trash"); |
213 | 216 |
214 // Because partition names are user specified, they can be arbitrarily long | 217 // Because partition names are user specified, they can be arbitrarily long |
215 // which makes them unsuitable for paths names. We use a truncation of a | 218 // which makes them unsuitable for paths names. We use a truncation of a |
216 // SHA256 hash to perform a deterministic shortening of the string. The | 219 // SHA256 hash to perform a deterministic shortening of the string. The |
217 // kPartitionNameHashBytes constant controls the length of the truncation. | 220 // kPartitionNameHashBytes constant controls the length of the truncation. |
218 // We use 6 bytes, which gives us 99.999% reliability against collisions over | 221 // We use 6 bytes, which gives us 99.999% reliability against collisions over |
219 // 1 million partition domains. | 222 // 1 million partition domains. |
220 // | 223 // |
221 // Analysis: | 224 // Analysis: |
222 // We assume that all partition names within one partition domain are | 225 // We assume that all partition names within one partition domain are |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
260 } | 263 } |
261 | 264 |
262 // Helper function for doing a depth-first deletion of the data on disk. | 265 // Helper function for doing a depth-first deletion of the data on disk. |
263 // Examines paths directly in |current_dir| (no recursion) and tries to | 266 // Examines paths directly in |current_dir| (no recursion) and tries to |
264 // delete from disk anything that is in, or isn't a parent of something in | 267 // delete from disk anything that is in, or isn't a parent of something in |
265 // |paths_to_keep|. Paths that need further expansion are added to | 268 // |paths_to_keep|. Paths that need further expansion are added to |
266 // |paths_to_consider|. | 269 // |paths_to_consider|. |
267 void ObliterateOneDirectory(const FilePath& current_dir, | 270 void ObliterateOneDirectory(const FilePath& current_dir, |
268 const std::vector<FilePath>& paths_to_keep, | 271 const std::vector<FilePath>& paths_to_keep, |
269 std::vector<FilePath>* paths_to_consider) { | 272 std::vector<FilePath>* paths_to_consider) { |
| 273 CHECK(current_dir.IsAbsolute()); |
| 274 |
270 file_util::FileEnumerator enumerator(current_dir, false, kAllFileTypes); | 275 file_util::FileEnumerator enumerator(current_dir, false, kAllFileTypes); |
271 for (FilePath to_delete = enumerator.Next(); !to_delete.empty(); | 276 for (FilePath to_delete = enumerator.Next(); !to_delete.empty(); |
272 to_delete = enumerator.Next()) { | 277 to_delete = enumerator.Next()) { |
273 // Enum tracking which of the 3 possible actions to take for |to_delete|. | 278 // Enum tracking which of the 3 possible actions to take for |to_delete|. |
274 enum { kSkip, kEnqueue, kDelete } action = kDelete; | 279 enum { kSkip, kEnqueue, kDelete } action = kDelete; |
275 | 280 |
276 for (std::vector<FilePath>::const_iterator to_keep = paths_to_keep.begin(); | 281 for (std::vector<FilePath>::const_iterator to_keep = paths_to_keep.begin(); |
277 to_keep != paths_to_keep.end(); | 282 to_keep != paths_to_keep.end(); |
278 ++to_keep) { | 283 ++to_keep) { |
279 if (to_delete == *to_keep) { | 284 if (to_delete == *to_keep) { |
(...skipping 18 matching lines...) Expand all Loading... |
298 | 303 |
299 case kSkip: | 304 case kSkip: |
300 break; | 305 break; |
301 } | 306 } |
302 } | 307 } |
303 } | 308 } |
304 | 309 |
305 // Synchronously attempts to delete |root|, preserving only entries in | 310 // Synchronously attempts to delete |root|, preserving only entries in |
306 // |paths_to_keep|. If there are no entries in |paths_to_keep| on disk, then it | 311 // |paths_to_keep|. If there are no entries in |paths_to_keep| on disk, then it |
307 // completely removes |root|. All paths must be absolute paths. | 312 // completely removes |root|. All paths must be absolute paths. |
308 void BlockingObliteratePath(const FilePath& root, | 313 void BlockingObliteratePath( |
309 const std::vector<FilePath>& paths_to_keep) { | 314 const FilePath& root, |
| 315 const std::vector<FilePath>& paths_to_keep, |
| 316 const scoped_refptr<base::TaskRunner>& closure_runner, |
| 317 const base::Closure& on_gc_required) { |
| 318 CHECK(root.IsAbsolute()); |
| 319 |
310 // Reduce |paths_to_keep| set to those under the root and actually on disk. | 320 // Reduce |paths_to_keep| set to those under the root and actually on disk. |
311 std::vector<FilePath> valid_paths_to_keep; | 321 std::vector<FilePath> valid_paths_to_keep; |
312 for (std::vector<FilePath>::const_iterator it = paths_to_keep.begin(); | 322 for (std::vector<FilePath>::const_iterator it = paths_to_keep.begin(); |
313 it != paths_to_keep.end(); | 323 it != paths_to_keep.end(); |
314 ++it) { | 324 ++it) { |
315 if (root.IsParent(*it) && file_util::PathExists(*it)) | 325 if (root.IsParent(*it) && file_util::PathExists(*it)) |
316 valid_paths_to_keep.push_back(*it); | 326 valid_paths_to_keep.push_back(*it); |
317 } | 327 } |
318 | 328 |
319 // If none of the |paths_to_keep| are valid anymore then we just whack the | 329 // If none of the |paths_to_keep| are valid anymore then we just whack the |
320 // root and be done with it. | 330 // root and be done with it. Otherwise, signal garbage collection and do |
| 331 // a best-effort delete of the on-disk structures. |
321 if (valid_paths_to_keep.empty()) { | 332 if (valid_paths_to_keep.empty()) { |
322 file_util::Delete(root, true); | 333 file_util::Delete(root, true); |
323 return; | 334 return; |
324 } | 335 } |
| 336 closure_runner->PostTask(FROM_HERE, on_gc_required); |
325 | 337 |
326 // Otherwise, start at the root and delete everything that is not in | 338 // Otherwise, start at the root and delete everything that is not in |
327 // |valid_paths_to_keep|. | 339 // |valid_paths_to_keep|. |
328 std::vector<FilePath> paths_to_consider; | 340 std::vector<FilePath> paths_to_consider; |
329 paths_to_consider.push_back(root); | 341 paths_to_consider.push_back(root); |
330 while(!paths_to_consider.empty()) { | 342 while(!paths_to_consider.empty()) { |
331 FilePath path = paths_to_consider.back(); | 343 FilePath path = paths_to_consider.back(); |
332 paths_to_consider.pop_back(); | 344 paths_to_consider.pop_back(); |
333 ObliterateOneDirectory(path, valid_paths_to_keep, &paths_to_consider); | 345 ObliterateOneDirectory(path, valid_paths_to_keep, &paths_to_consider); |
334 } | 346 } |
335 } | 347 } |
336 | 348 |
| 349 // Deletes all entries inside the |storage_root| that are not in the |
| 350 // |active_paths|. Deletion is done in 2 steps: |
| 351 // |
| 352 // (1) Moving all garbage collected paths into a trash directory. |
| 353 // (2) Asynchronously deleting the trash directory. |
| 354 // |
| 355 // The deletion is asynchronous because after (1) completes, calling code can |
| 356 // safely continue to use the paths that had just been garbage collected |
| 357 // without fear of race conditions. |
| 358 // |
| 359 // This code also ignores failed moves rather than attempting a smarter retry. |
| 360 // Moves shouldn't fail here unless there is some out-of-band error (eg., |
| 361 // FS corruption). Retry logic is dangerous in the general case because |
| 362 // there is not necessarily a guaranteed case where the logic may succeed. |
| 363 // |
| 364 // This function is still named BlockingGarbageCollect() because it does |
| 365 // execute a few filesystem operations synchronously. |
| 366 void BlockingGarbageCollect( |
| 367 const FilePath& storage_root, |
| 368 const scoped_refptr<base::TaskRunner>& file_access_runner, |
| 369 scoped_ptr<base::hash_set<FilePath> > active_paths) { |
| 370 CHECK(storage_root.IsAbsolute()); |
| 371 |
| 372 file_util::FileEnumerator enumerator(storage_root, false, kAllFileTypes); |
| 373 FilePath trash_directory; |
| 374 if (!file_util::CreateTemporaryDirInDir(storage_root, kTrashDirname, |
| 375 &trash_directory)) { |
| 376 // Unable to continue without creating the trash directory so give up. |
| 377 return; |
| 378 } |
| 379 for (FilePath path = enumerator.Next(); !path.empty(); |
| 380 path = enumerator.Next()) { |
| 381 if (active_paths->find(path) == active_paths->end() && |
| 382 path != trash_directory) { |
| 383 // Since |trash_directory| is unique for each run of this function there |
| 384 // can be no colllisions on the move. |
| 385 file_util::Move(path, trash_directory.Append(path.BaseName())); |
| 386 } |
| 387 } |
| 388 |
| 389 file_access_runner->PostTask( |
| 390 FROM_HERE, |
| 391 base::Bind(base::IgnoreResult(&file_util::Delete), trash_directory, |
| 392 true)); |
| 393 } |
| 394 |
337 } // namespace | 395 } // namespace |
338 | 396 |
339 // static | 397 // static |
340 FilePath StoragePartitionImplMap::GetStoragePartitionPath( | 398 FilePath StoragePartitionImplMap::GetStoragePartitionPath( |
341 const std::string& partition_domain, | 399 const std::string& partition_domain, |
342 const std::string& partition_name) { | 400 const std::string& partition_name) { |
343 if (partition_domain.empty()) | 401 if (partition_domain.empty()) |
344 return FilePath(); | 402 return FilePath(); |
345 | 403 |
346 FilePath path = GetStoragePartitionDomainPath(partition_domain); | 404 FilePath path = GetStoragePartitionDomainPath(partition_domain); |
(...skipping 11 matching lines...) Expand all Loading... |
358 return path.AppendASCII(base::HexEncode(buffer, sizeof(buffer))); | 416 return path.AppendASCII(base::HexEncode(buffer, sizeof(buffer))); |
359 } | 417 } |
360 | 418 |
361 return path.Append(kDefaultPartitionDirname); | 419 return path.Append(kDefaultPartitionDirname); |
362 } | 420 } |
363 | 421 |
364 StoragePartitionImplMap::StoragePartitionImplMap( | 422 StoragePartitionImplMap::StoragePartitionImplMap( |
365 BrowserContext* browser_context) | 423 BrowserContext* browser_context) |
366 : browser_context_(browser_context), | 424 : browser_context_(browser_context), |
367 resource_context_initialized_(false) { | 425 resource_context_initialized_(false) { |
| 426 // Doing here instead of initializer list cause it's just too ugly to read. |
| 427 base::SequencedWorkerPool* blocking_pool = BrowserThread::GetBlockingPool(); |
| 428 file_access_runner_ = |
| 429 blocking_pool->GetSequencedTaskRunner(blocking_pool->GetSequenceToken()); |
368 } | 430 } |
369 | 431 |
370 StoragePartitionImplMap::~StoragePartitionImplMap() { | 432 StoragePartitionImplMap::~StoragePartitionImplMap() { |
371 STLDeleteContainerPairSecondPointers(partitions_.begin(), | 433 STLDeleteContainerPairSecondPointers(partitions_.begin(), |
372 partitions_.end()); | 434 partitions_.end()); |
373 } | 435 } |
374 | 436 |
375 StoragePartitionImpl* StoragePartitionImplMap::Get( | 437 StoragePartitionImpl* StoragePartitionImplMap::Get( |
376 const std::string& partition_domain, | 438 const std::string& partition_domain, |
377 const std::string& partition_name, | 439 const std::string& partition_name, |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
411 partition_domain.empty() ? | 473 partition_domain.empty() ? |
412 browser_context_->GetMediaRequestContext() : | 474 browser_context_->GetMediaRequestContext() : |
413 browser_context_->GetMediaRequestContextForStoragePartition( | 475 browser_context_->GetMediaRequestContextForStoragePartition( |
414 partition->GetPath(), in_memory)); | 476 partition->GetPath(), in_memory)); |
415 | 477 |
416 PostCreateInitialization(partition, in_memory); | 478 PostCreateInitialization(partition, in_memory); |
417 | 479 |
418 return partition; | 480 return partition; |
419 } | 481 } |
420 | 482 |
421 void StoragePartitionImplMap::AsyncObliterate(const GURL& site) { | 483 void StoragePartitionImplMap::AsyncObliterate( |
| 484 const GURL& site, |
| 485 const base::Closure& on_gc_required) { |
422 // This method should avoid creating any StoragePartition (which would | 486 // This method should avoid creating any StoragePartition (which would |
423 // create more open file handles) so that it can delete as much of the | 487 // create more open file handles) so that it can delete as much of the |
424 // data off disk as possible. | 488 // data off disk as possible. |
425 std::string partition_domain; | 489 std::string partition_domain; |
426 std::string partition_name; | 490 std::string partition_name; |
427 bool in_memory = false; | 491 bool in_memory = false; |
428 GetContentClient()->browser()->GetStoragePartitionConfigForSite( | 492 GetContentClient()->browser()->GetStoragePartitionConfigForSite( |
429 browser_context_, site, false, &partition_domain, | 493 browser_context_, site, false, &partition_domain, |
430 &partition_name, &in_memory); | 494 &partition_name, &in_memory); |
431 | 495 |
432 // The default partition is the whole profile. We can't obliterate that and | |
433 // should never even try. | |
434 CHECK(!partition_domain.empty()) << site; | |
435 | |
436 // Find the active partitions for the domain. Because these partitions are | 496 // Find the active partitions for the domain. Because these partitions are |
437 // active, it is not possible to just delete the directories that contain | 497 // active, it is not possible to just delete the directories that contain |
438 // the backing data structures without causing the browser to crash. Instead, | 498 // the backing data structures without causing the browser to crash. Instead, |
439 // of deleteing the directory, we tell each storage context later to | 499 // of deleteing the directory, we tell each storage context later to |
440 // remove any data they have saved. This will leave the directory structure | 500 // remove any data they have saved. This will leave the directory structure |
441 // intact but it will only contain empty databases. | 501 // intact but it will only contain empty databases. |
442 std::vector<StoragePartitionImpl*> active_partitions; | 502 std::vector<StoragePartitionImpl*> active_partitions; |
443 std::vector<FilePath> paths_to_keep; | 503 std::vector<FilePath> paths_to_keep; |
444 for (PartitionMap::const_iterator it = partitions_.begin(); | 504 for (PartitionMap::const_iterator it = partitions_.begin(); |
445 it != partitions_.end(); | 505 it != partitions_.end(); |
446 ++it) { | 506 ++it) { |
447 const StoragePartitionConfig& config = it->first; | 507 const StoragePartitionConfig& config = it->first; |
448 if (config.partition_domain == partition_domain) { | 508 if (config.partition_domain == partition_domain) { |
449 it->second->AsyncClearAllData(); | 509 it->second->AsyncClearAllData(); |
450 if (!config.in_memory) { | 510 if (!config.in_memory) { |
451 paths_to_keep.push_back(it->second->GetPath()); | 511 paths_to_keep.push_back(it->second->GetPath()); |
452 } | 512 } |
453 } | 513 } |
454 } | 514 } |
455 | 515 |
456 // Start a best-effort delete of the on-disk storage excluding paths that are | 516 // Start a best-effort delete of the on-disk storage excluding paths that are |
457 // known to still be in use. This is to delete any previously created | 517 // known to still be in use. This is to delete any previously created |
458 // StoragePartition state that just happens to not have been used during this | 518 // StoragePartition state that just happens to not have been used during this |
459 // run of the browser. | 519 // run of the browser. |
460 FilePath domain_root = browser_context_->GetPath().Append( | 520 FilePath domain_root = browser_context_->GetPath().Append( |
461 GetStoragePartitionDomainPath(partition_domain)); | 521 GetStoragePartitionDomainPath(partition_domain)); |
462 base::WorkerPool::PostTask( | 522 |
| 523 // Early exit required because file_util::ContainsPath() will fail on POSIX |
| 524 // if |domain_root| does not exist. |
| 525 if (!file_util::PathExists(domain_root)) { |
| 526 return; |
| 527 } |
| 528 |
| 529 // Never try to obliterate things outside of the browser context root or the |
| 530 // browser context root itself. Die hard. |
| 531 FilePath browser_context_root = browser_context_->GetPath(); |
| 532 CHECK(file_util::AbsolutePath(&domain_root)); |
| 533 CHECK(file_util::AbsolutePath(&browser_context_root)); |
| 534 CHECK(file_util::ContainsPath(browser_context_root, domain_root) && |
| 535 browser_context_root != domain_root) << site; |
| 536 |
| 537 BrowserThread::PostBlockingPoolTask( |
463 FROM_HERE, | 538 FROM_HERE, |
464 base::Bind(&BlockingObliteratePath, domain_root, paths_to_keep), | 539 base::Bind(&BlockingObliteratePath, domain_root, paths_to_keep, |
465 true); | 540 base::MessageLoopProxy::current(), on_gc_required)); |
| 541 } |
466 | 542 |
467 // TODO(ajwong): Schedule a final AsyncObliterate of the whole directory on | 543 void StoragePartitionImplMap::GarbageCollect( |
468 // the next browser start. http://crbug.com/85127. | 544 scoped_ptr<base::hash_set<FilePath> > active_paths, |
| 545 const base::Closure& done) { |
| 546 // Include all paths for current StoragePartitions in the active_paths since |
| 547 // they cannot be deleted safely. |
| 548 for (PartitionMap::const_iterator it = partitions_.begin(); |
| 549 it != partitions_.end(); |
| 550 ++it) { |
| 551 const StoragePartitionConfig& config = it->first; |
| 552 if (!config.in_memory) |
| 553 active_paths->insert(it->second->GetPath()); |
| 554 } |
| 555 |
| 556 // Find the directory holding the StoragePartitions and delete everything in |
| 557 // there that isn't considered active. |
| 558 FilePath storage_root = browser_context_->GetPath().Append( |
| 559 GetStoragePartitionDomainPath(std::string())); |
| 560 file_access_runner_->PostTaskAndReply( |
| 561 FROM_HERE, |
| 562 base::Bind(&BlockingGarbageCollect, storage_root, |
| 563 file_access_runner_, |
| 564 base::Passed(&active_paths)), |
| 565 done); |
469 } | 566 } |
470 | 567 |
471 void StoragePartitionImplMap::ForEach( | 568 void StoragePartitionImplMap::ForEach( |
472 const BrowserContext::StoragePartitionCallback& callback) { | 569 const BrowserContext::StoragePartitionCallback& callback) { |
473 for (PartitionMap::const_iterator it = partitions_.begin(); | 570 for (PartitionMap::const_iterator it = partitions_.begin(); |
474 it != partitions_.end(); | 571 it != partitions_.end(); |
475 ++it) { | 572 ++it) { |
476 callback.Run(it->second); | 573 callback.Run(it->second); |
477 } | 574 } |
478 } | 575 } |
(...skipping 27 matching lines...) Expand all Loading... |
506 | 603 |
507 // We do not call InitializeURLRequestContext() for media contexts because, | 604 // We do not call InitializeURLRequestContext() for media contexts because, |
508 // other than the HTTP cache, the media contexts share the same backing | 605 // other than the HTTP cache, the media contexts share the same backing |
509 // objects as their associated "normal" request context. Thus, the previous | 606 // objects as their associated "normal" request context. Thus, the previous |
510 // call serves to initialize the media request context for this storage | 607 // call serves to initialize the media request context for this storage |
511 // partition as well. | 608 // partition as well. |
512 } | 609 } |
513 } | 610 } |
514 | 611 |
515 } // namespace content | 612 } // namespace content |
OLD | NEW |