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.procedure2.util.StringUtils; 025import org.apache.hadoop.hbase.util.Bytes; 026import org.apache.hadoop.util.Shell; 027import org.apache.yetus.audience.InterfaceAudience; 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031/** 032 * A class implementing PersistentIOEngine interface supports file integrity verification for 033 * {@link BucketCache} which use persistent IOEngine 034 */ 035@InterfaceAudience.Private 036public abstract class PersistentIOEngine implements IOEngine { 037 private static final Logger LOG = LoggerFactory.getLogger(PersistentIOEngine.class); 038 private static final DuFileCommand DU = new DuFileCommand(new String[] { "du", "" }); 039 protected final String[] filePaths; 040 041 public PersistentIOEngine(String... filePaths) { 042 this.filePaths = filePaths; 043 } 044 045 /** 046 * Verify cache files's integrity 047 * @param algorithm the backingMap persistence path 048 */ 049 protected void verifyFileIntegrity(byte[] persistentChecksum, String algorithm) 050 throws IOException { 051 byte[] calculateChecksum = calculateChecksum(algorithm); 052 if (!Bytes.equals(persistentChecksum, calculateChecksum)) { 053 throw new IOException( 054 "Mismatch of checksum! The persistent checksum is " + Bytes.toString(persistentChecksum) 055 + ", but the calculate checksum is " + Bytes.toString(calculateChecksum)); 056 } 057 } 058 059 /** 060 * Using an encryption algorithm to calculate a checksum, the default encryption algorithm is MD5 061 * @return the checksum which is convert to HexString 062 * @throws IOException something happened like file not exists 063 * @throws NoSuchAlgorithmException no such algorithm 064 */ 065 protected byte[] calculateChecksum(String algorithm) { 066 try { 067 StringBuilder sb = new StringBuilder(); 068 for (String filePath : filePaths) { 069 File file = new File(filePath); 070 sb.append(filePath); 071 sb.append(getFileSize(filePath)); 072 sb.append(file.lastModified()); 073 } 074 MessageDigest messageDigest = MessageDigest.getInstance(algorithm); 075 messageDigest.update(Bytes.toBytes(sb.toString())); 076 return messageDigest.digest(); 077 } catch (IOException ioex) { 078 LOG.error("Calculating checksum failed, because of ", ioex); 079 return new byte[0]; 080 } catch (NoSuchAlgorithmException e) { 081 LOG.error("No such algorithm : " + algorithm + "!"); 082 return new byte[0]; 083 } 084 } 085 086 /** 087 * Using Linux command du to get file's real size 088 * @param filePath the file 089 * @return file's real size 090 * @throws IOException something happened like file not exists 091 */ 092 private static long getFileSize(String filePath) throws IOException { 093 DU.setExecCommand(filePath); 094 DU.execute(); 095 String size = DU.getOutput().split("\t")[0]; 096 return StringUtils.isEmpty(size.trim()) ? 0 : Long.parseLong(size); 097 } 098 099 private static class DuFileCommand extends Shell.ShellCommandExecutor { 100 private String[] execCommand; 101 102 DuFileCommand(String[] execString) { 103 super(execString); 104 execCommand = execString; 105 } 106 107 void setExecCommand(String filePath) { 108 this.execCommand[1] = filePath; 109 } 110 111 @Override 112 public String[] getExecString() { 113 return this.execCommand; 114 } 115 } 116}