| Index: content/browser/storage_partition_impl_map.cc
|
| diff --git a/content/browser/storage_partition_impl_map.cc b/content/browser/storage_partition_impl_map.cc
|
| index 06f3dc432c7b73310544acca214c5fa1f66d2c58..12768ebc7e91a544efbf9ea6523dfa9c667675d4 100644
|
| --- a/content/browser/storage_partition_impl_map.cc
|
| +++ b/content/browser/storage_partition_impl_map.cc
|
| @@ -9,9 +9,10 @@
|
| #include "base/file_path.h"
|
| #include "base/file_util.h"
|
| #include "base/stl_util.h"
|
| -#include "base/string_util.h"
|
| #include "base/string_number_conversions.h"
|
| -#include "base/threading/worker_pool.h"
|
| +#include "base/string_util.h"
|
| +#include "base/stringprintf.h"
|
| +#include "base/threading/sequenced_worker_pool.h"
|
| #include "content/browser/appcache/chrome_appcache_service.h"
|
| #include "content/browser/fileapi/browser_file_system_helper.h"
|
| #include "content/browser/fileapi/chrome_blob_storage_context.h"
|
| @@ -210,6 +211,8 @@ const FilePath::CharType kExtensionsDirname[] =
|
| FILE_PATH_LITERAL("ext");
|
| const FilePath::CharType kDefaultPartitionDirname[] =
|
| FILE_PATH_LITERAL("def");
|
| +const FilePath::CharType kTrashDirname[] =
|
| + FILE_PATH_LITERAL("trash");
|
|
|
| // Because partition names are user specified, they can be arbitrarily long
|
| // which makes them unsuitable for paths names. We use a truncation of a
|
| @@ -267,6 +270,8 @@ FilePath GetStoragePartitionDomainPath(
|
| void ObliterateOneDirectory(const FilePath& current_dir,
|
| const std::vector<FilePath>& paths_to_keep,
|
| std::vector<FilePath>* paths_to_consider) {
|
| + CHECK(current_dir.IsAbsolute());
|
| +
|
| file_util::FileEnumerator enumerator(current_dir, false, kAllFileTypes);
|
| for (FilePath to_delete = enumerator.Next(); !to_delete.empty();
|
| to_delete = enumerator.Next()) {
|
| @@ -305,8 +310,13 @@ void ObliterateOneDirectory(const FilePath& current_dir,
|
| // Synchronously attempts to delete |root|, preserving only entries in
|
| // |paths_to_keep|. If there are no entries in |paths_to_keep| on disk, then it
|
| // completely removes |root|. All paths must be absolute paths.
|
| -void BlockingObliteratePath(const FilePath& root,
|
| - const std::vector<FilePath>& paths_to_keep) {
|
| +void BlockingObliteratePath(
|
| + const FilePath& root,
|
| + const std::vector<FilePath>& paths_to_keep,
|
| + const scoped_refptr<base::TaskRunner>& closure_runner,
|
| + const base::Closure& on_gc_required) {
|
| + CHECK(root.IsAbsolute());
|
| +
|
| // Reduce |paths_to_keep| set to those under the root and actually on disk.
|
| std::vector<FilePath> valid_paths_to_keep;
|
| for (std::vector<FilePath>::const_iterator it = paths_to_keep.begin();
|
| @@ -317,11 +327,13 @@ void BlockingObliteratePath(const FilePath& root,
|
| }
|
|
|
| // If none of the |paths_to_keep| are valid anymore then we just whack the
|
| - // root and be done with it.
|
| + // root and be done with it. Otherwise, signal garbage collection and do
|
| + // a best-effort delete of the on-disk structures.
|
| if (valid_paths_to_keep.empty()) {
|
| file_util::Delete(root, true);
|
| return;
|
| }
|
| + closure_runner->PostTask(FROM_HERE, on_gc_required);
|
|
|
| // Otherwise, start at the root and delete everything that is not in
|
| // |valid_paths_to_keep|.
|
| @@ -334,6 +346,52 @@ void BlockingObliteratePath(const FilePath& root,
|
| }
|
| }
|
|
|
| +// Deletes all entries inside the |storage_root| that are not in the
|
| +// |active_paths|. Deletion is done in 2 steps:
|
| +//
|
| +// (1) Moving all garbage collected paths into a trash directory.
|
| +// (2) Asynchronously deleting the trash directory.
|
| +//
|
| +// The deletion is asynchronous because after (1) completes, calling code can
|
| +// safely continue to use the paths that had just been garbage collected
|
| +// without fear of race conditions.
|
| +//
|
| +// This code also ignores failed moves rather than attempting a smarter retry.
|
| +// Moves shouldn't fail here unless there is some out-of-band error (eg.,
|
| +// FS corruption). Retry logic is dangerous in the general case because
|
| +// there is not necessarily a guaranteed case where the logic may succeed.
|
| +//
|
| +// This function is still named BlockingGarbageCollect() because it does
|
| +// execute a few filesystem operations synchronously.
|
| +void BlockingGarbageCollect(
|
| + const FilePath& storage_root,
|
| + const scoped_refptr<base::TaskRunner>& file_access_runner,
|
| + scoped_ptr<base::hash_set<FilePath> > active_paths) {
|
| + CHECK(storage_root.IsAbsolute());
|
| +
|
| + file_util::FileEnumerator enumerator(storage_root, false, kAllFileTypes);
|
| + FilePath trash_directory;
|
| + if (!file_util::CreateTemporaryDirInDir(storage_root, kTrashDirname,
|
| + &trash_directory)) {
|
| + // Unable to continue without creating the trash directory so give up.
|
| + return;
|
| + }
|
| + for (FilePath path = enumerator.Next(); !path.empty();
|
| + path = enumerator.Next()) {
|
| + if (active_paths->find(path) == active_paths->end() &&
|
| + path != trash_directory) {
|
| + // Since |trash_directory| is unique for each run of this function there
|
| + // can be no colllisions on the move.
|
| + file_util::Move(path, trash_directory.Append(path.BaseName()));
|
| + }
|
| + }
|
| +
|
| + file_access_runner->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(base::IgnoreResult(&file_util::Delete), trash_directory,
|
| + true));
|
| +}
|
| +
|
| } // namespace
|
|
|
| // static
|
| @@ -365,6 +423,10 @@ StoragePartitionImplMap::StoragePartitionImplMap(
|
| BrowserContext* browser_context)
|
| : browser_context_(browser_context),
|
| resource_context_initialized_(false) {
|
| + // Doing here instead of initializer list cause it's just too ugly to read.
|
| + base::SequencedWorkerPool* blocking_pool = BrowserThread::GetBlockingPool();
|
| + file_access_runner_ =
|
| + blocking_pool->GetSequencedTaskRunner(blocking_pool->GetSequenceToken());
|
| }
|
|
|
| StoragePartitionImplMap::~StoragePartitionImplMap() {
|
| @@ -418,7 +480,9 @@ StoragePartitionImpl* StoragePartitionImplMap::Get(
|
| return partition;
|
| }
|
|
|
| -void StoragePartitionImplMap::AsyncObliterate(const GURL& site) {
|
| +void StoragePartitionImplMap::AsyncObliterate(
|
| + const GURL& site,
|
| + const base::Closure& on_gc_required) {
|
| // This method should avoid creating any StoragePartition (which would
|
| // create more open file handles) so that it can delete as much of the
|
| // data off disk as possible.
|
| @@ -429,10 +493,6 @@ void StoragePartitionImplMap::AsyncObliterate(const GURL& site) {
|
| browser_context_, site, false, &partition_domain,
|
| &partition_name, &in_memory);
|
|
|
| - // The default partition is the whole profile. We can't obliterate that and
|
| - // should never even try.
|
| - CHECK(!partition_domain.empty()) << site;
|
| -
|
| // Find the active partitions for the domain. Because these partitions are
|
| // active, it is not possible to just delete the directories that contain
|
| // the backing data structures without causing the browser to crash. Instead,
|
| @@ -459,13 +519,50 @@ void StoragePartitionImplMap::AsyncObliterate(const GURL& site) {
|
| // run of the browser.
|
| FilePath domain_root = browser_context_->GetPath().Append(
|
| GetStoragePartitionDomainPath(partition_domain));
|
| - base::WorkerPool::PostTask(
|
| +
|
| + // Early exit required because file_util::ContainsPath() will fail on POSIX
|
| + // if |domain_root| does not exist.
|
| + if (!file_util::PathExists(domain_root)) {
|
| + return;
|
| + }
|
| +
|
| + // Never try to obliterate things outside of the browser context root or the
|
| + // browser context root itself. Die hard.
|
| + FilePath browser_context_root = browser_context_->GetPath();
|
| + CHECK(file_util::AbsolutePath(&domain_root));
|
| + CHECK(file_util::AbsolutePath(&browser_context_root));
|
| + CHECK(file_util::ContainsPath(browser_context_root, domain_root) &&
|
| + browser_context_root != domain_root) << site;
|
| +
|
| + BrowserThread::PostBlockingPoolTask(
|
| FROM_HERE,
|
| - base::Bind(&BlockingObliteratePath, domain_root, paths_to_keep),
|
| - true);
|
| + base::Bind(&BlockingObliteratePath, domain_root, paths_to_keep,
|
| + base::MessageLoopProxy::current(), on_gc_required));
|
| +}
|
|
|
| - // TODO(ajwong): Schedule a final AsyncObliterate of the whole directory on
|
| - // the next browser start. http://crbug.com/85127.
|
| +void StoragePartitionImplMap::GarbageCollect(
|
| + scoped_ptr<base::hash_set<FilePath> > active_paths,
|
| + const base::Closure& done) {
|
| + // Include all paths for current StoragePartitions in the active_paths since
|
| + // they cannot be deleted safely.
|
| + for (PartitionMap::const_iterator it = partitions_.begin();
|
| + it != partitions_.end();
|
| + ++it) {
|
| + const StoragePartitionConfig& config = it->first;
|
| + if (!config.in_memory)
|
| + active_paths->insert(it->second->GetPath());
|
| + }
|
| +
|
| + // Find the directory holding the StoragePartitions and delete everything in
|
| + // there that isn't considered active.
|
| + FilePath storage_root = browser_context_->GetPath().Append(
|
| + GetStoragePartitionDomainPath(std::string()));
|
| + file_access_runner_->PostTaskAndReply(
|
| + FROM_HERE,
|
| + base::Bind(&BlockingGarbageCollect, storage_root,
|
| + file_access_runner_,
|
| + base::Passed(&active_paths)),
|
| + done);
|
| }
|
|
|
| void StoragePartitionImplMap::ForEach(
|
|
|