Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(971)

Side by Side Diff: webkit/fileapi/sandbox_origin_database.cc

Issue 15442002: Move FileAPI sandboxed filesystem related code from webkit/fileapi to webkit/browser/fileapi (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebased Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 "webkit/fileapi/sandbox_origin_database.h"
6
7 #include <set>
8
9 #include "base/file_util.h"
10 #include "base/format_macros.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram.h"
14 #include "base/string_util.h"
15 #include "base/stringprintf.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "third_party/leveldatabase/src/include/leveldb/db.h"
18 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
19 #include "webkit/fileapi/file_system_util.h"
20
21 namespace {
22
23 const base::FilePath::CharType kOriginDatabaseName[] =
24 FILE_PATH_LITERAL("Origins");
25 const char kOriginKeyPrefix[] = "ORIGIN:";
26 const char kLastPathKey[] = "LAST_PATH";
27 const int64 kMinimumReportIntervalHours = 1;
28 const char kInitStatusHistogramLabel[] = "FileSystem.OriginDatabaseInit";
29 const char kDatabaseRepairHistogramLabel[] = "FileSystem.OriginDatabaseRepair";
30
31 enum InitStatus {
32 INIT_STATUS_OK = 0,
33 INIT_STATUS_CORRUPTION,
34 INIT_STATUS_IO_ERROR,
35 INIT_STATUS_UNKNOWN_ERROR,
36 INIT_STATUS_MAX
37 };
38
39 enum RepairResult {
40 DB_REPAIR_SUCCEEDED = 0,
41 DB_REPAIR_FAILED,
42 DB_REPAIR_MAX
43 };
44
45 std::string OriginToOriginKey(const std::string& origin) {
46 std::string key(kOriginKeyPrefix);
47 return key + origin;
48 }
49
50 const char* LastPathKey() {
51 return kLastPathKey;
52 }
53
54 } // namespace
55
56 namespace fileapi {
57
58 SandboxOriginDatabase::OriginRecord::OriginRecord() {
59 }
60
61 SandboxOriginDatabase::OriginRecord::OriginRecord(
62 const std::string& origin_in, const base::FilePath& path_in)
63 : origin(origin_in), path(path_in) {
64 }
65
66 SandboxOriginDatabase::OriginRecord::~OriginRecord() {
67 }
68
69 SandboxOriginDatabase::SandboxOriginDatabase(
70 const base::FilePath& file_system_directory)
71 : file_system_directory_(file_system_directory) {
72 }
73
74 SandboxOriginDatabase::~SandboxOriginDatabase() {
75 }
76
77 bool SandboxOriginDatabase::Init(RecoveryOption recovery_option) {
78 if (db_)
79 return true;
80
81 std::string path =
82 FilePathToString(file_system_directory_.Append(kOriginDatabaseName));
83 leveldb::Options options;
84 options.create_if_missing = true;
85 leveldb::DB* db;
86 leveldb::Status status = leveldb::DB::Open(options, path, &db);
87 ReportInitStatus(status);
88 if (status.ok()) {
89 db_.reset(db);
90 return true;
91 }
92 HandleError(FROM_HERE, status);
93
94 // Corruption due to missing necessary MANIFEST-* file causes IOError instead
95 // of Corruption error.
96 // Try to repair database even when IOError case.
97 if (!status.IsCorruption() && !status.IsIOError())
98 return false;
99
100 switch (recovery_option) {
101 case FAIL_ON_CORRUPTION:
102 return false;
103 case REPAIR_ON_CORRUPTION:
104 LOG(WARNING) << "Attempting to repair SandboxOriginDatabase.";
105
106 if (RepairDatabase(path)) {
107 UMA_HISTOGRAM_ENUMERATION(kDatabaseRepairHistogramLabel,
108 DB_REPAIR_SUCCEEDED, DB_REPAIR_MAX);
109 LOG(WARNING) << "Repairing SandboxOriginDatabase completed.";
110 return true;
111 }
112 UMA_HISTOGRAM_ENUMERATION(kDatabaseRepairHistogramLabel,
113 DB_REPAIR_FAILED, DB_REPAIR_MAX);
114 // fall through
115 case DELETE_ON_CORRUPTION:
116 if (!file_util::Delete(file_system_directory_, true))
117 return false;
118 if (!file_util::CreateDirectory(file_system_directory_))
119 return false;
120 return Init(FAIL_ON_CORRUPTION);
121 }
122 NOTREACHED();
123 return false;
124 }
125
126 bool SandboxOriginDatabase::RepairDatabase(const std::string& db_path) {
127 DCHECK(!db_.get());
128 if (!leveldb::RepairDB(db_path, leveldb::Options()).ok() ||
129 !Init(FAIL_ON_CORRUPTION)) {
130 LOG(WARNING) << "Failed to repair SandboxOriginDatabase.";
131 return false;
132 }
133
134 // See if the repaired entries match with what we have on disk.
135 std::set<base::FilePath> directories;
136 file_util::FileEnumerator file_enum(file_system_directory_,
137 false /* recursive */,
138 file_util::FileEnumerator::DIRECTORIES);
139 base::FilePath path_each;
140 while (!(path_each = file_enum.Next()).empty())
141 directories.insert(path_each.BaseName());
142 std::set<base::FilePath>::iterator db_dir_itr =
143 directories.find(base::FilePath(kOriginDatabaseName));
144 // Make sure we have the database file in its directory and therefore we are
145 // working on the correct path.
146 DCHECK(db_dir_itr != directories.end());
147 directories.erase(db_dir_itr);
148
149 std::vector<OriginRecord> origins;
150 if (!ListAllOrigins(&origins)) {
151 DropDatabase();
152 return false;
153 }
154
155 // Delete any obsolete entries from the origins database.
156 for (std::vector<OriginRecord>::iterator db_origin_itr = origins.begin();
157 db_origin_itr != origins.end();
158 ++db_origin_itr) {
159 std::set<base::FilePath>::iterator dir_itr =
160 directories.find(db_origin_itr->path);
161 if (dir_itr == directories.end()) {
162 if (!RemovePathForOrigin(db_origin_itr->origin)) {
163 DropDatabase();
164 return false;
165 }
166 } else {
167 directories.erase(dir_itr);
168 }
169 }
170
171 // Delete any directories not listed in the origins database.
172 for (std::set<base::FilePath>::iterator dir_itr = directories.begin();
173 dir_itr != directories.end();
174 ++dir_itr) {
175 if (!file_util::Delete(file_system_directory_.Append(*dir_itr),
176 true /* recursive */)) {
177 DropDatabase();
178 return false;
179 }
180 }
181
182 return true;
183 }
184
185 void SandboxOriginDatabase::HandleError(
186 const tracked_objects::Location& from_here,
187 const leveldb::Status& status) {
188 db_.reset();
189 LOG(ERROR) << "SandboxOriginDatabase failed at: "
190 << from_here.ToString() << " with error: " << status.ToString();
191 }
192
193 void SandboxOriginDatabase::ReportInitStatus(const leveldb::Status& status) {
194 base::Time now = base::Time::Now();
195 base::TimeDelta minimum_interval =
196 base::TimeDelta::FromHours(kMinimumReportIntervalHours);
197 if (last_reported_time_ + minimum_interval >= now)
198 return;
199 last_reported_time_ = now;
200
201 if (status.ok()) {
202 UMA_HISTOGRAM_ENUMERATION(kInitStatusHistogramLabel,
203 INIT_STATUS_OK, INIT_STATUS_MAX);
204 } else if (status.IsCorruption()) {
205 UMA_HISTOGRAM_ENUMERATION(kInitStatusHistogramLabel,
206 INIT_STATUS_CORRUPTION, INIT_STATUS_MAX);
207 } else if (status.IsIOError()) {
208 UMA_HISTOGRAM_ENUMERATION(kInitStatusHistogramLabel,
209 INIT_STATUS_IO_ERROR, INIT_STATUS_MAX);
210 } else {
211 UMA_HISTOGRAM_ENUMERATION(kInitStatusHistogramLabel,
212 INIT_STATUS_UNKNOWN_ERROR, INIT_STATUS_MAX);
213 }
214 }
215
216 bool SandboxOriginDatabase::HasOriginPath(const std::string& origin) {
217 if (!Init(REPAIR_ON_CORRUPTION))
218 return false;
219 if (origin.empty())
220 return false;
221 std::string path;
222 leveldb::Status status =
223 db_->Get(leveldb::ReadOptions(), OriginToOriginKey(origin), &path);
224 if (status.ok())
225 return true;
226 if (status.IsNotFound())
227 return false;
228 HandleError(FROM_HERE, status);
229 return false;
230 }
231
232 bool SandboxOriginDatabase::GetPathForOrigin(
233 const std::string& origin, base::FilePath* directory) {
234 if (!Init(REPAIR_ON_CORRUPTION))
235 return false;
236 DCHECK(directory);
237 if (origin.empty())
238 return false;
239 std::string path_string;
240 std::string origin_key = OriginToOriginKey(origin);
241 leveldb::Status status =
242 db_->Get(leveldb::ReadOptions(), origin_key, &path_string);
243 if (status.IsNotFound()) {
244 int last_path_number;
245 if (!GetLastPathNumber(&last_path_number))
246 return false;
247 path_string = base::StringPrintf("%03u", last_path_number + 1);
248 // store both back as a single transaction
249 leveldb::WriteBatch batch;
250 batch.Put(LastPathKey(), path_string);
251 batch.Put(origin_key, path_string);
252 status = db_->Write(leveldb::WriteOptions(), &batch);
253 if (!status.ok()) {
254 HandleError(FROM_HERE, status);
255 return false;
256 }
257 }
258 if (status.ok()) {
259 *directory = StringToFilePath(path_string);
260 return true;
261 }
262 HandleError(FROM_HERE, status);
263 return false;
264 }
265
266 bool SandboxOriginDatabase::RemovePathForOrigin(const std::string& origin) {
267 if (!Init(REPAIR_ON_CORRUPTION))
268 return false;
269 leveldb::Status status =
270 db_->Delete(leveldb::WriteOptions(), OriginToOriginKey(origin));
271 if (status.ok() || status.IsNotFound())
272 return true;
273 HandleError(FROM_HERE, status);
274 return false;
275 }
276
277 bool SandboxOriginDatabase::ListAllOrigins(
278 std::vector<OriginRecord>* origins) {
279 if (!Init(REPAIR_ON_CORRUPTION))
280 return false;
281 DCHECK(origins);
282 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions()));
283 std::string origin_key_prefix = OriginToOriginKey(std::string());
284 iter->Seek(origin_key_prefix);
285 origins->clear();
286 while (iter->Valid() &&
287 StartsWithASCII(iter->key().ToString(), origin_key_prefix, true)) {
288 std::string origin =
289 iter->key().ToString().substr(origin_key_prefix.length());
290 base::FilePath path = StringToFilePath(iter->value().ToString());
291 origins->push_back(OriginRecord(origin, path));
292 iter->Next();
293 }
294 return true;
295 }
296
297 void SandboxOriginDatabase::DropDatabase() {
298 db_.reset();
299 }
300
301 bool SandboxOriginDatabase::GetLastPathNumber(int* number) {
302 if (!Init(REPAIR_ON_CORRUPTION))
303 return false;
304 DCHECK(number);
305 std::string number_string;
306 leveldb::Status status =
307 db_->Get(leveldb::ReadOptions(), LastPathKey(), &number_string);
308 if (status.ok())
309 return base::StringToInt(number_string, number);
310 if (!status.IsNotFound()) {
311 HandleError(FROM_HERE, status);
312 return false;
313 }
314 // Verify that this is a totally new database, and initialize it.
315 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions()));
316 iter->SeekToFirst();
317 if (iter->Valid()) { // DB was not empty, but had no last path number!
318 LOG(ERROR) << "File system origin database is corrupt!";
319 return false;
320 }
321 // This is always the first write into the database. If we ever add a
322 // version number, they should go in in a single transaction.
323 status =
324 db_->Put(leveldb::WriteOptions(), LastPathKey(), std::string("-1"));
325 if (!status.ok()) {
326 HandleError(FROM_HERE, status);
327 return false;
328 }
329 *number = -1;
330 return true;
331 }
332
333 } // namespace fileapi
OLDNEW
« no previous file with comments | « webkit/fileapi/sandbox_origin_database.h ('k') | webkit/fileapi/sandbox_origin_database_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698