小组成员:谢瑞阳、徐翔宇
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.

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