sandbox_origin_database.cc 12 KB

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