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. |
321 if (valid_paths_to_keep.empty()) { | 331 if (valid_paths_to_keep.empty()) { |
322 file_util::Delete(root, true); | 332 file_util::Delete(root, true); |
323 return; | 333 return; |
334 } else { | |
Charlie Reis
2012/12/10 23:22:28
nit: Don't need the else, since we return early ab
awong
2012/12/11 01:18:32
Done.
| |
335 closure_runner->PostTask(FROM_HERE, on_gc_required); | |
324 } | 336 } |
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 asynchronously because after (1) completes, calling code can | |
Charlie Reis
2012/12/10 23:22:28
nit: asynchronous
Also, "calling code can safely c
awong
2012/12/11 01:18:32
Done.
| |
356 // continue to use the paths that had just been garbage collected without | |
357 // 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 void BlockingGarbageCollect( | |
Charlie Reis
2012/12/10 23:22:28
What does the "Blocking" part of the name refer to
awong
2012/12/11 01:18:32
Yeah, it's referring to the move to trash.
awong
2012/12/11 01:18:32
Yep. It's referring to the moves and the enumerati
| |
364 const FilePath& storage_root, | |
365 const scoped_refptr<base::TaskRunner>& file_access_runner, | |
366 scoped_ptr<base::hash_set<FilePath> > active_paths) { | |
367 CHECK(storage_root.IsAbsolute()); | |
368 | |
369 file_util::FileEnumerator enumerator(storage_root, false, kAllFileTypes); | |
370 FilePath trash_directory; | |
371 if (!file_util::CreateTemporaryDirInDir(storage_root, kTrashDirname, | |
372 &trash_directory)) { | |
373 // Unable to continue without creating the trash directory so give up. | |
374 return; | |
375 } | |
376 for (FilePath path = enumerator.Next(); !path.empty(); | |
377 path = enumerator.Next()) { | |
378 if (active_paths->find(path) == active_paths->end() && | |
379 path != trash_directory) { | |
380 // Since |trash_directory| is unique for each run of this function there | |
381 // can be no colllisions on the move. | |
382 file_util::Move(path, trash_directory.Append(path.BaseName())); | |
383 } | |
384 } | |
385 | |
386 file_access_runner->PostTask( | |
387 FROM_HERE, | |
388 base::Bind(base::IgnoreResult(&file_util::Delete), trash_directory, | |
389 true)); | |
390 } | |
391 | |
337 } // namespace | 392 } // namespace |
338 | 393 |
339 // static | 394 // static |
340 FilePath StoragePartitionImplMap::GetStoragePartitionPath( | 395 FilePath StoragePartitionImplMap::GetStoragePartitionPath( |
341 const std::string& partition_domain, | 396 const std::string& partition_domain, |
342 const std::string& partition_name) { | 397 const std::string& partition_name) { |
343 if (partition_domain.empty()) | 398 if (partition_domain.empty()) |
344 return FilePath(); | 399 return FilePath(); |
345 | 400 |
346 FilePath path = GetStoragePartitionDomainPath(partition_domain); | 401 FilePath path = GetStoragePartitionDomainPath(partition_domain); |
(...skipping 11 matching lines...) Expand all Loading... | |
358 return path.AppendASCII(base::HexEncode(buffer, sizeof(buffer))); | 413 return path.AppendASCII(base::HexEncode(buffer, sizeof(buffer))); |
359 } | 414 } |
360 | 415 |
361 return path.Append(kDefaultPartitionDirname); | 416 return path.Append(kDefaultPartitionDirname); |
362 } | 417 } |
363 | 418 |
364 StoragePartitionImplMap::StoragePartitionImplMap( | 419 StoragePartitionImplMap::StoragePartitionImplMap( |
365 BrowserContext* browser_context) | 420 BrowserContext* browser_context) |
366 : browser_context_(browser_context), | 421 : browser_context_(browser_context), |
367 resource_context_initialized_(false) { | 422 resource_context_initialized_(false) { |
423 // Doing here instead of initializer list cause it's just too ugly to read. | |
424 base::SequencedWorkerPool* blocking_pool = BrowserThread::GetBlockingPool(); | |
425 file_access_runner_ = | |
426 blocking_pool->GetSequencedTaskRunner(blocking_pool->GetSequenceToken()); | |
368 } | 427 } |
369 | 428 |
370 StoragePartitionImplMap::~StoragePartitionImplMap() { | 429 StoragePartitionImplMap::~StoragePartitionImplMap() { |
371 STLDeleteContainerPairSecondPointers(partitions_.begin(), | 430 STLDeleteContainerPairSecondPointers(partitions_.begin(), |
372 partitions_.end()); | 431 partitions_.end()); |
373 } | 432 } |
374 | 433 |
375 StoragePartitionImpl* StoragePartitionImplMap::Get( | 434 StoragePartitionImpl* StoragePartitionImplMap::Get( |
376 const std::string& partition_domain, | 435 const std::string& partition_domain, |
377 const std::string& partition_name, | 436 const std::string& partition_name, |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
411 partition_domain.empty() ? | 470 partition_domain.empty() ? |
412 browser_context_->GetMediaRequestContext() : | 471 browser_context_->GetMediaRequestContext() : |
413 browser_context_->GetMediaRequestContextForStoragePartition( | 472 browser_context_->GetMediaRequestContextForStoragePartition( |
414 partition->GetPath(), in_memory)); | 473 partition->GetPath(), in_memory)); |
415 | 474 |
416 PostCreateInitialization(partition, in_memory); | 475 PostCreateInitialization(partition, in_memory); |
417 | 476 |
418 return partition; | 477 return partition; |
419 } | 478 } |
420 | 479 |
421 void StoragePartitionImplMap::AsyncObliterate(const GURL& site) { | 480 void StoragePartitionImplMap::AsyncObliterate( |
481 const GURL& site, | |
482 const base::Closure& on_gc_required) { | |
422 // This method should avoid creating any StoragePartition (which would | 483 // This method should avoid creating any StoragePartition (which would |
423 // create more open file handles) so that it can delete as much of the | 484 // create more open file handles) so that it can delete as much of the |
424 // data off disk as possible. | 485 // data off disk as possible. |
425 std::string partition_domain; | 486 std::string partition_domain; |
426 std::string partition_name; | 487 std::string partition_name; |
427 bool in_memory = false; | 488 bool in_memory = false; |
428 GetContentClient()->browser()->GetStoragePartitionConfigForSite( | 489 GetContentClient()->browser()->GetStoragePartitionConfigForSite( |
429 browser_context_, site, false, &partition_domain, | 490 browser_context_, site, false, &partition_domain, |
430 &partition_name, &in_memory); | 491 &partition_name, &in_memory); |
431 | 492 |
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 | 493 // Find the active partitions for the domain. Because these partitions are |
437 // active, it is not possible to just delete the directories that contain | 494 // active, it is not possible to just delete the directories that contain |
438 // the backing data structures without causing the browser to crash. Instead, | 495 // the backing data structures without causing the browser to crash. Instead, |
439 // of deleteing the directory, we tell each storage context later to | 496 // of deleteing the directory, we tell each storage context later to |
440 // remove any data they have saved. This will leave the directory structure | 497 // remove any data they have saved. This will leave the directory structure |
441 // intact but it will only contain empty databases. | 498 // intact but it will only contain empty databases. |
442 std::vector<StoragePartitionImpl*> active_partitions; | 499 std::vector<StoragePartitionImpl*> active_partitions; |
443 std::vector<FilePath> paths_to_keep; | 500 std::vector<FilePath> paths_to_keep; |
444 for (PartitionMap::const_iterator it = partitions_.begin(); | 501 for (PartitionMap::const_iterator it = partitions_.begin(); |
445 it != partitions_.end(); | 502 it != partitions_.end(); |
446 ++it) { | 503 ++it) { |
447 const StoragePartitionConfig& config = it->first; | 504 const StoragePartitionConfig& config = it->first; |
448 if (config.partition_domain == partition_domain) { | 505 if (config.partition_domain == partition_domain) { |
449 it->second->AsyncClearAllData(); | 506 it->second->AsyncClearAllData(); |
450 if (!config.in_memory) { | 507 if (!config.in_memory) { |
451 paths_to_keep.push_back(it->second->GetPath()); | 508 paths_to_keep.push_back(it->second->GetPath()); |
452 } | 509 } |
453 } | 510 } |
454 } | 511 } |
455 | 512 |
456 // Start a best-effort delete of the on-disk storage excluding paths that are | 513 // 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 | 514 // 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 | 515 // StoragePartition state that just happens to not have been used during this |
459 // run of the browser. | 516 // run of the browser. |
460 FilePath domain_root = browser_context_->GetPath().Append( | 517 FilePath domain_root = browser_context_->GetPath().Append( |
461 GetStoragePartitionDomainPath(partition_domain)); | 518 GetStoragePartitionDomainPath(partition_domain)); |
462 base::WorkerPool::PostTask( | 519 |
520 // Never try to obliterate the browser context root. Die hard. | |
521 CHECK(domain_root != browser_context_->GetPath()) << site; | |
Charlie Reis
2012/12/10 23:22:28
Can we easily check that domain_root is within bro
awong
2012/12/11 01:18:32
Made this check more robust.
| |
522 | |
523 BrowserThread::PostBlockingPoolTask( | |
463 FROM_HERE, | 524 FROM_HERE, |
464 base::Bind(&BlockingObliteratePath, domain_root, paths_to_keep), | 525 base::Bind(&BlockingObliteratePath, domain_root, paths_to_keep, |
465 true); | 526 base::MessageLoopProxy::current(), on_gc_required)); |
527 } | |
466 | 528 |
467 // TODO(ajwong): Schedule a final AsyncObliterate of the whole directory on | 529 void StoragePartitionImplMap::GarbageCollect( |
468 // the next browser start. http://crbug.com/85127. | 530 scoped_ptr<base::hash_set<FilePath> > active_paths, |
531 const base::Closure& done) { | |
532 // Include all paths for current StoragePartitions in the active_paths since | |
533 // they cannot be deleted safely. | |
534 for (PartitionMap::const_iterator it = partitions_.begin(); | |
535 it != partitions_.end(); | |
536 ++it) { | |
537 const StoragePartitionConfig& config = it->first; | |
538 if (!config.in_memory) | |
539 active_paths->insert(it->second->GetPath()); | |
540 } | |
541 | |
542 // Find the directory holding the StoragePartitions and delete everything in | |
543 // there that isn't considered active. | |
544 FilePath storage_root = browser_context_->GetPath().Append( | |
545 GetStoragePartitionDomainPath(std::string())); | |
546 file_access_runner_->PostTaskAndReply( | |
547 FROM_HERE, | |
548 base::Bind(&BlockingGarbageCollect, storage_root, | |
549 file_access_runner_, | |
550 base::Passed(&active_paths)), | |
551 done); | |
469 } | 552 } |
470 | 553 |
471 void StoragePartitionImplMap::ForEach( | 554 void StoragePartitionImplMap::ForEach( |
472 const BrowserContext::StoragePartitionCallback& callback) { | 555 const BrowserContext::StoragePartitionCallback& callback) { |
473 for (PartitionMap::const_iterator it = partitions_.begin(); | 556 for (PartitionMap::const_iterator it = partitions_.begin(); |
474 it != partitions_.end(); | 557 it != partitions_.end(); |
475 ++it) { | 558 ++it) { |
476 callback.Run(it->second); | 559 callback.Run(it->second); |
477 } | 560 } |
478 } | 561 } |
(...skipping 27 matching lines...) Expand all Loading... | |
506 | 589 |
507 // We do not call InitializeURLRequestContext() for media contexts because, | 590 // We do not call InitializeURLRequestContext() for media contexts because, |
508 // other than the HTTP cache, the media contexts share the same backing | 591 // other than the HTTP cache, the media contexts share the same backing |
509 // objects as their associated "normal" request context. Thus, the previous | 592 // objects as their associated "normal" request context. Thus, the previous |
510 // call serves to initialize the media request context for this storage | 593 // call serves to initialize the media request context for this storage |
511 // partition as well. | 594 // partition as well. |
512 } | 595 } |
513 } | 596 } |
514 | 597 |
515 } // namespace content | 598 } // namespace content |
OLD | NEW |