1 //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2 //  This source code is licensed under both the GPLv2 (found in the
3 //  COPYING file in the root directory) and Apache 2.0 License
4 //  (found in the LICENSE.Apache file in the root directory).
5 //
6 #ifndef ROCKSDB_LITE
7 
8 #include "utilities/blob_db/blob_log_format.h"
9 
10 #include "util/coding.h"
11 #include "util/crc32c.h"
12 
13 namespace ROCKSDB_NAMESPACE {
14 namespace blob_db {
15 
EncodeTo(std::string * dst)16 void BlobLogHeader::EncodeTo(std::string* dst) {
17   assert(dst != nullptr);
18   dst->clear();
19   dst->reserve(BlobLogHeader::kSize);
20   PutFixed32(dst, kMagicNumber);
21   PutFixed32(dst, version);
22   PutFixed32(dst, column_family_id);
23   unsigned char flags = (has_ttl ? 1 : 0);
24   dst->push_back(flags);
25   dst->push_back(compression);
26   PutFixed64(dst, expiration_range.first);
27   PutFixed64(dst, expiration_range.second);
28 }
29 
DecodeFrom(Slice src)30 Status BlobLogHeader::DecodeFrom(Slice src) {
31   static const std::string kErrorMessage =
32       "Error while decoding blob log header";
33   if (src.size() != BlobLogHeader::kSize) {
34     return Status::Corruption(kErrorMessage,
35                               "Unexpected blob file header size");
36   }
37   uint32_t magic_number;
38   unsigned char flags;
39   if (!GetFixed32(&src, &magic_number) || !GetFixed32(&src, &version) ||
40       !GetFixed32(&src, &column_family_id)) {
41     return Status::Corruption(
42         kErrorMessage,
43         "Error decoding magic number, version and column family id");
44   }
45   if (magic_number != kMagicNumber) {
46     return Status::Corruption(kErrorMessage, "Magic number mismatch");
47   }
48   if (version != kVersion1) {
49     return Status::Corruption(kErrorMessage, "Unknown header version");
50   }
51   flags = src.data()[0];
52   compression = static_cast<CompressionType>(src.data()[1]);
53   has_ttl = (flags & 1) == 1;
54   src.remove_prefix(2);
55   if (!GetFixed64(&src, &expiration_range.first) ||
56       !GetFixed64(&src, &expiration_range.second)) {
57     return Status::Corruption(kErrorMessage, "Error decoding expiration range");
58   }
59   return Status::OK();
60 }
61 
EncodeTo(std::string * dst)62 void BlobLogFooter::EncodeTo(std::string* dst) {
63   assert(dst != nullptr);
64   dst->clear();
65   dst->reserve(BlobLogFooter::kSize);
66   PutFixed32(dst, kMagicNumber);
67   PutFixed64(dst, blob_count);
68   PutFixed64(dst, expiration_range.first);
69   PutFixed64(dst, expiration_range.second);
70   crc = crc32c::Value(dst->c_str(), dst->size());
71   crc = crc32c::Mask(crc);
72   PutFixed32(dst, crc);
73 }
74 
DecodeFrom(Slice src)75 Status BlobLogFooter::DecodeFrom(Slice src) {
76   static const std::string kErrorMessage =
77       "Error while decoding blob log footer";
78   if (src.size() != BlobLogFooter::kSize) {
79     return Status::Corruption(kErrorMessage,
80                               "Unexpected blob file footer size");
81   }
82   uint32_t src_crc = 0;
83   src_crc = crc32c::Value(src.data(), BlobLogFooter::kSize - sizeof(uint32_t));
84   src_crc = crc32c::Mask(src_crc);
85   uint32_t magic_number = 0;
86   if (!GetFixed32(&src, &magic_number) || !GetFixed64(&src, &blob_count) ||
87       !GetFixed64(&src, &expiration_range.first) ||
88       !GetFixed64(&src, &expiration_range.second) || !GetFixed32(&src, &crc)) {
89     return Status::Corruption(kErrorMessage, "Error decoding content");
90   }
91   if (magic_number != kMagicNumber) {
92     return Status::Corruption(kErrorMessage, "Magic number mismatch");
93   }
94   if (src_crc != crc) {
95     return Status::Corruption(kErrorMessage, "CRC mismatch");
96   }
97   return Status::OK();
98 }
99 
EncodeHeaderTo(std::string * dst)100 void BlobLogRecord::EncodeHeaderTo(std::string* dst) {
101   assert(dst != nullptr);
102   dst->clear();
103   dst->reserve(BlobLogRecord::kHeaderSize + key.size() + value.size());
104   PutFixed64(dst, key.size());
105   PutFixed64(dst, value.size());
106   PutFixed64(dst, expiration);
107   header_crc = crc32c::Value(dst->c_str(), dst->size());
108   header_crc = crc32c::Mask(header_crc);
109   PutFixed32(dst, header_crc);
110   blob_crc = crc32c::Value(key.data(), key.size());
111   blob_crc = crc32c::Extend(blob_crc, value.data(), value.size());
112   blob_crc = crc32c::Mask(blob_crc);
113   PutFixed32(dst, blob_crc);
114 }
115 
DecodeHeaderFrom(Slice src)116 Status BlobLogRecord::DecodeHeaderFrom(Slice src) {
117   static const std::string kErrorMessage = "Error while decoding blob record";
118   if (src.size() != BlobLogRecord::kHeaderSize) {
119     return Status::Corruption(kErrorMessage,
120                               "Unexpected blob record header size");
121   }
122   uint32_t src_crc = 0;
123   src_crc = crc32c::Value(src.data(), BlobLogRecord::kHeaderSize - 8);
124   src_crc = crc32c::Mask(src_crc);
125   if (!GetFixed64(&src, &key_size) || !GetFixed64(&src, &value_size) ||
126       !GetFixed64(&src, &expiration) || !GetFixed32(&src, &header_crc) ||
127       !GetFixed32(&src, &blob_crc)) {
128     return Status::Corruption(kErrorMessage, "Error decoding content");
129   }
130   if (src_crc != header_crc) {
131     return Status::Corruption(kErrorMessage, "Header CRC mismatch");
132   }
133   return Status::OK();
134 }
135 
CheckBlobCRC() const136 Status BlobLogRecord::CheckBlobCRC() const {
137   uint32_t expected_crc = 0;
138   expected_crc = crc32c::Value(key.data(), key.size());
139   expected_crc = crc32c::Extend(expected_crc, value.data(), value.size());
140   expected_crc = crc32c::Mask(expected_crc);
141   if (expected_crc != blob_crc) {
142     return Status::Corruption("Blob CRC mismatch");
143   }
144   return Status::OK();
145 }
146 
147 }  // namespace blob_db
148 }  // namespace ROCKSDB_NAMESPACE
149 #endif  // ROCKSDB_LITE
150