/*
* Copyright (c) 2012-2015 iWave Software LLC
* All Rights Reserved
*/
package com.iwave.ext.windows.scsi;
import com.google.common.collect.Maps;
import org.apache.commons.codec.binary.Hex;
import java.util.Map;
/**
* This contains tools for parsing the SCSI DeviceIdentifierPage in Windows.
*/
public class DeviceIdentification {
private static final byte HEADER_QUALIFIER_DEVICE_TYPE = 0x00;
private static final byte HEADER_PAGE_CODE = (byte) 0x83;
private static final byte CODE_SET_BINARY = 0x01;
private static final byte CODE_SET_ASCII = 0x02;
private static final int MIN_LENGTH = 8;
private static final int NAA_IDENTIFIER_TYPE = 3;
private static final int ALTERNATE_IDENTIFIER_TYPE = 2;
public static String getWwid(byte[] deviceIdentifierPage) {
Map<Integer, String> identifiers = parseIdentifiers(deviceIdentifierPage);
String identifier = identifiers.get(NAA_IDENTIFIER_TYPE) == null ? identifiers.get(ALTERNATE_IDENTIFIER_TYPE) : identifiers
.get(NAA_IDENTIFIER_TYPE);
if (identifier == null) {
throw new IllegalArgumentException("No device identifier found in the device identifier page in the registry");
}
return identifier;
}
public static Map<Integer, String> parseIdentifiers(byte[] bytes) {
if (bytes.length < MIN_LENGTH) {
throw new IllegalArgumentException("Not enough data from device identifier page");
}
if (bytes[0] != HEADER_QUALIFIER_DEVICE_TYPE && bytes[1] != HEADER_PAGE_CODE) {
throw new IllegalArgumentException("Invalid device identifier page header");
}
int offset = 2;
// bytes[2], bytes[3] are total length
int totalLength = (bytes[offset++] & 0xFF) << 8 | (bytes[offset++] & 0xFF);
if (totalLength != bytes.length - offset) {
throw new IllegalArgumentException(String.format("Device identifier page size (%s) does not match content size (%s)",
totalLength, bytes.length - offset));
}
// bytes[4] starts descriptor list:
// Byte 0 - Protocol Identifier + Code Set 01 = Binary, 02 = Ascii
// Byte 1 - PIV, Association, Designator Type 01 = Vendor ID, 03 = NAA (WWID)
// Byte 2 - Reserved
// Byte 3 - Length
// Byte 4..n - Identifier
Map<Integer, String> identifiers = Maps.newLinkedHashMap();
while (offset < totalLength) {
byte protocolIdentifierAndCodeSet = bytes[offset++];
int type = bytes[offset++]; // Last half of the byte for the type but PIV for us should always be 0
offset++;
int identifierLength = (bytes[offset++] & 0xFF);
if (identifierLength + offset > bytes.length) {
throw new IllegalArgumentException("Device Identifier length too long for page size");
}
// Read device identifier
if (protocolIdentifierAndCodeSet == CODE_SET_BINARY) {
byte[] identifierBytes = new byte[identifierLength];
System.arraycopy(bytes, offset, identifierBytes, 0, identifierLength);
String identifier = Hex.encodeHexString(identifierBytes);
identifiers.put(type, identifier);
}
else if (protocolIdentifierAndCodeSet == CODE_SET_ASCII) {
String identifier = new String(bytes, offset, identifierLength);
identifiers.put(type, identifier);
}
offset += identifierLength;
}
return identifiers;
}
}