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