001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.io.hfile.bucket; 019 020import java.io.File; 021import java.io.IOException; 022import java.security.MessageDigest; 023import java.security.NoSuchAlgorithmException; 024import org.apache.hadoop.hbase.util.Bytes; 025import org.apache.hadoop.util.Shell; 026import org.apache.yetus.audience.InterfaceAudience; 027import org.slf4j.Logger; 028import org.slf4j.LoggerFactory; 029 030/** 031 * A class implementing PersistentIOEngine interface supports file integrity verification for 032 * {@link BucketCache} which use persistent IOEngine 033 */ 034@InterfaceAudience.Private 035public abstract class PersistentIOEngine implements IOEngine { 036 private static final Logger LOG = LoggerFactory.getLogger(PersistentIOEngine.class); 037 private static final DuFileCommand DU = new DuFileCommand(new String[] { "du", "" }); 038 protected final String[] filePaths; 039 040 public PersistentIOEngine(String... filePaths) { 041 this.filePaths = filePaths; 042 } 043 044 /** 045 * Verify cache files's integrity 046 * @param algorithm the backingMap persistence path 047 */ 048 protected void verifyFileIntegrity(byte[] persistentChecksum, String algorithm) 049 throws IOException { 050 byte[] calculateChecksum = calculateChecksum(algorithm); 051 if (!Bytes.equals(persistentChecksum, calculateChecksum)) { 052 throw new IOException( 053 "Mismatch of checksum! The persistent checksum is " + Bytes.toString(persistentChecksum) 054 + ", but the calculate checksum is " + Bytes.toString(calculateChecksum)); 055 } 056 } 057 058 /** 059 * Using an encryption algorithm to calculate a checksum, the default encryption algorithm is MD5 060 * @return the checksum which is convert to HexString 061 * @throws IOException something happened like file not exists 062 * @throws NoSuchAlgorithmException no such algorithm 063 */ 064 protected byte[] calculateChecksum(String algorithm) { 065 try { 066 StringBuilder sb = new StringBuilder(); 067 for (String filePath : filePaths) { 068 File file = new File(filePath); 069 sb.append(filePath); 070 sb.append(getFileSize(filePath)); 071 sb.append(file.lastModified()); 072 } 073 MessageDigest messageDigest = MessageDigest.getInstance(algorithm); 074 messageDigest.update(Bytes.toBytes(sb.toString())); 075 return messageDigest.digest(); 076 } catch (IOException ioex) { 077 LOG.error("Calculating checksum failed, because of ", ioex); 078 return new byte[0]; 079 } catch (NoSuchAlgorithmException e) { 080 LOG.error("No such algorithm : " + algorithm + "!"); 081 return new byte[0]; 082 } 083 } 084 085 /** 086 * Using Linux command du to get file's real size 087 * @param filePath the file 088 * @return file's real size 089 * @throws IOException something happened like file not exists 090 */ 091 private static long getFileSize(String filePath) throws IOException { 092 DU.setExecCommand(filePath); 093 DU.execute(); 094 return Long.parseLong(DU.getOutput().split("\t")[0]); 095 } 096 097 private static class DuFileCommand extends Shell.ShellCommandExecutor { 098 private String[] execCommand; 099 100 DuFileCommand(String[] execString) { 101 super(execString); 102 execCommand = execString; 103 } 104 105 void setExecCommand(String filePath) { 106 this.execCommand[1] = filePath; 107 } 108 109 @Override 110 public String[] getExecString() { 111 return this.execCommand; 112 } 113 } 114}