1 // Copyright 2019 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 #ifndef COMPONENTS_SERVICES_STORAGE_DOM_STORAGE_DOM_STORAGE_DATABASE_H_
6 #define COMPONENTS_SERVICES_STORAGE_DOM_STORAGE_DOM_STORAGE_DATABASE_H_
7 
8 #include <stdint.h>
9 
10 #include <memory>
11 #include <string>
12 #include <vector>
13 
14 #include "base/callback.h"
15 #include "base/containers/span.h"
16 #include "base/files/file_path.h"
17 #include "base/macros.h"
18 #include "base/memory/scoped_refptr.h"
19 #include "base/optional.h"
20 #include "base/sequence_checker.h"
21 #include "base/sequenced_task_runner.h"
22 #include "base/threading/sequence_bound.h"
23 #include "base/trace_event/memory_allocator_dump_guid.h"
24 #include "base/trace_event/memory_dump_provider.h"
25 #include "third_party/leveldatabase/env_chromium.h"
26 #include "third_party/leveldatabase/src/include/leveldb/db.h"
27 #include "third_party/leveldatabase/src/include/leveldb/env.h"
28 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
29 
30 namespace storage {
31 
32 // Wraps its own leveldb::DB instance on behalf of the DOM Storage backend
33 // implementation. This object is not sequence-safe and must be instantiated on
34 // a sequence which allows use of blocking file operations.
35 //
36 // Use the static |OpenInMemory()| or |OpenDirectory()| helpers to
37 // asynchronously create an instance of this type from any sequence.
38 // When owning a SequenceBound<DomStorageDatabase> as produced by these helpers,
39 // all work on the DomStorageDatabase can be safely done via
40 // |SequenceBound::PostTaskWithThisObject|.
41 class DomStorageDatabase : private base::trace_event::MemoryDumpProvider {
42  public:
43   using Key = std::vector<uint8_t>;
44   using KeyView = base::span<const uint8_t>;
45   using Value = std::vector<uint8_t>;
46   using ValueView = base::span<const uint8_t>;
47   using Status = leveldb::Status;
48 
49   // Callback used for basic async operations on this class.
50   using StatusCallback = base::OnceCallback<void(Status)>;
51 
52   struct KeyValuePair {
53     KeyValuePair();
54     KeyValuePair(KeyValuePair&&);
55     KeyValuePair(const KeyValuePair&);
56     KeyValuePair(Key key, Value value);
57     ~KeyValuePair();
58     KeyValuePair& operator=(KeyValuePair&&);
59     KeyValuePair& operator=(const KeyValuePair&);
60 
61     bool operator==(const KeyValuePair& rhs) const;
62 
63     Key key;
64     Value value;
65   };
66 
67   ~DomStorageDatabase() override;
68 
69   // Callback invoked asynchronously with the result of both |OpenDirectory()|
70   // and |OpenInMemory()| defined below. Includes both the status and the
71   // (possibly null, on failure) sequence-bound DomStorageDatabase instance.
72   using OpenCallback =
73       base::OnceCallback<void(base::SequenceBound<DomStorageDatabase> database,
74                               leveldb::Status status)>;
75 
76   // Creates a DomStorageDatabase instance for a persistent database within a
77   // filesystem directory given by |directory|, which must be an absolute path.
78   // The database may or may not already exist at this path, and whether or not
79   // this operation succeeds in either case depends on options set in |options|,
80   // e.g. |create_if_missing| and/or |error_if_exists|.
81   //
82   // The instance will be bound to and perform all operations on |task_runner|,
83   // which must support blocking operations. |callback| is called on the calling
84   // sequence once the operation completes.
85   static void OpenDirectory(
86       const base::FilePath& directory,
87       const std::string& name,
88       const leveldb_env::Options& options,
89       const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
90           memory_dump_id,
91       scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
92       OpenCallback callback);
93 
94   // Creates a DomStorageDatabase instance for a new in-memory database.
95   //
96   // The instance will be bound to and perform all operations on |task_runner|,
97   // which must support blocking operations. |callback| is called on the calling
98   // sequence once the operation completes.
99   static void OpenInMemory(
100       const std::string& name,
101       const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
102           memory_dump_id,
103       scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
104       OpenCallback callback);
105 
106   // Destroys the persistent database named |name| within the filesystem
107   // directory identified by the absolute path in |directory|.
108   //
109   // All work is done on |task_runner|, which must support blocking operations,
110   // and upon completion |callback| is called on the calling sequence.
111   static void Destroy(
112       const base::FilePath& directory,
113       const std::string& name,
114       scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
115       StatusCallback callback);
116 
117   // Retrieves the value for |key| in the database.
118   Status Get(KeyView key, Value* out_value) const;
119 
120   // Sets the database entry for |key| to |value|.
121   Status Put(KeyView key, ValueView value) const;
122 
123   // Deletes the database entry for |key|.
124   Status Delete(KeyView key) const;
125 
126   // Gets all database entries whose key starts with |prefix|.
127   Status GetPrefixed(KeyView prefix, std::vector<KeyValuePair>* entries) const;
128 
129   // Adds operations to |batch| which will delete all database entries whose key
130   // starts with |prefix| when committed.
131   Status DeletePrefixed(KeyView prefix, leveldb::WriteBatch* batch) const;
132 
133   // Adds operations to |batch| which when committed will copy all database
134   // entries whose key starts with |prefix| over to new entries with |prefix|
135   // replaced by |new_prefix| in each new key.
136   Status CopyPrefixed(KeyView prefix,
137                       KeyView new_prefix,
138                       leveldb::WriteBatch* batch) const;
139 
140   // Commits operations in |batch| to the database.
141   Status Commit(leveldb::WriteBatch* batch) const;
142 
143   // Rewrites the database on disk to clean up traces of deleted entries.
144   //
145   // NOTE: If |RewriteDB()| fails, this DomStorageDatabase may no longer be
146   // usable; in such cases, all future operations will return an IOError status.
147   Status RewriteDB();
148 
SetDestructionCallbackForTesting(base::OnceClosure callback)149   void SetDestructionCallbackForTesting(base::OnceClosure callback) {
150     destruction_callback_ = std::move(callback);
151   }
152 
MakeAllCommitsFailForTesting()153   void MakeAllCommitsFailForTesting() { fail_commits_for_testing_ = true; }
154 
155  private:
156   friend class base::SequenceBound<DomStorageDatabase>;
157 
158   // Constructs a new DomStorageDatabase, creating or opening persistent
159   // on-filesystem database as specified. Asynchronously invokes |callback| on
160   // |callback_task_runner| when done.
161   //
162   // This must be called on a sequence that allows blocking operations. Prefer
163   // to instead call one of the static methods defined below, which can be
164   // called from any sequence.
165   DomStorageDatabase(
166       const base::FilePath& directory,
167       const std::string& name,
168       const leveldb_env::Options& options,
169       const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
170           memory_dump_id,
171       scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
172       StatusCallback callback);
173 
174   // Same as above, but for an in-memory database. |tracking_name| is used
175   // internally for memory dump details.
176   DomStorageDatabase(
177       const std::string& tracking_name,
178       const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
179           memory_dump_id,
180       scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
181       StatusCallback callback);
182 
183   DomStorageDatabase(
184       const std::string& name,
185       std::unique_ptr<leveldb::Env> env,
186       const leveldb_env::Options& options,
187       const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>
188           memory_dump_id_,
189       scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
190       StatusCallback callback);
191 
192   // base::trace_event::MemoryDumpProvider implementation:
193   bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
194                     base::trace_event::ProcessMemoryDump* pmd) override;
195 
196   const std::string name_;
197   const std::unique_ptr<leveldb::Env> env_;
198   const leveldb_env::Options options_;
199   const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>
200       memory_dump_id_;
201   std::unique_ptr<leveldb::DB> db_;
202 
203   // Causes all calls to |Commit()| to fail with an IOError for simulated
204   // disk failures in testing.
205   bool fail_commits_for_testing_ = false;
206 
207   // Callback to run on destruction in tests.
208   base::OnceClosure destruction_callback_;
209 
210   SEQUENCE_CHECKER(sequence_checker_);
211 
212   DISALLOW_COPY_AND_ASSIGN(DomStorageDatabase);
213 };
214 
215 }  // namespace storage
216 
217 #endif  // COMPONENTS_SERVICES_STORAGE_DOM_STORAGE_DOM_STORAGE_DATABASE_H_
218