File: file_path_util.cc

package info (click to toggle)
chromium 140.0.7339.185-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,193,740 kB
  • sloc: cpp: 35,093,945; ansic: 7,161,670; javascript: 4,199,694; python: 1,441,797; asm: 949,904; xml: 747,515; pascal: 187,748; perl: 88,691; sh: 88,248; objc: 79,953; sql: 52,714; cs: 44,599; fortran: 24,137; makefile: 22,114; tcl: 15,277; php: 13,980; yacc: 9,000; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (178 lines) | stat: -rw-r--r-- 6,661 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
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/indexed_db/file_path_util.h"

#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>

#include <string>
#include <string_view>

#include "base/containers/span.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/function_ref.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "build/buildflag.h"
#include "components/base32/base32.h"
#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
#include "crypto/hash.h"
#include "storage/common/database/database_identifier.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"

namespace content::indexed_db {

namespace {
constexpr base::FilePath::CharType kBlobExtension[] =
    FILE_PATH_LITERAL(".blob");

// The file name used for databases that have an empty name.
constexpr char kSqliteEmptyDatabaseNameFileName[] = "0";
}  // namespace

bool ShouldUseLegacyFilePath(const storage::BucketLocator& bucket_locator) {
  return bucket_locator.storage_key.IsFirstPartyContext() &&
         bucket_locator.is_default;
}

base::FilePath GetBlobStoreFileName(
    const storage::BucketLocator& bucket_locator) {
  if (ShouldUseLegacyFilePath(bucket_locator)) {
    // First-party blob files, for legacy reasons, are stored at:
    // {{first_party_data_path}}/{{serialized_origin}}.indexeddb.blob
    return base::FilePath()
        .AppendASCII(storage::GetIdentifierFromOrigin(
            bucket_locator.storage_key.origin()))
        .AddExtension(kIndexedDBExtension)
        .AddExtension(kBlobExtension);
  }

  // Third-party blob files are stored at:
  // {{third_party_data_path}}/{{bucket_id}}/IndexedDB/indexeddb.blob
  return base::FilePath(kIndexedDBFile).AddExtension(kBlobExtension);
}

base::FilePath GetLevelDBFileName(
    const storage::BucketLocator& bucket_locator) {
  if (ShouldUseLegacyFilePath(bucket_locator)) {
    // First-party leveldb files, for legacy reasons, are stored at:
    // {{first_party_data_path}}/{{serialized_origin}}.indexeddb.leveldb
    // TODO(crbug.com/40855748): Migrate all first party buckets to the new
    // path.
    return base::FilePath()
        .AppendASCII(storage::GetIdentifierFromOrigin(
            bucket_locator.storage_key.origin()))
        .AddExtension(kIndexedDBExtension)
        .AddExtension(kLevelDBExtension);
  }

  // Third-party leveldb files are stored at:
  // {{third_party_data_path}}/{{bucket_id}}/IndexedDB/indexeddb.leveldb
  return base::FilePath(kIndexedDBFile).AddExtension(kLevelDBExtension);
}

base::FilePath GetBlobDirectoryName(const base::FilePath& path_base,
                                    int64_t database_id) {
  return path_base.AppendASCII(base::StringPrintf("%" PRIx64, database_id));
}

base::FilePath GetBlobDirectoryNameForKey(const base::FilePath& path_base,
                                          int64_t database_id,
                                          int64_t blob_number) {
  base::FilePath path = GetBlobDirectoryName(path_base, database_id);
  path = path.AppendASCII(base::StringPrintf(
      "%02x", static_cast<int>(blob_number & 0x000000000000ff00) >> 8));
  return path;
}

base::FilePath GetBlobFileNameForKey(const base::FilePath& path_base,
                                     int64_t database_id,
                                     int64_t blob_number) {
  base::FilePath path =
      GetBlobDirectoryNameForKey(path_base, database_id, blob_number);
  path = path.AppendASCII(base::StringPrintf("%" PRIx64, blob_number));
  return path;
}

bool IsPathTooLong(const base::FilePath& path) {
  int limit = base::GetMaximumPathComponentLength(path.DirName());
  if (limit < 0) {
    DPLOG(WARNING) << "GetMaximumPathComponentLength returned -1 for "
                   << path.DirName();
// In limited testing, ChromeOS returns 143, other OSes 255.
#if BUILDFLAG(IS_CHROMEOS)
    limit = 143;
#else
    limit = 255;
#endif
  }
  return path.BaseName().value().length() > static_cast<uint32_t>(limit);
}

base::FilePath GetSqliteDbDirectory(
    const storage::BucketLocator& bucket_locator) {
  if (ShouldUseLegacyFilePath(bucket_locator)) {
    // All sites share a single data path for their default bucket. Append a
    // directory for this specific site.
    return base::FilePath().AppendASCII(
        storage::GetIdentifierFromOrigin(bucket_locator.storage_key.origin()));
  }

  // The base data path is already specific to the site and bucket. The SQLite
  // DB will be stored within it.
  return base::FilePath();
}

base::FilePath DatabaseNameToFileName(std::u16string_view db_name) {
  // The goal is to create a deterministic mapping from DB name to file name.
  // There are essentially no constraints on `db_name`, in terms of length or
  // contents. File names have to conform to a certain character set and length,
  // (which depends on the file system). Thus, the space of all file names is
  // smaller than the space of all database names, and we can't simply use the
  // db name as the file name.
  //
  // To address this, we first hash the db name using SHA256, which ensures a
  // negligible probability of collisions. Then we encode using Base32, because
  // it uses only a character set that is safe for all file systems, including
  // case-insensitive ones.
  return db_name.empty()
             ? base::FilePath::FromASCII(kSqliteEmptyDatabaseNameFileName)
             : base::FilePath::FromASCII(base32::Base32Encode(
                   crypto::hash::Sha256(base::as_byte_span(db_name)),
                   base32::Base32EncodePolicy::OMIT_PADDING));
}

void EnumerateDatabasesInDirectory(
    const base::FilePath& directory,
    base::FunctionRef<void(const base::FilePath& path)> ref) {
  base::FileEnumerator enumerator(directory, /*recursive=*/false,
                                  base::FileEnumerator::FILES);
  enumerator.ForEach([&](const base::FilePath& path) {
    if (path.BaseName() ==
        base::FilePath::FromASCII(kSqliteEmptyDatabaseNameFileName)) {
      ref(path);
      return;
    }

    std::string ascii_name = path.BaseName().MaybeAsASCII();
    if (ascii_name.empty()) {
      return;
    }

    if (base32::Base32Decode(ascii_name).size() !=
        crypto::hash::DigestSizeForHashKind(crypto::hash::HashKind::kSha256)) {
      return;
    }

    ref(path);
  });
}

}  // namespace content::indexed_db