姚凯文 姜嘉琪
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.

274 lines
8.7 KiB

  1. // Copyright (c) 2011 The LevelDB 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. See the AUTHORS file for names of contributors.
  4. #include "db/log_reader.h"
  5. #include <cstdio>
  6. #include "leveldb/env.h"
  7. #include "util/coding.h"
  8. #include "util/crc32c.h"
  9. namespace leveldb {
  10. namespace log {
  11. Reader::Reporter::~Reporter() = default;
  12. Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum,
  13. uint64_t initial_offset)
  14. : file_(file),
  15. reporter_(reporter),
  16. checksum_(checksum),
  17. backing_store_(new char[kBlockSize]),
  18. buffer_(),
  19. eof_(false),
  20. last_record_offset_(0),
  21. end_of_buffer_offset_(0),
  22. initial_offset_(initial_offset),
  23. resyncing_(initial_offset > 0) {}
  24. Reader::~Reader() { delete[] backing_store_; }
  25. bool Reader::SkipToInitialBlock() {
  26. const size_t offset_in_block = initial_offset_ % kBlockSize;
  27. uint64_t block_start_location = initial_offset_ - offset_in_block;
  28. // Don't search a block if we'd be in the trailer
  29. if (offset_in_block > kBlockSize - 6) {
  30. block_start_location += kBlockSize;
  31. }
  32. end_of_buffer_offset_ = block_start_location;
  33. // Skip to start of first block that can contain the initial record
  34. if (block_start_location > 0) {
  35. Status skip_status = file_->Skip(block_start_location);
  36. if (!skip_status.ok()) {
  37. ReportDrop(block_start_location, skip_status);
  38. return false;
  39. }
  40. }
  41. return true;
  42. }
  43. bool Reader::ReadRecord(Slice* record, std::string* scratch) {
  44. if (last_record_offset_ < initial_offset_) {
  45. if (!SkipToInitialBlock()) {
  46. return false;
  47. }
  48. }
  49. scratch->clear();
  50. record->clear();
  51. bool in_fragmented_record = false;
  52. // Record offset of the logical record that we're reading
  53. // 0 is a dummy value to make compilers happy
  54. uint64_t prospective_record_offset = 0;
  55. Slice fragment;
  56. while (true) {
  57. const unsigned int record_type = ReadPhysicalRecord(&fragment);
  58. // ReadPhysicalRecord may have only had an empty trailer remaining in its
  59. // internal buffer. Calculate the offset of the next physical record now
  60. // that it has returned, properly accounting for its header size.
  61. uint64_t physical_record_offset =
  62. end_of_buffer_offset_ - buffer_.size() - kHeaderSize - fragment.size();
  63. if (resyncing_) {
  64. if (record_type == kMiddleType) {
  65. continue;
  66. } else if (record_type == kLastType) {
  67. resyncing_ = false;
  68. continue;
  69. } else {
  70. resyncing_ = false;
  71. }
  72. }
  73. switch (record_type) {
  74. case kFullType:
  75. if (in_fragmented_record) {
  76. // Handle bug in earlier versions of log::Writer where
  77. // it could emit an empty kFirstType record at the tail end
  78. // of a block followed by a kFullType or kFirstType record
  79. // at the beginning of the next block.
  80. if (!scratch->empty()) {
  81. ReportCorruption(scratch->size(), "partial record without end(1)");
  82. }
  83. }
  84. prospective_record_offset = physical_record_offset;
  85. scratch->clear();
  86. *record = fragment;
  87. last_record_offset_ = prospective_record_offset;
  88. return true;
  89. case kFirstType:
  90. if (in_fragmented_record) {
  91. // Handle bug in earlier versions of log::Writer where
  92. // it could emit an empty kFirstType record at the tail end
  93. // of a block followed by a kFullType or kFirstType record
  94. // at the beginning of the next block.
  95. if (!scratch->empty()) {
  96. ReportCorruption(scratch->size(), "partial record without end(2)");
  97. }
  98. }
  99. prospective_record_offset = physical_record_offset;
  100. scratch->assign(fragment.data(), fragment.size());
  101. in_fragmented_record = true;
  102. break;
  103. case kMiddleType:
  104. if (!in_fragmented_record) {
  105. ReportCorruption(fragment.size(),
  106. "missing start of fragmented record(1)");
  107. } else {
  108. scratch->append(fragment.data(), fragment.size());
  109. }
  110. break;
  111. case kLastType:
  112. if (!in_fragmented_record) {
  113. ReportCorruption(fragment.size(),
  114. "missing start of fragmented record(2)");
  115. } else {
  116. scratch->append(fragment.data(), fragment.size());
  117. *record = Slice(*scratch);
  118. last_record_offset_ = prospective_record_offset;
  119. return true;
  120. }
  121. break;
  122. case kEof:
  123. if (in_fragmented_record) {
  124. // This can be caused by the writer dying immediately after
  125. // writing a physical record but before completing the next; don't
  126. // treat it as a corruption, just ignore the entire logical record.
  127. scratch->clear();
  128. }
  129. return false;
  130. case kBadRecord:
  131. if (in_fragmented_record) {
  132. ReportCorruption(scratch->size(), "error in middle of record");
  133. in_fragmented_record = false;
  134. scratch->clear();
  135. }
  136. break;
  137. default: {
  138. char buf[40];
  139. std::snprintf(buf, sizeof(buf), "unknown record type %u", record_type);
  140. ReportCorruption(
  141. (fragment.size() + (in_fragmented_record ? scratch->size() : 0)),
  142. buf);
  143. in_fragmented_record = false;
  144. scratch->clear();
  145. break;
  146. }
  147. }
  148. }
  149. return false;
  150. }
  151. uint64_t Reader::LastRecordOffset() { return last_record_offset_; }
  152. void Reader::ReportCorruption(uint64_t bytes, const char* reason) {
  153. ReportDrop(bytes, Status::Corruption(reason));
  154. }
  155. void Reader::ReportDrop(uint64_t bytes, const Status& reason) {
  156. if (reporter_ != nullptr &&
  157. end_of_buffer_offset_ - buffer_.size() - bytes >= initial_offset_) {
  158. reporter_->Corruption(static_cast<size_t>(bytes), reason);
  159. }
  160. }
  161. unsigned int Reader::ReadPhysicalRecord(Slice* result) {
  162. while (true) {
  163. if (buffer_.size() < kHeaderSize) {
  164. if (!eof_) {
  165. // Last read was a full read, so this is a trailer to skip
  166. buffer_.clear();
  167. Status status = file_->Read(kBlockSize, &buffer_, backing_store_);
  168. end_of_buffer_offset_ += buffer_.size();
  169. if (!status.ok()) {
  170. buffer_.clear();
  171. ReportDrop(kBlockSize, status);
  172. eof_ = true;
  173. return kEof;
  174. } else if (buffer_.size() < kBlockSize) {
  175. eof_ = true;
  176. }
  177. continue;
  178. } else {
  179. // Note that if buffer_ is non-empty, we have a truncated header at the
  180. // end of the file, which can be caused by the writer crashing in the
  181. // middle of writing the header. Instead of considering this an error,
  182. // just report EOF.
  183. buffer_.clear();
  184. return kEof;
  185. }
  186. }
  187. // Parse the header
  188. const char* header = buffer_.data();
  189. const uint32_t a = static_cast<uint32_t>(header[4]) & 0xff;
  190. const uint32_t b = static_cast<uint32_t>(header[5]) & 0xff;
  191. const unsigned int type = header[6];
  192. const uint32_t length = a | (b << 8);
  193. if (kHeaderSize + length > buffer_.size()) {
  194. size_t drop_size = buffer_.size();
  195. buffer_.clear();
  196. if (!eof_) {
  197. ReportCorruption(drop_size, "bad record length");
  198. return kBadRecord;
  199. }
  200. // If the end of the file has been reached without reading |length| bytes
  201. // of payload, assume the writer died in the middle of writing the record.
  202. // Don't report a corruption.
  203. return kEof;
  204. }
  205. if (type == kZeroType && length == 0) {
  206. // Skip zero length record without reporting any drops since
  207. // such records are produced by the mmap based writing code in
  208. // env_posix.cc that preallocates file regions.
  209. buffer_.clear();
  210. return kBadRecord;
  211. }
  212. // Check crc
  213. if (checksum_) {
  214. uint32_t expected_crc = crc32c::Unmask(DecodeFixed32(header));
  215. uint32_t actual_crc = crc32c::Value(header + 6, 1 + length);
  216. if (actual_crc != expected_crc) {
  217. // Drop the rest of the buffer since "length" itself may have
  218. // been corrupted and if we trust it, we could find some
  219. // fragment of a real log record that just happens to look
  220. // like a valid log record.
  221. size_t drop_size = buffer_.size();
  222. buffer_.clear();
  223. ReportCorruption(drop_size, "checksum mismatch");
  224. return kBadRecord;
  225. }
  226. }
  227. buffer_.remove_prefix(kHeaderSize + length);
  228. // Skip physical record that started before initial_offset_
  229. if (end_of_buffer_offset_ - buffer_.size() - kHeaderSize - length <
  230. initial_offset_) {
  231. result->clear();
  232. return kBadRecord;
  233. }
  234. *result = Slice(header + kHeaderSize, length);
  235. return type;
  236. }
  237. }
  238. } // namespace log
  239. } // namespace leveldb