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 "net/disk_cache/cache_util.h"
6
7#include "base/file_util.h"
8#include "base/files/file_enumerator.h"
9#include "base/location.h"
10#include "base/strings/string_util.h"
11#include "base/strings/stringprintf.h"
12#include "base/threading/thread_restrictions.h"
13#include "base/threading/worker_pool.h"
14
15namespace {
16
17const int kMaxOldFolders = 100;
18
19// Returns a fully qualified name from path and name, using a given name prefix
20// and index number. For instance, if the arguments are "/foo", "bar" and 5, it
21// will return "/foo/old_bar_005".
22base::FilePath GetPrefixedName(const base::FilePath& path,
23                               const std::string& name,
24                               int index) {
25  std::string tmp = base::StringPrintf("%s%s_%03d", "old_",
26                                       name.c_str(), index);
27  return path.AppendASCII(tmp);
28}
29
30// This is a simple callback to cleanup old caches.
31void CleanupCallback(const base::FilePath& path, const std::string& name) {
32  for (int i = 0; i < kMaxOldFolders; i++) {
33    base::FilePath to_delete = GetPrefixedName(path, name, i);
34    disk_cache::DeleteCache(to_delete, true);
35  }
36}
37
38// Returns a full path to rename the current cache, in order to delete it. path
39// is the current folder location, and name is the current folder name.
40base::FilePath GetTempCacheName(const base::FilePath& path,
41                                const std::string& name) {
42  // We'll attempt to have up to kMaxOldFolders folders for deletion.
43  for (int i = 0; i < kMaxOldFolders; i++) {
44    base::FilePath to_delete = GetPrefixedName(path, name, i);
45    if (!base::PathExists(to_delete))
46      return to_delete;
47  }
48  return base::FilePath();
49}
50
51int64 PreferredCacheSizeInternal(int64 available) {
52  using disk_cache::kDefaultCacheSize;
53  // Return 80% of the available space if there is not enough space to use
54  // kDefaultCacheSize.
55  if (available < kDefaultCacheSize * 10 / 8)
56    return available * 8 / 10;
57
58  // Return kDefaultCacheSize if it uses 10% to 80% of the available space.
59  if (available < kDefaultCacheSize * 10)
60    return kDefaultCacheSize;
61
62  // Return 10% of the available space if the target size
63  // (2.5 * kDefaultCacheSize) is more than 10%.
64  if (available < static_cast<int64>(kDefaultCacheSize) * 25)
65    return available / 10;
66
67  // Return the target size (2.5 * kDefaultCacheSize) if it uses 10% to 1%
68  // of the available space.
69  if (available < static_cast<int64>(kDefaultCacheSize) * 250)
70    return kDefaultCacheSize * 5 / 2;
71
72  // Return 1% of the available space.
73  return available / 100;
74}
75
76}  // namespace
77
78namespace disk_cache {
79
80const int kDefaultCacheSize = 80 * 1024 * 1024;
81
82void DeleteCache(const base::FilePath& path, bool remove_folder) {
83  if (remove_folder) {
84    if (!base::DeleteFile(path, /* recursive */ true))
85      LOG(WARNING) << "Unable to delete cache folder.";
86    return;
87  }
88
89  base::FileEnumerator iter(
90      path,
91      /* recursive */ false,
92      base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
93  for (base::FilePath file = iter.Next(); !file.value().empty();
94       file = iter.Next()) {
95    if (!base::DeleteFile(file, /* recursive */ true)) {
96      LOG(WARNING) << "Unable to delete cache.";
97      return;
98    }
99  }
100}
101
102// In order to process a potentially large number of files, we'll rename the
103// cache directory to old_ + original_name + number, (located on the same parent
104// directory), and use a worker thread to delete all the files on all the stale
105// cache directories. The whole process can still fail if we are not able to
106// rename the cache directory (for instance due to a sharing violation), and in
107// that case a cache for this profile (on the desired path) cannot be created.
108bool DelayedCacheCleanup(const base::FilePath& full_path) {
109  // GetTempCacheName() and MoveCache() use synchronous file
110  // operations.
111  base::ThreadRestrictions::ScopedAllowIO allow_io;
112
113  base::FilePath current_path = full_path.StripTrailingSeparators();
114
115  base::FilePath path = current_path.DirName();
116  base::FilePath name = current_path.BaseName();
117#if defined(OS_POSIX)
118  std::string name_str = name.value();
119#elif defined(OS_WIN)
120  // We created this file so it should only contain ASCII.
121  std::string name_str = WideToASCII(name.value());
122#endif
123
124  base::FilePath to_delete = GetTempCacheName(path, name_str);
125  if (to_delete.empty()) {
126    LOG(ERROR) << "Unable to get another cache folder";
127    return false;
128  }
129
130  if (!disk_cache::MoveCache(full_path, to_delete)) {
131    LOG(ERROR) << "Unable to move cache folder " << full_path.value() << " to "
132               << to_delete.value();
133    return false;
134  }
135
136  base::WorkerPool::PostTask(
137      FROM_HERE, base::Bind(&CleanupCallback, path, name_str), true);
138  return true;
139}
140
141// Returns the preferred maximum number of bytes for the cache given the
142// number of available bytes.
143int PreferredCacheSize(int64 available) {
144  if (available < 0)
145    return kDefaultCacheSize;
146
147  int64 max_size = PreferredCacheSizeInternal(available);
148
149  // Limit cache size to somewhat less than kint32max to avoid potential
150  // integer overflows in cache backend implementations.
151  DCHECK(kDefaultCacheSize * 4 < kint32max);
152  if (max_size > kDefaultCacheSize * 4)
153    max_size = kDefaultCacheSize * 4;
154
155  return implicit_cast<int32>(max_size);
156}
157
158}  // namespace disk_cache
159