/*
* Copyright (c) 2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.systemservices.impl.iso;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Class to create a byte buffer with the contents of an ISO image
*
*/
public class ISOBuffer {
private ByteBuffer byteBuffer;
private Date creationDate;
private int currentExtent;
private List<DirectoryRecord> directoryRecords;
private List<DirectoryRecord> fileRecords;
private List<byte[]> fileDataList;
private PrimaryVolumeDescriptor vd;
private PathTable pt;
private DirectoryRecord rootDR;
private static final int START_EXTENT = 20;
private int dataStartPosition;
private int dataEndPosition;
public ISOBuffer() {
creationDate = new Date();
// Start adding use data from the 20th extent
currentExtent = START_EXTENT;
directoryRecords = new ArrayList<DirectoryRecord>();
fileRecords = new ArrayList<DirectoryRecord>();
fileDataList = new ArrayList<byte[]>();
dataStartPosition = 0;
dataEndPosition = 0;
}
/**
* Allocate the byte buffer and add the required root directory, volume descriptor and path table
*/
private void initializeISO() {
initializeISO(ISOConstants.VOLUME_IDENTIFIER);
}
private void initializeISO(String identifier) {
byteBuffer = ByteBuffer.allocate(ISOConstants.BUFFER_SIZE);
// Reserving first 16 sectors for bootable disks.
ISOUtil.padWithReserved(byteBuffer, 16 * ISOConstants.SECTOR_SIZE);
// Create a record for the root directory
createRootDirectory();
// Primary volume descriptor
createVolumeDescriptor(identifier);
// Path table
createPathTable();
vd.addToBuffer(byteBuffer);
pt.addToBufferLittleEndian(byteBuffer);
pt.addToBufferBigEndian(byteBuffer);
}
private void createVolumeDescriptor(String identifier) {
vd = new PrimaryVolumeDescriptor(creationDate);
// vd.systemIdentifier = ISOConstants.VOLUME_IDENTIFIER;
vd.systemIdentifier = identifier;
// vd.volumeIdentifier = ISOConstants.VOLUME_IDENTIFIER;
vd.volumeIdentifier = identifier;
vd.volumeSpaceSize = 23;
vd.volumeSetSize = 1;
vd.volumeSequenceNumber = 1;
vd.logicalBlockSize = ISOConstants.SECTOR_SIZE;
vd.pathTableSize = 11;
vd.locationOfLittleEndianPathTable = 18;
vd.locationOfBigEndianPathTable = 19;
vd.rootDirectoryRecord = rootDR;
}
private void createPathTable() {
pt = new PathTable();
pt.sectorSize = ISOConstants.SECTOR_SIZE;
pt.directoryIdentifierLength = 1;
pt.extentLocation = START_EXTENT;
pt.dirNumOfParentDirectory = 1;
}
private void createRootDirectory() {
rootDR = createDirectory((byte) 0);
directoryRecords.add(rootDR);
directoryRecords.add(createDirectory((byte) 1));
}
private DirectoryRecord createDirectory(final byte paddingByte) {
DirectoryRecord dr = new DirectoryRecord(creationDate);
dr.lengthOfDirRecord = 34;
dr.extentLocation = START_EXTENT;
dr.directoryEntryLength = 2048;
dr.directoryFlagBit = 2;
dr.volumeSequenceNumber = 1;
dr.directoryIdentifierLength = 1;
dr.paddingByte = paddingByte;
return dr;
}
public void addFile(final String fileName, byte[] data) {
DirectoryRecord fileEntry = new DirectoryRecord(creationDate);
fileEntry.lengthOfDirRecord = (byte) (fileName.length() + 33);
fileEntry.extentLocation = ++currentExtent;
fileEntry.directoryEntryLength = data.length;
fileEntry.directoryFlagBit = 0;
fileEntry.volumeSequenceNumber = 1;
fileEntry.directoryIdentifierLength = (byte) fileName.length();
fileEntry.dirName = fileName;
// Add the file record to the list then add the file data
// the array lit maintains the order of the data so that it is written to the correct extent
fileRecords.add(fileEntry);
fileDataList.add(data);
}
private void writeDirectoryRecords() {
for (DirectoryRecord directory : directoryRecords) {
directory.addToBuffer(byteBuffer);
}
}
private void writeFileRecords() {
for (DirectoryRecord fileRecord : fileRecords) {
fileRecord.addToBuffer(byteBuffer);
}
}
private void writeFileData() {
for (byte[] fileData : fileDataList) {
int reserve = ISOConstants.SECTOR_SIZE - byteBuffer.position() % ISOConstants.SECTOR_SIZE;
ISOUtil.padWithReserved(byteBuffer, reserve);
if (dataStartPosition == 0) {
dataStartPosition = byteBuffer.position();
}
byteBuffer.put(fileData);
}
}
/**
* Write all of the ISO image contents to a byte buffer
*
* @return byte [] representation of the ISO
*/
public byte[] createISO() {
return createISO(ISOConstants.VOLUME_IDENTIFIER);
}
public byte[] createISO(String identifier) {
// First initialize the ISO structure
initializeISO(identifier);
// write the directories to the buffer
writeDirectoryRecords();
// write the file records to the buffer
writeFileRecords();
// now write the file contents to the buffer
writeFileData();
dataEndPosition = byteBuffer.position();
return byteBuffer.array();
}
public int getDataStartPosition() {
return dataStartPosition;
}
public int getDataEndPosition() {
return dataEndPosition;
}
}