Hello EZ,
In this post I will discuss how to get checksums/hash of a logical volume in an OS. There are several tools available which provides similar features but most of them are coded in C++/C# (maybe I am not sure). Some flavors of Linux comes packed with CLI tools like dc3dd/dcfldd which provides on the fly hash generation. I wanted to achieve this volume hash generation using java and after googling quite a bit I didn't quite find any good tutorials or references but I found something interesting
here. Thereafter I tried to access this drive as a file in Java and once I have a file object, its easier to get the checksum using java's builtin MessageDigest class.
The only catch being that you have to execute the code using admin privileges. The code I will be sharing now has been tested on windows and the resulting hash obtained has been verified by calculating the hash for a particular drive using a hex editor (Winhex).
1. Task One: Get list of all Logical Drives File[] drives = File.listRoots(); //Returns the system drive letters.
If you're interested interested in knowing the type of drive then use the following snippet:
FileSystemView fsv = FileSystemView.getFileSystemView();
for (File f : drives){
System.out.println(fsv.getSystemTypeDescription(f));
}
The above outputs something like the following where Local Disk means a logical volume (namely C:\, D:\ etc.) To get the display name for each drive use the function
getSystemDisplayName from FileSystemView class.
Local Disk
Local Disk
Local Disk
CD Drive
1. Task Two: Understanding how to access the drives as file objects.According to the link I gave earlier to get raw access to logical drives we should map the path as
\\.\[Drive Letter]. Say if we have a function
getVolumeHash(File file, String hashAlgo) where the first parameter is the complete path to the logical volume then we will pass the arguments as follows:
File compLoc = new File("\\\\.\\" + drives[0].toString());
byte[] mdbytes = getVolumeHash(compLoc, "SHA1"); //Let's take SHA1 for now.
The above will return the digest of the volume as an array of bytes which we can convert to hex using the following method.
/**
* Converts array of bytes to hex string.
*
* @param bytes Byte Array to be converted to Hex String.
* @return Returns the hex string for {@code bytes} array.
*/
public static String byteArrayToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).
substring(1));
}
return sb.toString();
}
So, Here the complete code (Only class):-
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
*
* @author Psycho_Coder
*/
public class DigestDiskVolume {
/**
* Converts array of bytes to hex string.
*
* @param bytes Byte Array to be converted to Hex String.
* @return Returns the hex string for {@code bytes} array.
*/
public static String byteArrayToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).
substring(1));
}
return sb.toString();
}
/**
* Array of Logical Volumes
*
* @return returns File array of Logical Volumes
*/
public static File[] getSystemLogicalVolumes() {
return File.listRoots();
}
/**
* <p>
* Returns the hash of the file whose path and the type of Hashing Algo
* scheme is passed as arguments to the method.</p>
*
* @param file Logical Volume which is to be hashed.
* @param hashAlgo Hashing algorithm to be used.
* @return returns Hex encoded hash of the file
*/
public static String getVolumeHash(File file, String hashAlgo) {
byte[] mdbytes = null;
try (RandomAccessFile raf = new RandomAccessFile(file, "r");
FileChannel fc = raf.getChannel();) {
ByteBuffer buffer = ByteBuffer.allocate(4096);
MessageDigest md = MessageDigest.getInstance(hashAlgo);
while (fc.read(buffer) != -1) {
buffer.flip();
md.update(buffer);
buffer.clear();
}
mdbytes = md.digest();
} catch (NoSuchAlgorithmException | FileNotFoundException ex) {
System.err.println(ex.getMessage());
} catch (IOException ex) {
System.err.println(ex.getMessage());
}
return byteArrayToHex(mdbytes);
}
}
I believe the code performance can be increased if we use
MappedByteBuffer. Well you can have a go. I hope you like this short tutorial and learned something new today. If you have some doubts please comment. I will try my best to answer your query.
Thanking you,
Sincerely,
Psycho_Coder.