Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(86)

Side by Side Diff: content/browser/storage_partition_impl_map.cc

Issue 11419307: Garbage Collect the Storage directory on next profile start after an extension uninstall. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address mpcomplete's comments Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698