package org.argeo.cms.security; import static javax.xml.bind.DatatypeConverter.printBase64Binary; import java.io.IOException; import java.math.BigInteger; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.security.MessageDigest; import java.util.zip.Checksum; import org.argeo.cms.CmsException; /** Allows to fine tune how files are read. */ public class ChecksumFactory { private int regionSize = 10 * 1024 * 1024; public byte[] digest(Path path, final String algo) { try { final MessageDigest md = MessageDigest.getInstance(algo); if (Files.isDirectory(path)) { long begin = System.currentTimeMillis(); Files.walkFileTree(path, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (!Files.isDirectory(file)) { byte[] digest = digest(file, algo); md.update(digest); } return FileVisitResult.CONTINUE; } }); byte[] digest = md.digest(); long duration = System.currentTimeMillis() - begin; System.out.println(printBase64Binary(digest) + " " + path + " (" + duration / 1000 + "s)"); return digest; } else { long begin = System.nanoTime(); long length = -1; try (FileChannel channel = (FileChannel) Files .newByteChannel(path);) { length = channel.size(); long cursor = 0; while (cursor < length) { long effectiveSize = Math.min(regionSize, length - cursor); MappedByteBuffer mb = channel.map( FileChannel.MapMode.READ_ONLY, cursor, effectiveSize); // md.update(mb); byte[] buffer = new byte[1024]; while (mb.hasRemaining()){ mb.get(buffer); md.update(buffer); } // sub digest // mb.flip(); // MessageDigest subMd = // MessageDigest.getInstance(algo); // subMd.update(mb); // byte[] subDigest = subMd.digest(); // System.out.println(" -> " + cursor); // System.out.println(IOUtils.encodeHexString(subDigest)); // System.out.println(new BigInteger(1, // subDigest).toString(16)); // System.out.println(new BigInteger(1, subDigest) // .toString(Character.MAX_RADIX)); // System.out.println(printBase64Binary(subDigest)); cursor = cursor + regionSize; } byte[] digest = md.digest(); long duration = System.nanoTime() - begin; System.out.println(printBase64Binary(digest) + " " + path.getFileName() + " (" + duration / 1000000 + "ms, " + (length / 1024) + "kB, " + (length / (duration / 1000000)) * 1000 / (1024 * 1024) + " MB/s)"); return digest; } } } catch (Exception e) { throw new CmsException("Cannot digest " + path, e); } } /** Whether the file should be mapped. */ protected boolean mapFile(FileChannel fileChannel) throws IOException { long size = fileChannel.size(); if (size > (regionSize / 10)) return true; return false; } public long checksum(Path path, Checksum crc) { final int bufferSize = 2 * 1024 * 1024; long begin = System.currentTimeMillis(); try (FileChannel channel = (FileChannel) Files.newByteChannel(path);) { byte[] bytes = new byte[bufferSize]; long length = channel.size(); long cursor = 0; while (cursor < length) { long effectiveSize = Math.min(regionSize, length - cursor); MappedByteBuffer mb = channel.map( FileChannel.MapMode.READ_ONLY, cursor, effectiveSize); int nGet; while (mb.hasRemaining()) { nGet = Math.min(mb.remaining(), bufferSize); mb.get(bytes, 0, nGet); crc.update(bytes, 0, nGet); } cursor = cursor + regionSize; } return crc.getValue(); } catch (Exception e) { throw new CmsException("Cannot checksum " + path, e); } finally { long duration = System.currentTimeMillis() - begin; System.out.println(duration / 1000 + "s"); } } public static void main(String... args) { ChecksumFactory cf = new ChecksumFactory(); // Path path = // Paths.get("/home/mbaudier/apache-maven-3.2.3-bin.tar.gz"); Path path; if (args.length > 0) { path = Paths.get(args[0]); } else { path = Paths .get("/home/mbaudier/Downloads/torrents/CentOS-7-x86_64-DVD-1503-01/" + "CentOS-7-x86_64-DVD-1503-01.iso"); } // long adler = cf.checksum(path, new Adler32()); // System.out.format("Adler=%d%n", adler); // long crc = cf.checksum(path, new CRC32()); // System.out.format("CRC=%d%n", crc); String algo = "SHA1"; byte[] digest = cf.digest(path, algo); System.out.println(algo + " " + printBase64Binary(digest)); System.out.println(algo + " " + new BigInteger(1, digest).toString(16)); // String sha1 = printBase64Binary(cf.digest(path, "SHA1")); // System.out.format("SHA1=%s%n", sha1); } }