1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.io.hfile;
19
20 import java.io.IOException;
21 import java.security.Key;
22 import java.security.KeyException;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.apache.hadoop.hbase.classification.InterfaceAudience;
27 import org.apache.hadoop.conf.Configuration;
28 import org.apache.hadoop.fs.Path;
29 import org.apache.hadoop.hbase.Cell;
30 import org.apache.hadoop.hbase.CellUtil;
31 import org.apache.hadoop.hbase.HConstants;
32 import org.apache.hadoop.hbase.KeyValue;
33 import org.apache.hadoop.hbase.NoTagsKeyValue;
34 import org.apache.hadoop.hbase.fs.HFileSystem;
35 import org.apache.hadoop.hbase.io.FSDataInputStreamWrapper;
36 import org.apache.hadoop.hbase.io.crypto.Cipher;
37 import org.apache.hadoop.hbase.io.crypto.Encryption;
38 import org.apache.hadoop.hbase.io.hfile.HFile.FileInfo;
39 import org.apache.hadoop.hbase.security.EncryptionUtil;
40 import org.apache.hadoop.hbase.security.User;
41 import org.apache.hadoop.hbase.util.ByteBufferUtils;
42 import org.apache.hadoop.hbase.util.Bytes;
43 import org.apache.hadoop.io.WritableUtils;
44
45
46
47
48 @InterfaceAudience.Private
49 public class HFileReaderV3 extends HFileReaderV2 {
50
51 private static final Log LOG = LogFactory.getLog(HFileReaderV3.class);
52
53 public static final int MAX_MINOR_VERSION = 0;
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73 public HFileReaderV3(final Path path, FixedFileTrailer trailer,
74 final FSDataInputStreamWrapper fsdis,
75 final long size, final CacheConfig cacheConf, final HFileSystem hfs,
76 final Configuration conf) throws IOException {
77 super(path, trailer, fsdis, size, cacheConf, hfs, conf);
78 byte[] tmp = fileInfo.get(FileInfo.MAX_TAGS_LEN);
79
80 if (tmp != null) {
81 hfileContext.setIncludesTags(true);
82 tmp = fileInfo.get(FileInfo.TAGS_COMPRESSED);
83 if (tmp != null && Bytes.toBoolean(tmp)) {
84 hfileContext.setCompressTags(true);
85 }
86 }
87 }
88
89 @Override
90 protected HFileContext createHFileContext(FSDataInputStreamWrapper fsdis, long fileSize,
91 HFileSystem hfs, Path path, FixedFileTrailer trailer) throws IOException {
92 trailer.expectMajorVersion(3);
93 HFileContextBuilder builder = new HFileContextBuilder()
94 .withIncludesMvcc(shouldIncludeMemstoreTS())
95 .withHBaseCheckSum(true)
96 .withCompression(this.compressAlgo);
97
98
99 byte[] keyBytes = trailer.getEncryptionKey();
100 if (keyBytes != null) {
101 Encryption.Context cryptoContext = Encryption.newContext(conf);
102 Key key;
103 String masterKeyName = conf.get(HConstants.CRYPTO_MASTERKEY_NAME_CONF_KEY,
104 User.getCurrent().getShortName());
105 try {
106
107 key = EncryptionUtil.unwrapKey(conf, masterKeyName, keyBytes);
108 } catch (KeyException e) {
109
110
111 if (LOG.isDebugEnabled()) {
112 LOG.debug("Unable to unwrap key with current master key '" + masterKeyName + "'");
113 }
114 String alternateKeyName =
115 conf.get(HConstants.CRYPTO_MASTERKEY_ALTERNATE_NAME_CONF_KEY);
116 if (alternateKeyName != null) {
117 try {
118 key = EncryptionUtil.unwrapKey(conf, alternateKeyName, keyBytes);
119 } catch (KeyException ex) {
120 throw new IOException(ex);
121 }
122 } else {
123 throw new IOException(e);
124 }
125 }
126
127 Cipher cipher = Encryption.getCipher(conf, key.getAlgorithm());
128 if (cipher == null) {
129 throw new IOException("Cipher '" + key.getAlgorithm() + "' is not available");
130 }
131 cryptoContext.setCipher(cipher);
132 cryptoContext.setKey(key);
133 builder.withEncryptionContext(cryptoContext);
134 }
135
136 HFileContext context = builder.build();
137
138 if (LOG.isTraceEnabled()) {
139 LOG.trace("Reader" + (path != null ? " for " + path : "" ) +
140 " initialized with cacheConf: " + cacheConf +
141 " comparator: " + comparator.getClass().getSimpleName() +
142 " fileContext: " + context);
143 }
144
145 return context;
146 }
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162 @Override
163 public HFileScanner getScanner(boolean cacheBlocks, final boolean pread,
164 final boolean isCompaction) {
165 if (dataBlockEncoder.useEncodedScanner()) {
166 return new EncodedScannerV3(this, cacheBlocks, pread, isCompaction, this.hfileContext);
167 }
168 return new ScannerV3(this, cacheBlocks, pread, isCompaction);
169 }
170
171
172
173
174 protected static class ScannerV3 extends ScannerV2 {
175
176 private HFileReaderV3 reader;
177 private int currTagsLen;
178
179 public ScannerV3(HFileReaderV3 r, boolean cacheBlocks, final boolean pread,
180 final boolean isCompaction) {
181 super(r, cacheBlocks, pread, isCompaction);
182 this.reader = r;
183 }
184
185 @Override
186 protected int getCellBufSize() {
187 int kvBufSize = super.getCellBufSize();
188 if (reader.hfileContext.isIncludesTags()) {
189 kvBufSize += Bytes.SIZEOF_SHORT + currTagsLen;
190 }
191 return kvBufSize;
192 }
193
194 @Override
195 public Cell getKeyValue() {
196 if (!isSeeked())
197 return null;
198 if (currTagsLen > 0) {
199 KeyValue ret = new KeyValue(blockBuffer.array(), blockBuffer.arrayOffset()
200 + blockBuffer.position(), getCellBufSize());
201 if (this.reader.shouldIncludeMemstoreTS()) {
202 ret.setSequenceId(currMemstoreTS);
203 }
204 return ret;
205 } else {
206 return formNoTagsKeyValue();
207 }
208 }
209
210 protected void setNonSeekedState() {
211 super.setNonSeekedState();
212 currTagsLen = 0;
213 }
214
215 @Override
216 protected int getNextCellStartPosition() {
217 int nextKvPos = super.getNextCellStartPosition();
218 if (reader.hfileContext.isIncludesTags()) {
219 nextKvPos += Bytes.SIZEOF_SHORT + currTagsLen;
220 }
221 return nextKvPos;
222 }
223
224 private final void checkTagsLen() {
225 if (checkLen(this.currTagsLen)) {
226 throw new IllegalStateException("Invalid currTagsLen " + this.currTagsLen +
227 ". Block offset: " + block.getOffset() + ", block length: " + this.blockBuffer.limit() +
228 ", position: " + this.blockBuffer.position() + " (without header).");
229 }
230 }
231
232 protected final void readKeyValueLen() {
233
234
235
236
237
238 int p = blockBuffer.position() + blockBuffer.arrayOffset();
239
240
241 long ll = Bytes.toLong(blockBuffer.array(), p);
242
243 this.currKeyLen = (int)(ll >> Integer.SIZE);
244 this.currValueLen = (int)(Bytes.MASK_FOR_LOWER_INT_IN_LONG ^ ll);
245 checkKeyValueLen();
246
247 p += (Bytes.SIZEOF_LONG + currKeyLen + currValueLen);
248 if (reader.hfileContext.isIncludesTags()) {
249
250 this.currTagsLen = Bytes.toShort(blockBuffer.array(), p);
251 checkTagsLen();
252 p += (Bytes.SIZEOF_SHORT + currTagsLen);
253 }
254 readMvccVersion(p);
255 }
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272 @Override
273 protected int blockSeek(Cell key, boolean seekBefore) {
274 int klen, vlen, tlen = 0;
275 long memstoreTS = 0;
276 int memstoreTSLen = 0;
277 int lastKeyValueSize = -1;
278 KeyValue.KeyOnlyKeyValue keyOnlyKv = new KeyValue.KeyOnlyKeyValue();
279 do {
280 blockBuffer.mark();
281 klen = blockBuffer.getInt();
282 vlen = blockBuffer.getInt();
283 if (klen < 0 || vlen < 0 || klen > blockBuffer.limit()
284 || vlen > blockBuffer.limit()) {
285 throw new IllegalStateException("Invalid klen " + klen + " or vlen "
286 + vlen + ". Block offset: "
287 + block.getOffset() + ", block length: " + blockBuffer.limit() + ", position: "
288 + blockBuffer.position() + " (without header).");
289 }
290 ByteBufferUtils.skip(blockBuffer, klen + vlen);
291 if (reader.hfileContext.isIncludesTags()) {
292
293 tlen = ((blockBuffer.get() & 0xff) << 8) ^ (blockBuffer.get() & 0xff);
294 if (tlen < 0 || tlen > blockBuffer.limit()) {
295 throw new IllegalStateException("Invalid tlen " + tlen + ". Block offset: "
296 + block.getOffset() + ", block length: " + blockBuffer.limit() + ", position: "
297 + blockBuffer.position() + " (without header).");
298 }
299 ByteBufferUtils.skip(blockBuffer, tlen);
300 }
301 if (this.reader.shouldIncludeMemstoreTS()) {
302 if (this.reader.decodeMemstoreTS) {
303 memstoreTS = Bytes.readAsVLong(blockBuffer.array(), blockBuffer.arrayOffset()
304 + blockBuffer.position());
305 memstoreTSLen = WritableUtils.getVIntSize(memstoreTS);
306 } else {
307 memstoreTS = 0;
308 memstoreTSLen = 1;
309 }
310 }
311 blockBuffer.reset();
312 int keyOffset =
313 blockBuffer.arrayOffset() + blockBuffer.position() + (Bytes.SIZEOF_INT * 2);
314 keyOnlyKv.setKey(blockBuffer.array(), keyOffset, klen);
315 int comp = reader.getComparator().compareOnlyKeyPortion(key, keyOnlyKv);
316
317 if (comp == 0) {
318 if (seekBefore) {
319 if (lastKeyValueSize < 0) {
320 throw new IllegalStateException("blockSeek with seekBefore "
321 + "at the first key of the block: key="
322 + CellUtil.getCellKeyAsString(key)
323 + ", blockOffset=" + block.getOffset() + ", onDiskSize="
324 + block.getOnDiskSizeWithHeader());
325 }
326 blockBuffer.position(blockBuffer.position() - lastKeyValueSize);
327 readKeyValueLen();
328 return 1;
329 }
330 currKeyLen = klen;
331 currValueLen = vlen;
332 currTagsLen = tlen;
333 if (this.reader.shouldIncludeMemstoreTS()) {
334 currMemstoreTS = memstoreTS;
335 currMemstoreTSLen = memstoreTSLen;
336 }
337 return 0;
338 } else if (comp < 0) {
339 if (lastKeyValueSize > 0)
340 blockBuffer.position(blockBuffer.position() - lastKeyValueSize);
341 readKeyValueLen();
342 if (lastKeyValueSize == -1 && blockBuffer.position() == 0) {
343 return HConstants.INDEX_KEY_MAGIC;
344 }
345 return 1;
346 }
347
348
349 lastKeyValueSize = klen + vlen + memstoreTSLen + KEY_VALUE_LEN_SIZE;
350
351 if (reader.hfileContext.isIncludesTags()) {
352 lastKeyValueSize += tlen + Bytes.SIZEOF_SHORT;
353 }
354 blockBuffer.position(blockBuffer.position() + lastKeyValueSize);
355 } while (blockBuffer.remaining() > 0);
356
357
358
359
360 blockBuffer.position(blockBuffer.position() - lastKeyValueSize);
361 readKeyValueLen();
362 return 1;
363 }
364 }
365
366
367
368
369 protected static class EncodedScannerV3 extends EncodedScannerV2 {
370 public EncodedScannerV3(HFileReaderV3 reader, boolean cacheBlocks, boolean pread,
371 boolean isCompaction, HFileContext context) {
372 super(reader, cacheBlocks, pread, isCompaction, context);
373 }
374 }
375
376 @Override
377 public int getMajorVersion() {
378 return 3;
379 }
380 }