OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013 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 "chrome/browser/policy/resource_cache.h" | |
6 | |
7 #include <string.h> | |
8 | |
9 #include "base/file_util.h" | |
10 #include "base/files/file_path.h" | |
11 #include "base/logging.h" | |
12 #include "base/string_util.h" | |
13 #include "base/strings/string_number_conversions.h" | |
14 #include "base/threading/thread_restrictions.h" | |
15 #include "third_party/leveldatabase/src/include/leveldb/db.h" | |
16 #include "third_party/leveldatabase/src/include/leveldb/iterator.h" | |
17 #include "third_party/leveldatabase/src/include/leveldb/options.h" | |
18 #include "third_party/leveldatabase/src/include/leveldb/slice.h" | |
19 #include "third_party/leveldatabase/src/include/leveldb/status.h" | |
20 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" | |
21 | |
22 namespace policy { | |
23 | |
24 ResourceCache::ResourceCache(const base::FilePath& cache_path) { | |
25 // Allow the cache to be created in a different thread than the thread that's | |
26 // going to use it. | |
27 DetachFromThread(); | |
28 | |
29 file_util::CreateDirectory(cache_path.DirName()); | |
30 leveldb::Options options; | |
31 options.create_if_missing = true; | |
32 leveldb::DB* db = NULL; | |
33 leveldb::Status status = | |
34 leveldb::DB::Open(options, cache_path.AsUTF8Unsafe(), &db); | |
35 if (!status.ok()) { | |
36 LOG(WARNING) << "Failed to open leveldb at " << cache_path.AsUTF8Unsafe() | |
37 << ": " << status.ToString(); | |
38 // Maybe the database is busted; drop everything and try to create it again. | |
39 file_util::Delete(cache_path, true); | |
40 status = leveldb::DB::Open(options, cache_path.AsUTF8Unsafe(), &db); | |
41 | |
42 if (!status.ok()) | |
43 LOG(WARNING) << "Failed to open a new leveldb after wiping: " | |
44 << status.ToString(); | |
45 } | |
46 db_.reset(db); | |
47 } | |
48 | |
49 ResourceCache::~ResourceCache() { | |
50 DCHECK(CalledOnValidThread()); | |
51 } | |
52 | |
53 bool ResourceCache::Store(const std::string& key, | |
54 const std::string& subkey, | |
55 const std::string& data) { | |
56 DCHECK(CalledOnValidThread()); | |
57 base::ThreadRestrictions::AssertIOAllowed(); | |
58 | |
59 if (!IsOpen()) | |
60 return false; | |
61 | |
62 std::string path(CreatePath(key, subkey)); | |
63 return db_->Put(leveldb::WriteOptions(), path, data).ok(); | |
64 } | |
65 | |
66 bool ResourceCache::Load(const std::string& key, | |
67 const std::string& subkey, | |
68 std::string* data) { | |
69 DCHECK(CalledOnValidThread()); | |
70 base::ThreadRestrictions::AssertIOAllowed(); | |
71 if (!IsOpen()) | |
72 return false; | |
73 | |
74 leveldb::ReadOptions options; | |
75 options.fill_cache = false; | |
76 std::string path(CreatePath(key, subkey)); | |
77 return db_->Get(options, path, data).ok(); | |
78 } | |
79 | |
80 void ResourceCache::LoadAllSubkeys( | |
81 const std::string& key, | |
82 std::map<std::string, std::string>* contents) { | |
83 DCHECK(CalledOnValidThread()); | |
84 base::ThreadRestrictions::AssertIOAllowed(); | |
85 if (!IsOpen()) | |
86 return; | |
87 | |
88 contents->clear(); | |
89 | |
90 const std::string prefix(CreatePathPrefix(key)); | |
91 leveldb::ReadOptions options; | |
92 options.fill_cache = false; | |
93 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options)); | |
94 for (it->Seek(prefix); it->Valid(); it->Next()) { | |
95 if (!it->key().starts_with(prefix)) | |
96 break; | |
97 const std::string subkey(GetSubkey(it->key().ToString())); | |
98 leveldb::Slice slice = it->value(); | |
99 (*contents)[subkey].assign(slice.data(), slice.size()); | |
100 } | |
101 } | |
102 | |
103 void ResourceCache::Delete(const std::string& key, const std::string& subkey) { | |
104 DCHECK(CalledOnValidThread()); | |
105 base::ThreadRestrictions::AssertIOAllowed(); | |
106 if (!IsOpen()) | |
107 return; | |
108 | |
109 const std::string path(CreatePath(key, subkey)); | |
110 leveldb::Status status = db_->Delete(leveldb::WriteOptions(), path); | |
111 if (!status.ok()) { | |
112 LOG(WARNING) << "Failed to Delete \"" << path << "\" from leveldb: " | |
113 << status.ToString(); | |
114 } | |
115 } | |
116 | |
117 void ResourceCache::PurgeOtherSubkeys( | |
118 const std::string& key, | |
119 const std::set<std::string>& subkeys_to_keep) { | |
120 DCHECK(CalledOnValidThread()); | |
121 base::ThreadRestrictions::AssertIOAllowed(); | |
122 if (!IsOpen()) | |
123 return; | |
124 | |
125 leveldb::WriteBatch batch; | |
126 const std::string prefix(CreatePathPrefix(key)); | |
127 leveldb::ReadOptions options; | |
128 options.fill_cache = false; | |
129 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options)); | |
130 for (it->Seek(prefix); it->Valid(); it->Next()) { | |
131 if (!it->key().starts_with(prefix)) | |
132 break; | |
133 const std::string subkey(GetSubkey(it->key().ToString())); | |
134 if (subkeys_to_keep.find(subkey) == subkeys_to_keep.end()) | |
135 batch.Delete(it->key()); | |
136 } | |
137 | |
138 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); | |
139 if (!status.ok()) { | |
140 LOG(WARNING) << "Purge of leveldb subkeys of " << key << " failed: " | |
141 << status.ToString(); | |
142 } | |
143 } | |
144 | |
145 std::string ResourceCache::GetStringWithPrefix(const std::string& s) { | |
146 char buffer[sizeof(size_t)]; | |
147 size_t size = s.size(); | |
148 memcpy(buffer, &size, sizeof(size_t)); | |
149 return std::string(buffer, sizeof(size_t)) + s; | |
150 } | |
151 | |
152 std::string ResourceCache::CreatePathPrefix(const std::string& key) { | |
153 return GetStringWithPrefix(key); | |
154 } | |
155 | |
156 std::string ResourceCache::CreatePath(const std::string& key, | |
157 const std::string& subkey) { | |
158 return GetStringWithPrefix(key) + GetStringWithPrefix(subkey); | |
159 } | |
160 | |
161 std::string ResourceCache::GetSubkey(const std::string& path) { | |
162 // |path| was produced by CreatePath(). Skip the first key, then skip the | |
163 // size of the subkey. | |
164 if (path.size() < sizeof(size_t)) { | |
165 NOTREACHED(); | |
166 return EmptyString(); | |
167 } | |
168 | |
169 // Skip the first string. |offset| is the start of the second string, | |
170 // immediately preceded by its size. | |
171 const size_t* size = reinterpret_cast<const size_t*>(path.data()); | |
172 size_t offset = sizeof(size_t) + *size + sizeof(size_t); | |
173 if (path.size() < offset) { | |
174 NOTREACHED(); | |
175 return EmptyString(); | |
176 } | |
177 | |
178 // Skip the size of the second string. | |
179 size = reinterpret_cast<const size_t*>(path.data() + offset - sizeof(size_t)); | |
180 if (*size != path.size() - offset) { | |
181 NOTREACHED(); | |
182 return EmptyString(); | |
183 } | |
184 | |
185 return path.substr(offset, *size); | |
186 } | |
187 | |
188 } // namespace policy | |
OLD | NEW |