You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

dbwrapper.cpp 7.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // Copyright (c) 2012-2016 The Starwels developers
  2. // Distributed under the MIT software license, see the accompanying
  3. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4. #include "dbwrapper.h"
  5. #include "fs.h"
  6. #include "util.h"
  7. #include "random.h"
  8. #include <leveldb/cache.h>
  9. #include <leveldb/env.h>
  10. #include <leveldb/filter_policy.h>
  11. #include <memenv.h>
  12. #include <stdint.h>
  13. #include <algorithm>
  14. class CStarwelsLevelDBLogger : public leveldb::Logger {
  15. public:
  16. // This code is adapted from posix_logger.h, which is why it is using vsprintf.
  17. // Please do not do this in normal code
  18. virtual void Logv(const char * format, va_list ap) override {
  19. if (!LogAcceptCategory(BCLog::LEVELDB)) {
  20. return;
  21. }
  22. char buffer[500];
  23. for (int iter = 0; iter < 2; iter++) {
  24. char* base;
  25. int bufsize;
  26. if (iter == 0) {
  27. bufsize = sizeof(buffer);
  28. base = buffer;
  29. }
  30. else {
  31. bufsize = 30000;
  32. base = new char[bufsize];
  33. }
  34. char* p = base;
  35. char* limit = base + bufsize;
  36. // Print the message
  37. if (p < limit) {
  38. va_list backup_ap;
  39. va_copy(backup_ap, ap);
  40. // Do not use vsnprintf elsewhere in starwels source code, see above.
  41. p += vsnprintf(p, limit - p, format, backup_ap);
  42. va_end(backup_ap);
  43. }
  44. // Truncate to available space if necessary
  45. if (p >= limit) {
  46. if (iter == 0) {
  47. continue; // Try again with larger buffer
  48. }
  49. else {
  50. p = limit - 1;
  51. }
  52. }
  53. // Add newline if necessary
  54. if (p == base || p[-1] != '\n') {
  55. *p++ = '\n';
  56. }
  57. assert(p <= limit);
  58. base[std::min(bufsize - 1, (int)(p - base))] = '\0';
  59. LogPrintStr(base);
  60. if (base != buffer) {
  61. delete[] base;
  62. }
  63. break;
  64. }
  65. }
  66. };
  67. static leveldb::Options GetOptions(size_t nCacheSize)
  68. {
  69. leveldb::Options options;
  70. options.block_cache = leveldb::NewLRUCache(nCacheSize / 2);
  71. options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously
  72. options.filter_policy = leveldb::NewBloomFilterPolicy(10);
  73. options.compression = leveldb::kNoCompression;
  74. options.max_open_files = 64;
  75. options.info_log = new CStarwelsLevelDBLogger();
  76. if (leveldb::kMajorVersion > 1 || (leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16)) {
  77. // LevelDB versions before 1.16 consider short writes to be corruption. Only trigger error
  78. // on corruption in later versions.
  79. options.paranoid_checks = true;
  80. }
  81. return options;
  82. }
  83. CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate)
  84. {
  85. penv = nullptr;
  86. readoptions.verify_checksums = true;
  87. iteroptions.verify_checksums = true;
  88. iteroptions.fill_cache = false;
  89. syncoptions.sync = true;
  90. options = GetOptions(nCacheSize);
  91. options.create_if_missing = true;
  92. if (fMemory) {
  93. penv = leveldb::NewMemEnv(leveldb::Env::Default());
  94. options.env = penv;
  95. } else {
  96. if (fWipe) {
  97. LogPrintf("Wiping LevelDB in %s\n", path.string());
  98. leveldb::Status result = leveldb::DestroyDB(path.string(), options);
  99. dbwrapper_private::HandleError(result);
  100. }
  101. TryCreateDirectories(path);
  102. LogPrintf("Opening LevelDB in %s\n", path.string());
  103. }
  104. leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb);
  105. dbwrapper_private::HandleError(status);
  106. LogPrintf("Opened LevelDB successfully\n");
  107. if (gArgs.GetBoolArg("-forcecompactdb", false)) {
  108. LogPrintf("Starting database compaction of %s\n", path.string());
  109. pdb->CompactRange(nullptr, nullptr);
  110. LogPrintf("Finished database compaction of %s\n", path.string());
  111. }
  112. // The base-case obfuscation key, which is a noop.
  113. obfuscate_key = std::vector<unsigned char>(OBFUSCATE_KEY_NUM_BYTES, '\000');
  114. bool key_exists = Read(OBFUSCATE_KEY_KEY, obfuscate_key);
  115. if (!key_exists && obfuscate && IsEmpty()) {
  116. // Initialize non-degenerate obfuscation if it won't upset
  117. // existing, non-obfuscated data.
  118. std::vector<unsigned char> new_key = CreateObfuscateKey();
  119. // Write `new_key` so we don't obfuscate the key with itself
  120. Write(OBFUSCATE_KEY_KEY, new_key);
  121. obfuscate_key = new_key;
  122. LogPrintf("Wrote new obfuscate key for %s: %s\n", path.string(), HexStr(obfuscate_key));
  123. }
  124. LogPrintf("Using obfuscation key for %s: %s\n", path.string(), HexStr(obfuscate_key));
  125. }
  126. CDBWrapper::~CDBWrapper()
  127. {
  128. delete pdb;
  129. pdb = nullptr;
  130. delete options.filter_policy;
  131. options.filter_policy = nullptr;
  132. delete options.info_log;
  133. options.info_log = nullptr;
  134. delete options.block_cache;
  135. options.block_cache = nullptr;
  136. delete penv;
  137. options.env = nullptr;
  138. }
  139. bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync)
  140. {
  141. leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch);
  142. dbwrapper_private::HandleError(status);
  143. return true;
  144. }
  145. // Prefixed with null character to avoid collisions with other keys
  146. //
  147. // We must use a string constructor which specifies length so that we copy
  148. // past the null-terminator.
  149. const std::string CDBWrapper::OBFUSCATE_KEY_KEY("\000obfuscate_key", 14);
  150. const unsigned int CDBWrapper::OBFUSCATE_KEY_NUM_BYTES = 8;
  151. /**
  152. * Returns a string (consisting of 8 random bytes) suitable for use as an
  153. * obfuscating XOR key.
  154. */
  155. std::vector<unsigned char> CDBWrapper::CreateObfuscateKey() const
  156. {
  157. unsigned char buff[OBFUSCATE_KEY_NUM_BYTES];
  158. GetRandBytes(buff, OBFUSCATE_KEY_NUM_BYTES);
  159. return std::vector<unsigned char>(&buff[0], &buff[OBFUSCATE_KEY_NUM_BYTES]);
  160. }
  161. bool CDBWrapper::IsEmpty()
  162. {
  163. std::unique_ptr<CDBIterator> it(NewIterator());
  164. it->SeekToFirst();
  165. return !(it->Valid());
  166. }
  167. CDBIterator::~CDBIterator() { delete piter; }
  168. bool CDBIterator::Valid() { return piter->Valid(); }
  169. void CDBIterator::SeekToFirst() { piter->SeekToFirst(); }
  170. void CDBIterator::Next() { piter->Next(); }
  171. namespace dbwrapper_private {
  172. void HandleError(const leveldb::Status& status)
  173. {
  174. if (status.ok())
  175. return;
  176. LogPrintf("%s\n", status.ToString());
  177. if (status.IsCorruption())
  178. throw dbwrapper_error("Database corrupted");
  179. if (status.IsIOError())
  180. throw dbwrapper_error("Database I/O error");
  181. if (status.IsNotFound())
  182. throw dbwrapper_error("Database entry missing");
  183. throw dbwrapper_error("Unknown database error");
  184. }
  185. const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w)
  186. {
  187. return w.obfuscate_key;
  188. }
  189. } // namespace dbwrapper_private