File: backend_storage.cc

package info (click to toggle)
chromium 144.0.7559.109-2
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 5,915,868 kB
  • sloc: cpp: 35,866,215; ansic: 7,599,035; javascript: 3,623,761; python: 1,639,407; xml: 833,084; asm: 716,173; pascal: 185,323; sh: 88,763; perl: 88,699; objc: 79,984; sql: 58,217; cs: 42,430; fortran: 24,101; makefile: 20,747; tcl: 15,277; php: 14,022; yacc: 9,059; ruby: 7,553; awk: 3,720; lisp: 3,233; lex: 1,330; ada: 727; jsp: 228; sed: 36
file content (179 lines) | stat: -rw-r--r-- 6,393 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/persistent_cache/backend_storage.h"

#include <algorithm>
#include <functional>
#include <utility>
#include <vector>

#include "base/check.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/numerics/clamped_math.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/persistent_cache/backend.h"
#include "components/persistent_cache/backend_type.h"
#include "components/persistent_cache/pending_backend.h"
#include "components/persistent_cache/persistent_cache.h"

#if !BUILDFLAG(IS_FUCHSIA)
#include "components/persistent_cache/sqlite/backend_storage_delegate.h"
#endif

namespace persistent_cache {

namespace {

std::unique_ptr<BackendStorage::Delegate> MakeDelegateOfType(
    BackendType backend_type) {
#if BUILDFLAG(IS_FUCHSIA)
  return nullptr;
#else
  switch (backend_type) {
    case BackendType::kSqlite:
      return std::make_unique<sqlite::BackendStorageDelegate>();
  }
#endif
}

// Deletes the contents of `directory` without deleting `directory` itself.
void DeleteDirectoryContents(const base::FilePath& directory) {
  base::FileEnumerator(directory, /*recursive=*/false,
                       base::FileEnumerator::NAMES_ONLY)
      .ForEach([](const base::FilePath& path) {
        base::DeletePathRecursively(path);
      });
}

}  // namespace

BackendStorage::BackendStorage(BackendType backend_type,
                               base::FilePath directory)
    : BackendStorage(MakeDelegateOfType(backend_type), std::move(directory)) {}

BackendStorage::BackendStorage(std::unique_ptr<Delegate> delegate,
                               base::FilePath directory)
    : delegate_(std::move(delegate)), directory_(std::move(directory)) {
  CHECK(!directory_.empty());
  is_valid_ = delegate_ && base::CreateDirectory(directory_);
}

BackendStorage::~BackendStorage() = default;

std::optional<PendingBackend> BackendStorage::MakePendingBackend(
    const base::FilePath& base_name,
    bool single_connection,
    bool journal_mode_wal) {
  return is_valid_
             ? delegate_->MakePendingBackend(
                   directory_, base_name, single_connection, journal_mode_wal)
             : std::nullopt;
}

std::unique_ptr<Backend> BackendStorage::MakeBackend(
    const base::FilePath& base_name,
    bool single_connection,
    bool journal_mode_wal) {
  return is_valid_ ? delegate_->MakeBackend(directory_, base_name,
                                            single_connection, journal_mode_wal)
                   : nullptr;
}

std::optional<PendingBackend> BackendStorage::ShareReadOnlyConnection(
    const base::FilePath& base_name,
    const PersistentCache& cache) {
  return is_valid_ ? delegate_->ShareReadOnlyConnection(directory_, base_name,
                                                        cache.backend())
                   : std::nullopt;
}

std::optional<PendingBackend> BackendStorage::ShareReadWriteConnection(
    const base::FilePath& base_name,
    const PersistentCache& cache) {
  return is_valid_ ? delegate_->ShareReadWriteConnection(directory_, base_name,
                                                         cache.backend())
                   : std::nullopt;
}

void BackendStorage::DeleteAllFiles() {
  if (is_valid_) {
    // All files are opened with FLAG_WIN_SHARE_DELETE, so it is possible to
    // delete them even if any are still open. The parent directory will not
    // be deleted in this case, but that's okay.

    // TODO(https://crbug.com/377475540): On Windows, a file cannot be marked
    // for deletion while it is mapped into a process's address space. If WAL
    // mode is used, we will need to investigate if the wal-index ("-shm") file
    // can always be opened with `FLAG_DELETE_ON_CLOSE` so that it is
    // unconditionally deleted when the DB is closed.
    // https://sqlite.org/walformat.html#shm indicates that this should be safe.
    DeleteDirectoryContents(directory_);
  }
}

void BackendStorage::DeleteFiles(const base::FilePath& base_name) {
  if (is_valid_) {
    delegate_->DeleteFiles(directory_, base_name);
  }
}

BackendStorage::FootprintReductionResult
BackendStorage::BringDownTotalFootprintOfFiles(int64_t target_footprint) {
  if (!is_valid_) {
    return {0, 0};
  }

  // Measure the size of the directory while collecting the basenames and
  // last modified times of each of the backend's files.
  int64_t total_footprint = 0;
  std::vector<std::pair<base::FilePath, base::Time>> base_names;
  base::FileEnumerator(directory_, /*recursive=*/false,
                       base::FileEnumerator::FILES)
      .ForEach([&total_footprint, &base_names,
                &delegate = *delegate_](const base::FilePath& file_path) {
        base::File::Info info;
        // Ignore errors -- info.last_modified will be zero, so this will look
        // like the oldest file; first to be deleted.
        base::GetFileInfo(file_path, &info);

        // Only target files managed by the backend for deletion.
        if (auto base_name = delegate.GetBaseName(file_path);
            !base_name.empty()) {
          base_names.emplace_back(std::move(base_name), info.last_modified);
        }

        // All files count towards measured footprint.
        total_footprint = base::ClampAdd(total_footprint, info.size);
      });

  // Nothing to do.
  if (total_footprint <= target_footprint) {
    return FootprintReductionResult{.current_footprint = total_footprint};
  }

  // Sort the collected base names oldest to newest.
  std::ranges::sort(base_names, std::ranges::less(),
                    &std::pair<base::FilePath, base::Time>::second);

  // Delete files until reaching the desired target.
  const int64_t size_of_necessary_deletes = total_footprint - target_footprint;
  int64_t deleted_size = 0;

  for (const auto& [base_name, last_modified_time] : base_names) {
    deleted_size = base::ClampAdd(
        deleted_size, delegate_->DeleteFiles(directory_, base_name));
    if (deleted_size >= size_of_necessary_deletes) {
      break;
    }
  }

  return FootprintReductionResult{
      .current_footprint = total_footprint - deleted_size,
      .number_of_bytes_deleted = deleted_size};
}

}  // namespace persistent_cache